9

Consider the following piece of code:

\documentclass{book}
\usepackage{cleveref}
\usepackage{lipsum}

\begin{document}
  \chapter{Chapter One}
  \section{Section One.One}
  \label{mysection}
  \lipsum

  \section{Section One.Two}
  Please ignore \cref{mysection}.

  \chapter{Chapter Two}
  \section{Section Two.One}

  As you may have noticed, \cref{mysection} is useless.
\end{document}

By default, in both uses of \cref I get Section 1.1 as expected.

Is it possible to change the way the reference is shown in such a way that the second use of \cref above will print Section 1 of Chapter 1? i.e., naming the parent counter as well.

However, this should be done only outside the own chapter, so the first use of \cref in the above example should print Section 1.

This comes from a use case of a normative text where the document is divided in "articles" and "commas", and it is good to have references like "Comma 1 of Article 2" or "Article 2, Comma 1".

1 Answers1

13

Update 2017/03/05 at the end

Update 2017/06/07: Similar question https://tex.stackexchange.com/a/373769/31729

Determining the parent counter of a counter is easy, if there is only one parent counter, so this is a unique mapping then.

I stored all counters being defined at the begin of the document in a sequence and 'cracked' the \cl@X list (X being a driver counter name, such as, say, chapter), storing their content into a property list which contains the parent counter.

The \getparentcounter macro can be used to retrieve the value.

Now the zref package stores the properties childcountervalue, parentcountervalue and parentcountername in order to retrieve them later with \zref@extract.

The anchor is stored as well, just in case we're using hyperref.

The macros \parentcref and \parentCref will output the parent counter and the child counter with the first character in lower case (which is the counter name, actually) and the first character in upper case, i.e. section → Section etc.

The code assumes that the counters are using numbers only, i.e. \arabic{foo}. I'll try to remove this limitation.

\documentclass{book}
\usepackage{etoolbox}
\usepackage{lipsum}
\usepackage{hyperref}
\usepackage[user,counter,hyperref]{zref}
\usepackage{cleveref}

\makeatletter
\AtEndPreamble{
\newif\if@hyperrefloaded

\@ifpackageloaded{hyperref}{%
  \@hyperrefloadedtrue
}{
  \@hyperrefloadedfalse
}
}
\makeatother

\usepackage{letltxmacro}



\usepackage{xparse}
\makeatletter

\ExplSyntaxOn
\prop_new:N \g_latex_parentcounters_prop 
\seq_new:N  \g_latex_counters_seq

\cs_generate_variant:Nn \seq_gset_from_clist:Nn {Nx,cx}
\cs_generate_variant:Nn \prop_item:Nn {Nx,cx,No}
\cs_generate_variant:Nn \str_head:n {x,o,V}
\cs_generate_variant:Nn \str_tail:n {x,o,V}
\cs_generate_variant:Nn \str_uppercase:n {x,o,V}
\cs_generate_variant:Nn \str_set:Nn {Nx,No}

% Store all reset lists and make a property list for each counter and its possible parent counter
\cs_new_nopar:Npn \GetAllResetLists {%
  \def\@elt##1{%
    \seq_gput_right:Nn \g_latex_counters_seq {##1}%
    \seq_new:c { g_latex_cntr_##1_seq }
  }
  \cl@@ckpt%
  \seq_map_inline:Nn \g_latex_counters_seq {%
    \def\@elt####1{####1,}
    \seq_gset_from_clist:cx { g_latex_cntr_##1_seq } { \use:c{cl@##1} }
  }
  % Now fill the parent counter lists
  \seq_map_inline:Nn \g_latex_counters_seq {%
    \seq_map_inline:cn { g_latex_cntr_##1_seq }{ %
      \prop_gput:Nnn \g_latex_parentcounters_prop {####1} {##1}
    }
  }
}

% Gives the parent counter of a specific counter (or nothing)
\cs_new:Npn \getparentcounter #1{%
  \prop_item:No \g_latex_parentcounters_prop {#1}
}

% Make the first character of a word upper case
\newcommand{\FirstUpcase}[1]{%
  \str_set:Nx \l_tmpa_str {#1}%
  \str_uppercase:x {\str_head:N \l_tmpa_str}\str_tail:V {\l_tmpa_str}%
}

\ExplSyntaxOff

\GetAllResetLists

\def\LastRefSteppedCounter{}

% Define new properties
\zref@newprop{childcountervalue}{\arabic{\LastRefSteppedCounter}}% This is the naked value
\zref@newprop{parentcountervalue}{\csname the\getparentcounter{\LastRefSteppedCounter}\endcsname}
\zref@newprop{parentcountername}{\getparentcounter{\LastRefSteppedCounter}}

% Add the new properties to the main property list stored with \zlabel
\zref@addprops{main}{childcountervalue,parentcountervalue,parentcountername}

\AtBeginDocument{%

  \LetLtxMacro\latex@@label\label
  \let\latex@@refstepcounterorig\refstepcounter

  \RenewDocumentCommand{\refstepcounter}{m}{%
    \global\xdef\LastRefSteppedCounter{#1}%
    \latex@@refstepcounterorig{#1}%
  }
  %Make the ordinary label and a \zlabel
  \RenewDocumentCommand{\label}{om}{%
    \IfValueTF{#1}{%
      \latex@@label[1]{#2}%
    }{%
      \latex@@label{#2}%
    }%
    \zlabel{#2}%
  }%
}

% Command for uppercase output
\NewDocumentCommand{\parentCref}{m}{%
  \zref@ifrefundefined{#1}{%
    \Cref{#1}%
  }{%
    \edef\@tmpb@{\zref@extract{#1}{parentcountervalue}}%
    \edef\@tmpa@{\csname the\zref@extract{#1}{parentcountername}\endcsname}%
    \ifx\@tmpb@\@tmpa@
    \Cref{#1}%
    \else
    \if@hyperrefloaded
    \FirstUpcase{\zref@extract{#1}{counter}} \hyperlink{\zref@extract{#1}{anchor}}{\zref@extract{#1}{childcountervalue}} of \FirstUpcase{\zref@extract{#1}{parentcountername}} \hyperlink{\zref@extract{#1}{anchor}}{\zref@extract{#1}{parentcountervalue}}%
    \else
    \FirstUpcase{\zref@extract{#1}{counter}} \zref@extract{#1}{childcountervalue} of \FirstUpcase{\zref@extract{#1}{parentcountername}} \zref@extract{#1}{parentcountervalue}%
    \fi
    \fi
  }%
}

% Command for lowercase output
\NewDocumentCommand{\parentcref}{m}{%
  \zref@ifrefundefined{#1}{%
    \cref{#1}%
  }{%
    \edef\@tmpb@{\zref@extract{#1}{parentcountervalue}}%
    \edef\@tmpa@{\csname the\zref@extract{#1}{parentcountername}\endcsname}%
    \ifx\@tmpb@\@tmpa@
    \Cref{#1}%
    \else
    \if@hyperrefloaded
    \zref@extract{#1}{counter} \hyperlink{\zref@extract{#1}{anchor}}{\zref@extract{#1}{childcountervalue}} of \zref@extract{#1}{parentcountername} \hyperlink{\zref@extract{#1}{anchor}}{\zref@extract{#1}{parentcountervalue}}%
    \else
    \zref@extract{#1}{counter} \zref@extract{#1}{childcountervalue} of \zref@extract{#1}{parentcountername} \zref@extract{#1}{parentcountervalue}%
    \fi
    \fi
  }%
}
\makeatother

\begin{document}
  \chapter{Chapter One}
  Please look at \parentCref{othersection} or at \parentCref{foosubsection} or at \parentcref{foosubsection}


  \section{Section One.One}
  \label{mysection}
  \lipsum

  \section{Section One.Two}

  \subsection{A subsection} \label{foosubsection}
  Please ignore \parentCref{mysection}.

  \chapter{Chapter Two}
  \section{Section Two.One}

  As you may have noticed, \parentCref{mysection} is useless.


  \chapter{Chapter Three}
  \section{Section Three.One}
  \section{Section Three.Two}\label{othersection}

\end{document}

enter image description here

See Renumbering chapter after part, include part number as suffix in cross-reference to chapter number for a similar question, but that's not a duplicate!

Update 2017/03/06 Some features of this answer has been added to the xassoccnt v1.3 package (Download from xassoccnt until it is available on CTAN, TeXLive (since 2017/03/06) and MikTeX) The solution is much shorter now:

\documentclass{book}
\usepackage{lipsum}
\usepackage{xassoccnt}
\usepackage{xpatch}
\usepackage{hyperref}
\usepackage[user,counter,hyperref]{zref}
\usepackage{cleveref}

\makeatletter
\AtEndPreamble{
  \newif\if@hyperrefloaded

\@ifpackageloaded{hyperref}{%
  \@hyperrefloadedtrue
}{
  \@hyperrefloadedfalse
}
}
\makeatother




\usepackage{xparse}
\makeatletter

\ExplSyntaxOn
\prop_new:N \g_latex_parentcounters_prop 
\seq_new:N  \g_latex_counters_seq

\cs_generate_variant:Nn \str_head:n {x,o,V}
\cs_generate_variant:Nn \str_tail:n {x,o,V}
\cs_generate_variant:Nn \str_uppercase:n {x,o,V}
\cs_generate_variant:Nn \str_set:Nn {Nx,No}

% Store all reset lists and make a property list for each counter and its possible parent counter

% Make the first character of a word upper case
\newcommand{\FirstUpcase}[1]{%
  \str_set:Nx \l_tmpa_str {#1}%
  \str_uppercase:x {\str_head:N \l_tmpa_str}\str_tail:V {\l_tmpa_str}%
}

\ExplSyntaxOff




% Define new properties
\zref@newprop{childcountervalue}{\arabic{\LastRefSteppedCounter}}% This is the naked value
\zref@newprop{parentcountervalue}{\csname the\GetParentCounter{\LastRefSteppedCounter}\endcsname}
\zref@newprop{parentcountername}{\GetParentCounter{\LastRefSteppedCounter}}

% Add the new properties to the main property list stored with \zlabel
\zref@addprops{main}{childcountervalue,parentcountervalue,parentcountername}

% Command for uppercase output
\NewDocumentCommand{\parentCref}{m}{%
  \zref@ifrefundefined{#1}{%
    \Cref{#1}%
  }{%
    \edef\@tmpb@{\zref@extract{#1}{parentcountervalue}}%
    \edef\@tmpa@{\csname the\zref@extract{#1}{parentcountername}\endcsname}%
    \ifx\@tmpb@\@tmpa@
    \Cref{#1}%
    \else
    \if@hyperrefloaded
    \FirstUpcase{\zref@extract{#1}{counter}} \hyperlink{\zref@extract{#1}{anchor}}{\zref@extract{#1}{childcountervalue}} of \FirstUpcase{\zref@extract{#1}{parentcountername}} \hyperlink{\zref@extract{#1}{anchor}}{\zref@extract{#1}{parentcountervalue}}%
    \else
    \FirstUpcase{\zref@extract{#1}{counter}} \zref@extract{#1}{childcountervalue} of \FirstUpcase{\zref@extract{#1}{parentcountername}} \zref@extract{#1}{parentcountervalue}%
    \fi
    \fi
  }%
}

% Command for lowercase output
\NewDocumentCommand{\parentcref}{m}{%
  \zref@ifrefundefined{#1}{%
    \cref{#1}%
  }{%
    \edef\@tmpb@{\zref@extract{#1}{parentcountervalue}}%
    \edef\@tmpa@{\csname the\zref@extract{#1}{parentcountername}\endcsname}%
    \ifx\@tmpb@\@tmpa@
    \Cref{#1}%
    \else
    \if@hyperrefloaded
    \zref@extract{#1}{counter} \hyperlink{\zref@extract{#1}{anchor}}{\zref@extract{#1}{childcountervalue}} of \zref@extract{#1}{parentcountername} \hyperlink{\zref@extract{#1}{anchor}}{\zref@extract{#1}{parentcountervalue}}%
    \else
    \zref@extract{#1}{counter} \zref@extract{#1}{childcountervalue} of \zref@extract{#1}{parentcountername} \zref@extract{#1}{parentcountervalue}%
    \fi
    \fi
  }%
}

\makeatother


\GetAllResetLists
\RegisterPostLabelHook{\zlabel}



\begin{document}
\chapter{Chapter One}
  Please look at \parentCref{othersection} or at \parentCref{foosubsection} or at \parentcref{foosubsection}


  \section{Section One.One}
  \label{mysection}
  \lipsum

  \section{Section One.Two}

  \subsection{A subsection} \label{foosubsection}
  Please ignore \parentCref{mysection}.

  \chapter{Chapter Two}
  \section{Section Two.One}

  As you may have noticed, \parentCref{mysection} is useless.


  \chapter{Chapter Three}
  \section{Section Three.One}   %
  \section{Section Three.Two}\label{othersection}

\end{document}
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • 1
    @Mico: It's a counter - related question... don't expect too much ;-) But thanks! (The usual complainers will show up soon ;-)) –  Feb 12 '17 at 22:35
  • That does indeed what I needed! I'm not at all familiar with the expl3 syntax. What is going on more precisely? – Nicola Gigante Feb 12 '17 at 22:48
  • @gigabytes: I will explain later on, alright? The expl3 way is just used to make a simplified storage of the bunch of data (the counter names, here) and easy access of data -- you can think of a \prop_.... list like a hash array, where a key has a value, here the counter name is key and its value is its parent counter. The other content is rather string uppercase related. One could bypass this by using another \prop_.... list but it would have to be done manually, most likely –  Feb 12 '17 at 22:52
  • 1
    @Mico: See the bunch of upvotes :D :D –  Feb 19 '17 at 12:03
  • It's nevertheless a great answer. :-) – Mico Feb 19 '17 at 14:15
  • @Mico: Thanks. Do you think it is useful to cast it into some package? I am thinking of extending my xassoccnt with reference features –  Feb 24 '17 at 11:21
  • 1
    @ChristianHupfer - What's really difficult is to gauge the demand for such a feature. Speaking for myself, I find it discouraging that there's been only very limited uptake to date of the zref package. Of course, this may be because the machinery of the zref package is, on the whole, rather very "low level" and doesn't lend itself easily to adoption by "end users", i.e., the "average" writer of a LaTeX-based document. If you can figure out how to provide a straightforward front end, which hides the programming complexity, from end users, some end users might actually start using it. – Mico Feb 24 '17 at 12:04
  • 1
    @Mico: The zref package is brilliant in its technique, but its main downside is the splitting between \label and \zlabel or the explicit need of \label@byprops etc. If it would hijack \label completely, many tasks could be simplified, but of course, this would break other \label/\ref related packages. I'll think about it –  Feb 24 '17 at 12:08