\NeedsTeXFormat{LaTeX2e}% \ProvidesPackage{assoccnt}[2014/08/21 v0.3 -- Associate counters stepping]% %%% %% License: LaTeX Project Public License %% Copyright (2014) Dr. Christian Hupfer %% Author: Christian Hupfer christian.hupfer@siebenfelsen.de %% %%%% %%% Some options later on %%%% \RequirePackage{etoolbox}[2011/01/03 2.2]% \RequirePackage{xkeyval}[2012/10/14 v2.6b]% \def\@@assoccnt@@keys{@@assoccnt@@keyfamily}% \newcounter{@@assoccnt@@internalcounter}% \let\@@assoccnt@@standardstepcounter\stepcounter% \let\@@assoccnt@@standardrefstepcounter\refstepcounter% % Just for a quick suffix \newcommand{\@@assoccnt@@associatedcounterslistsuffix}{% AssociatedCountersList% }% %%% Internal macro to generate the name of the list of associated counters \newcommand{\@@assoccnt@@generatelistname}[1]{% % #1 Name of the driver counter @@#1@@\@@assoccnt@@associatedcounterslistsuffix }% \newcommand{\@@assoccnt@@drivercounterlist}{% @@assoccnt@@DriverCountersList% }% %% %% Adds a driver counter to an internal administration list \newcommand{\AddDriverCounter}[2][]{% % #1 -> options % #2 -> driver counter name \setkeys{\@@assoccnt@@keys}{#1}% \ifcsdef{\@@assoccnt@@drivercounterlist}{% }{% \csgdef{\@@assoccnt@@drivercounterlist}{}% }% \listcsgadd{\@@assoccnt@@drivercounterlist}{#2}% }% %% %% Removes the complete list of driver counters \newcommand{\ClearDriverCountersList}{% \ifcsdef{\@@assoccnt@@drivercounterlist}% {% % Clear the possible driver counter entry in the list, the trailing associated counters list % is cleared as well \forlistcsloop{\ClearAssociatedCountersList}{\@@assoccnt@@drivercounterlist}% }{% % Nothing to be done }% }% \newcommand{\IsDriverCounter}[3]{% \ifcsdef{\@@assoccnt@@drivercounterlist}{% \ifinlistcs{#1}{\@@assoccnt@@drivercounterlist}{% True branch, it's in the list #2% }{% False branch, not in the list #3% }% }{% List does not exist at all --> expand false branch #3% }% }% \newcommand{\@@assoccnt@@generateboundtocounterslist}[1]{% #1@@boundtocounterslist% }% \newcommand{\@@assoccnt@@addassociatedcounter}[3][]{% \ifcsdef{\@@assoccnt@@generateboundtocounterslist{#3}}{% % The counter is already to some other counter, it should not be multiply associated! }{% \csgdef{\@@assoccnt@@generateboundtocounterslist{#3}}{}% \listcsgadd{\@@assoccnt@@generateboundtocounterslist{#3}}{#1} \ifcsdef{#2}{% \ifinlistcs{#3}{#2}{% % Do nothing, since counter is already in the list, prevent multiple list insertion }{% \listcsgadd{#2}{#3}% }% }{% % Nothing in here }% }% }% \newcommand{\AddAssociatedCounters}[3][]{% % #1 --> options % #2 --> driver counter % #3 --> CSV list of counters, that should be stepped, if the driver counter is stepped \setkeys{\@@assoccnt@@keys}{#1}% \ifcsdef{\@@assoccnt@@generatelistname{#2}}{% % % Nothing to be done --> List already exists \forcsvlist{\@@assoccnt@@addassociatedcounter[#2]{\@@assoccnt@@generatelistname{#2}}}{#3}% % Prevent accidental self - association \RemoveAssociatedCounter{#2}{#2}% }{% \GenericError{}{Error: List for driver counter #2 does not exist -- Usage on line \the\inputlineno}{}{} }% }% % This command defines a list of counters, that should be stepped also if the driver counter % is `\stepcounter`ed. % A self - association is not possible, as this would lead to inconsistent counting \newcommand{\DeclareAssociatedCounters}[3][]{% % #1 --> options % #2 --> driver counter % #3 --> CSV list of other counters, that should be stepped, if the driver counter is stepped \setkeys{\@@assoccnt@@keys}{#1}% \ifcsdef{\@@assoccnt@@generatelistname{#2}}{% % % Nothing to be done --> List already exists \GenericWarning{}{Warning: List of associated counters for driver counter #2 already exists}% }{% \AddDriverCounter{#2}% \csgdef{\@@assoccnt@@generatelistname{#2}}{}% Define some global list }% % Now add the counter names from #3 to the list % Note: Currently, it is not checked whether a counter is already added! \forcsvlist{\@@assoccnt@@addassociatedcounter[#2]{\@@assoccnt@@generatelistname{#2}}}{#3}% % Now remove an accidental self-association \RemoveAssociatedCounter{#2}{#2}% }% %\@onlypreamble{\DeclareAssociatedCounters}% %% %%% A generic macro, that removes a list entry from the list by %%% defining a temporary list and omitting the \newcommand{\@@assoccnt@@removefromlist}[2]{% % #1 list name % #2 element to be removed \ifcsdef{#1}{% \gdef\@@assoccnt@@mytemplist{}% \renewcommand*{\do}[1]{% \ifstrequal{##1}{#2}{ % Later one some success routine etc. ??? }{% \listgadd{\@@assoccnt@@mytemplist}{##1}% }% }% \dolistcsloop{#1}% \csxdef{#1}{\@@assoccnt@@mytemplist}% }{% % The list is not defined at all, can't remove something from something not existing... }% }% %% Remove a particular counter from the list \newcommand{\RemoveAssociatedCounter}[2]{% % #1 arg: driver counter % #2 arg: counter name to be removed \@@assoccnt@@removefromlist{\@@assoccnt@@generatelistname{#1}}{#2}% }% %% Remove a CSV list of counters from the list of associated counters \newcommand{\RemoveAssociatedCounters}[2]{% % #1 arg: driver counter % #2 arg: CSV list of counters to be removed \forcsvlist{\RemoveAssociatedCounter{#1}}{#2}% }% %% Remove all associated counters from the list \newcommand{\ClearAssociatedCountersList}[1]{% % #1 arg: driver counter \csundef{\@@assoccnt@@generatelistname{#1}}% \@@assoccnt@@removefromlist{\@@assoccnt@@drivercounterlist}{#1}% }% %% Test if a counter is an associated counter of driver counter \newcommand{\IsAssociatedToCounter}[4]{% % #1 arg: driver counter % #2 arg: (possibly) associated counter % #3 arg: Code for execution on true branch % #4 arg: Code for execution on false branch \ifcsdef{\@@assoccnt@@generatelistname{#1}}{% \ifinlistcs{#2}{\@@assoccnt@@generatelistname{#1}}{% #3% }{% #4% }% }{% List does not exist, so it's not associated #4% }% }% \newcommand{\IsAssociatedCounter}[3]{% \ifcsdef{\@@assoccnt@@generateboundtocounterslist{#1}}{% true branch \setcounter{@@assoccnt@@internalcounter}{0}% \renewcommand*{\do}[1]{% \@@assoccnt@@standardstepcounter{@@assoccnt@@internalcounter}% }% \dolistcsloop{\@@assoccnt@@generateboundtocounterslist{#1}}% \ifnumgreater{\number\value{@@assoccnt@@internalcounter}}{0}{% #2% }{% #3% }% }{% #3% }% End of \ifcsdef false branch }% \newcommand{\GetDriverCounter}[1]{% \setcounter{@@assoccnt@@internalcounter}{0}% \renewcommand*{\do}[1]{% ##1% \ifnumequal{\number\value{@@assoccnt@@internalcounter}}{1}{% \listbreak}{% \@@assoccnt@@standardstepcounter{@@assoccnt@@internalcounter}% }% }% \dolistcsloop{\@@assoccnt@@generateboundtocounterslist{#1}}% }% \newcommand{\@@assoccnt@@showassociatedcounterlist}[1]{% \textcolor{red}{\textbf{#1{ }}}% }% \newcommand{\@@assoccnt@@showdrivercounterlist}[1]{% \textcolor{blue}{\textbf{#1}} \ifcsdef{\@@assoccnt@@generatelistname{#1}}{% \forlistcsloop{\@@assoccnt@@showassociatedcounterlist}{\@@assoccnt@@generatelistname{#1}}% }{}% \par% }% \newcommand{\AssociationStatistics}[1][]{% \forlistcsloop{\@@assoccnt@@showdrivercounterlist}{\@@assoccnt@@drivercounterlist}% }% %%% The stepcounter wrapper for the standard stepcounter command %%% This is just for convenience, if the command as to be improved/extended later on in future \newcommand{\@@assoccnt@@stepcounter}[1]{% \@@assoccnt@@standardstepcounter{#1}% }% %%% Not needed so far \newcommand{\@@assoccnt@refstepcounter}[1]{% \@@assoccnt@@standardrefstepcounter{#1}% }% %%%% Redefinition of the \stepcounter command \renewcommand{\stepcounter}[1]{% \@@assoccnt@@standardstepcounter{#1}% Traditional behaviour, since this is expected! \ifcsdef{\@@assoccnt@@generatelistname{#1}}{% Check first, whether the list exists at all, \forlistcsloop{\@@assoccnt@@stepcounter}{\@@assoccnt@@generatelistname{#1}}% March through the list }{% %No list -> do nothing at all }% }% %%%% Redefinition of the \stepcounter command \renewcommand{\refstepcounter}[1]{% \@@assoccnt@@standardstepcounter{#1}% Traditional behaviour, since this is expected! \protected@edef\@currentlabel% {\csname p@#1\endcsname\csname the#1\endcsname}% \ifcsdef{\@@assoccnt@@generatelistname{#1}}{% Check first, whether the list exists at all, \forlistcsloop{\@@assoccnt@@stepcounter}{\@@assoccnt@@generatelistname{#1}}% March through the list }{% %No list -> do nothing at all }% }% \endinput%