strc-pag.mkiv / last modification: 2020-01-30 14:16
%D \module
%D   [       file=strc-pag,
%D        version=2008.10.20,
%D          title=\CONTEXT\ Structure Macros,
%D       subtitle=Pagenumbering,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{ConTeXt Structure Macros / Pagenumbering}

\registerctxluafile{strc-pag}{}

\unprotect

\startcontextdefinitioncode

% Allocation:

\countdef\realpageno \zerocount  \realpageno \plusone
\countdef\userpageno \plusone    \userpageno \plusone
\countdef\subpageno  \plustwo    \subpageno  \plusone % was \zerocount but that doesn't work well with bytext
\countdef\arrangeno  \plusthree  \arrangeno  \zerocount % !
\countdef\pagenoshift\plusfour   \pagenoshift\zerocount % !
\countdef\lastpageno \plusfive   \lastpageno \zerocount % !

\let\pageno\userpageno

\def\realfolio{\the\realpageno}
\def\userfolio{\the\userpageno}
\def\subfolio {\the\subpageno }
\def\lastfolio{\the\lastpageno}

\newtoks\everyinitializepagecounters

\unexpanded\def\initializepagecounters
  {\the\everyinitializepagecounters}

\appendtoks
    \initializepagecounters
\to \everyjob

% Page numbers are kind of independent of each other and therefore they
% all get their own counter. After all, it's easier to combine them in
% a pseudo counterset than to deal with a complex set itself.

% \defineprefixset   [mine][section-1,section-2]
% \defineseparatorset[mine][:]
%
% \setupuserpagenumber
%   [way=bypart,
%    prefix=yes,
%    prefixset=mine,
%    prefixseparatorset=mine]

% \defineconversionset[frontpart:pagenumber][][romannumerals]
% \defineconversionset[bodypart:pagenumber] [][numbers]
%
% \setupuserpagenumber[way=byblock]
% \setupuserpagenumber[way=bychapter]
% \setupuserpagenumber[numberconversionset=pagenumber]
%
% \starttext
%     \startfrontmatter
%         \completecontent[criterium=all]
%         \chapter{tufte} \section{one} \input tufte \page \section{two} \input tufte \page
%         \chapter{tufte} \section{one} \input tufte \page \section{two} \input tufte \page
%     \stopfrontmatter
%     \startbodymatter
%         \chapter{knuth} \section{one} \input knuth \page \section{two} \input knuth \page \section{three} \input knuth \page
%         \chapter{knuth} \section{one} \input knuth \page \section{two} \input knuth \page \section{three} \input knuth \page
%     \stopbodymatter
% \stoptext

\definecounter[\s!realpage][\c!prefix=\v!no,\c!start=\plusone,\c!prefixsegments=,\s!counter=realpageno,\c!method=\v!page]
\definecounter[\s!userpage][\c!prefix=\v!no,\c!start=\plusone,\c!prefixsegments=,\s!counter=userpageno,\c!method=\v!page]
\definecounter[\s!subpage] [\c!prefix=\v!no,\c!start=\plusone,\c!prefixsegments=,\s!counter=subpageno, \c!method=\v!page]

\newtoks\everysetuprealpagenumber % todo: set state: none, start, stop, reset
\newtoks\everysetupuserpagenumber % todo: set state: none, start, stop, reset
\newtoks\everysetupsubpagenumber  % todo: set state: none, start, stop, reset

\unexpanded\def\setuprealpagenumber{\dosingleargument\strc_pagenumbers_setup_realpage}
\unexpanded\def\setupuserpagenumber{\dosingleargument\strc_pagenumbers_setup_userpage}
\unexpanded\def\setupsubpagenumber {\dosingleargument\strc_pagenumbers_setup_subpage }

\let\m_strc_pagenumbers_state_old\zerocount
\let\m_strc_pagenumbers_state_new\zerocount

\def\strc_pagenumbers_save_state#1{\edef\m_strc_pagenumbers_state_old{\namedcounterparameter#1\c!state}}

\def\strc_pagenumbers_setup_realpage[#1]{\strc_pagenumbers_save_state\s!realpage\setupcounter[\s!realpage][#1]\the\everysetuprealpagenumber}
\def\strc_pagenumbers_setup_userpage[#1]{\strc_pagenumbers_save_state\s!userpage\setupcounter[\s!userpage][#1]\the\everysetupuserpagenumber}
\def\strc_pagenumbers_setup_subpage [#1]{\strc_pagenumbers_save_state\s!subpage \setupcounter[\s!subpage ][#1]\the\everysetupsubpagenumber }

\unexpanded\def\resetrealpagenumber {} % not permitted
\unexpanded\def\resetuserpagenumber {\strc_counters_reset\s!userpage}
\unexpanded\def\resetsubpagenumber  {\strc_counters_reset\s!subpage}

\appendtoks
    \strc_counters_set\s!realpage\realpageno
    \strc_counters_set\s!userpage\userpageno
    \strc_counters_set\s!subpage \subpageno
    \lastpageno\lastcountervalue[\s!realpage]\relax
\to \everyinitializepagecounters

\let\setuppagenumber\setupuserpagenumber
\let\resetpagenumber\resetuserpagenumber

% invisible =

\def\strc_pagenumbers_page_state_save % \normalexpanded?
  {\clf_savepagedata
     {
        prefix        {\namedcounterparameter\s!userpage\c!prefix}
        separatorset  {\namedcounterparameter\s!userpage\c!prefixseparatorset}
        conversion    {\namedcounterparameter\s!userpage\c!prefixconversion}
        conversionset {\namedcounterparameter\s!userpage\c!prefixconversionset}
        set           {\namedcounterparameter\s!userpage\c!prefixset}
        segments      {\namedcounterparameter\s!userpage\c!prefixsegments}
        connector     {\namedcounterparameter\s!userpage\c!prefixconnector}
     }{
        conversion    {\namedcounterparameter\s!userpage\c!numberconversion}
        conversionset {\namedcounterparameter\s!userpage\c!numberconversionset}
        starter       {\namedcounterparameter\s!userpage\c!numberstarter}
        stopper       {\namedcounterparameter\s!userpage\c!numberstopper}
     }{
        viewerprefix  {\namedcounterparameter\s!userpage\c!viewerprefix}
        state         {\namedcounterparameter\s!userpage\c!state}
     }%
   \relax}

\prependtoks
    \strc_pagenumbers_page_state_save
\to \everyshipout

\installcorenamespace{pagestatestack} % no level yet

\unexpanded\def\strc_pagenumbers_page_state_push{\setxvalue{\??pagestatestack\c!state}{\namedcounterparameter\s!userpage\c!state}}
\unexpanded\def\strc_pagenumbers_page_state_pop {\normalexpanded{\setuppagenumber[\c!state=\getvalue{\??pagestatestack\c!state}]}}

\setuppagenumber
  [\c!way=\v!by\v!text,
   \c!prefix=\v!no,
   \c!prefixset=\v!part,
   \c!prefixconnector=\endash,
   \c!state=\v!start]

\setupsubpagenumber
  [\c!way=\v!by\v!part,
   \c!state=\v!start] % was stop but start looks better in logging

% Counters

\def\firstrealpagenumber{\convertedcounter[\s!realpage][\c!type=\v!first]}
\def\firstuserpagenumber{\convertedcounter[\s!userpage][\c!type=\v!first]}
\def\firstsubpagenumber {\convertedcounter[\s!subpage ][\c!type=\v!first]}

\def\lastrealpagenumber {\convertedcounter[\s!realpage][\c!type=\v!last]}
\def\lastuserpagenumber {\convertedcounter[\s!userpage][\c!type=\v!last]}
\def\lastsubpagenumber  {\convertedcounter[\s!subpage ][\c!type=\v!last]}

\def\prevrealpagenumber {\convertedcounter[\s!realpage][\c!type=\v!previous]}
\def\prevuserpagenumber {\convertedcounter[\s!userpage][\c!type=\v!previous]}
\def\prevsubpagenumber  {\convertedcounter[\s!subpage ][\c!type=\v!previous]}

\def\nextrealpagenumber {\convertedcounter[\s!realpage][\c!type=\v!next]}
\def\nextuserpagenumber {\convertedcounter[\s!userpage][\c!type=\v!next]}
\def\nextsubpagenumber  {\convertedcounter[\s!subpage ][\c!type=\v!next]}

\def\firstrealpage{\strc_counters_first\s!realpage}
\def\firstuserpage{\strc_counters_first\s!userpage}
\def\firstsubpage {\strc_counters_first\s!subpage }

\def\prevrealpage {\strc_counters_prev \s!realpage}
\def\prevuserpage {\strc_counters_prev \s!userpage}
\def\prevsubpage  {\strc_counters_prev \s!subpage }

\def\nextrealpage {\strc_counters_next \s!realpage}
\def\nextuserpage {\strc_counters_next \s!userpage}
\def\nextsubpage  {\strc_counters_next \s!subpage }

\def\lastrealpage {\strc_counters_last \s!realpage}
\def\lastuserpage {\strc_counters_last \s!userpage}
\def\lastsubpage  {\strc_counters_last \s!subpage }

\let\firstpage\firstrealpage
\let\prevpage \prevrealpage
\let\nextpage \nextrealpage
\let\lastpage \lastrealpage

% Compatibility counters:

\def\nofrealpages {\lastrealpage} \def\totalnumberofpages{\lastrealpage}
\def\nofuserpages {\lastuserpage} \def\lastpagenumber    {\lastuserpage}
\def\nofsubpages  {\lastsubpage }

% Renderers:

\def\pagenumber         {\strc_counters_raw\s!userpage}
\def\prefixedpagenumber {\directconvertedcounter\s!userpage\empty} % \userpagenumber

\def\realpagenumber     {\directconvertedcounter\s!realpage\empty}
\def\userpagenumber     {\directconvertedcounter\s!userpage\empty}
\def\subpagenumber      {\directconvertedcounter\s!subpage \empty}

\def\firstrealpagenumber{\directconvertedcounter\s!realpage\v!first}
\def\firstuserpagenumber{\directconvertedcounter\s!userpage\v!first}
\def\firstsubpagenumber {\directconvertedcounter\s!subpage \v!first}

\def\lastrealpagenumber {\directconvertedcounter\s!realpage\v!last}
\def\lastuserpagenumber {\directconvertedcounter\s!userpage\v!last}
\def\lastsubpagenumber  {\directconvertedcounter\s!subpage \v!last}

\def\prevrealpagenumber {\directconvertedcounter\s!realpage\v!previous}
\def\prevuserpagenumber {\directconvertedcounter\s!userpage\v!previous}
\def\prevsubpagenumber  {\directconvertedcounter\s!subpage \v!previous}

\def\nextrealpagenumber {\directconvertedcounter\s!realpage\v!next}
\def\nextuserpagenumber {\directconvertedcounter\s!userpage\v!next}
\def\nextsubpagenumber  {\directconvertedcounter\s!subpage \v!next}

\unexpanded\def\strc_pagenumbers_decrement_counters % only at the end
   {\strc_counters_decrement\s!realpage
    \strc_counters_decrement\s!userpage
    \strc_counters_decrement\s!subpage}

\unexpanded\def\strc_pagenumbers_increment_counters
  {\incrementpagenumber
   \incrementsubpagenumber}

\appendtoks
    \strc_pagenumbers_decrement_counters
\to \everygoodbye

\newcount\c_strc_subpage_first_real \c_strc_subpage_first_real\plusone

\appendtoks
   \ifcase\subpageno\relax
     \global\c_strc_subpage_first_real\realpageno
   \or
     \global\c_strc_subpage_first_real\realpageno
   \fi
\to \everybeforepagebody

\def\therealsubpageno#1% new helper
  {\the\numexpr\c_strc_subpage_first_real+#1+\minusone\relax}

% Equivalents (compatibility):
%
% todo: maybe leave lastpage etc lua calls

\def\realpage{\the\realpageno}
\def\userpage{\the\userpageno}
\def\subpage {\the\subpageno}

% Hooks:

\def\currentpage{\the\realpageno}% rather useless

\appendtoks
    \ifnum\realpageno>\lastpage \glet\lastpage\lastrealpage \fi
\to \everyinitializepagecounters

% States:

\newif\ifdoublesided  \newconditional\layoutisdoublesided
\newif\ifsinglesided  \newconditional\layoutissinglesided

% Realpage and subpage numbers:

\unexpanded\def\setnextrealpageno{\global\realpageno\strc_counters_incremented\s!realpage\relax}
\unexpanded\def\setnextsubpageno {\global\subpageno \strc_counters_incremented\s!subpage \relax}

% Page numbers: (can move to lua) ... inconsistent names

\installcorenamespace{pagenumberinc}
\installcorenamespace{pagenumberdec}

\unexpanded\def\strc_pagenumbers_decrement_userpage{\global\userpageno\strc_counters_decremented\s!userpage\relax}
\unexpanded\def\strc_pagenumbers_increment_userpage{\global\userpageno\strc_counters_incremented\s!userpage\relax}

\unexpanded\def\decrementsubpagenumber{\global\subpageno \strc_counters_decremented\s!subpage \relax}
\unexpanded\def\incrementsubpagenumber{\global\subpageno \strc_counters_incremented\s!subpage \relax}

\unexpanded\def\strc_pagenumbers_synchronize_userpage{\global\c_strc_pagenumbers_state_userpage\plustwo} % start and visible

\unexpanded\def\decrementpagenumber{\csname\??pagenumberdec\namedcounterparameter\s!userpage\c!state\endcsname}
\unexpanded\def\incrementpagenumber{\csname\??pagenumberinc\namedcounterparameter\s!userpage\c!state\endcsname}

\letvalue{\??pagenumberdec\v!start}\strc_pagenumbers_decrement_userpage
\letvalue{\??pagenumberdec\v!none }\strc_pagenumbers_decrement_userpage
\letvalue{\??pagenumberdec\v!empty}\strc_pagenumbers_decrement_userpage

\letvalue{\??pagenumberinc\v!start}\strc_pagenumbers_increment_userpage
\letvalue{\??pagenumberinc\v!none }\strc_pagenumbers_increment_userpage
\setvalue{\??pagenumberinc\v!empty}{\strc_pagenumbers_increment_userpage\strc_pagenumbers_synchronize_userpage}
\letvalue{\??pagenumberinc\v!keep }\strc_pagenumbers_synchronize_userpage

% Setup general page numbering

\installcorenamespace{pagenumbering}

\installdirectcommandhandler \??pagenumbering {pagenumbering}

% some day ifsinglesided and ifdoublesided will become obsolete

\newtoks\everysidedswitch

\appendtoks
   \singlesidedfalse \setfalse\layoutisdoublesided
   \doublesidedfalse \setfalse\layoutissinglesided
   \resetsystemmode\v!singlesided
   \resetsystemmode\v!doublesided
   \processallactionsinset[\directpagenumberingparameter\c!alternative]
     [ \v!singlesided=>\setsystemmode\v!singlesided\singlesidedtrue\settrue\layoutissinglesided,
       \v!doublesided=>\setsystemmode\v!doublesided\doublesidedtrue\settrue\layoutisdoublesided]%
   \the\everysidedswitch
   \pageduplexmode
     \ifsinglesided
       \ifdoublesided\plustwo\else\zerocount\fi
     \else
       \ifdoublesided\plusone\else\zerocount\fi
     \fi
   \page_backgrounds_recalculate
   \strc_pagenumbers_set_location
\to \everysetuppagenumbering

\appendtoks
   \ifdefined\trackingmarginnotestrue
     \ifdoublesided
       \trackingmarginnotestrue
     \else
       \trackingmarginnotesfalse
     \fi
   \fi
\to \everysidedswitch

\ifdefined \page_backgrounds_recalculate \else
    \let\page_backgrounds_recalculate\relax
\fi

\ifdefined \strc_pagenumbers_set_location \else
    \let\strc_pagenumbers_set_location\relax
\fi

\unexpanded\def\strc_pagenumbers_flush_final_page
  {\edef\p_strc_pagenumbers_page{\directpagenumberingparameter\c!page}%
   \ifx\p_strc_pagenumbers_page\empty \else
     \ifx\p_strc_pagenumbers_page\v!no \else
       \page[\p_strc_pagenumbers_page]%
     \fi
   \fi}

% The numbered location handler is there because we need to be downward
% compatible. So, in fact there can be multiple handlers active at the
% same time, but only the current one does something.

% Rendering:

\unexpanded\def\strc_pagenumbers_place_location
  {\ifnum\c_strc_pagenumbers_state_userpage=\plustwo
     \ifnum\c_strc_pagenumbers_state=\plusone
        \doif{\directpagenumberingparameter\c!strut}\v!yes\strut
        \begingroup
        \usepagenumberingstyleandcolor\c!style\c!color
        \directpagenumberingparameter\c!command
          {\directpagenumberingparameter\c!left
           \labeltexts\v!pagenumber\prefixedpagenumber
           \directpagenumberingparameter\c!right}%
        \endgroup
     \fi
   \fi}

\unexpanded\def\completepagenumber
  {\ifnum\c_strc_pagenumbers_state_userpage=\plustwo
     \ifnum\c_strc_pagenumbers_state=\plusone
        \directpagenumberingparameter\c!left
        \labeltexts\v!pagenumber\prefixedpagenumber
        \directpagenumberingparameter\c!right
     \fi
   \fi}

\unexpanded\def\placepagenumber
  {\ifnum\c_strc_pagenumbers_state_userpage=\plustwo
     \ifnum\c_strc_pagenumbers_state=\plusone
        \labeltexts\v!pagenumber\pagenumber
     \fi
   \fi}

\unexpanded\def\referencepagenumber[#1]%
  {\doifelsenothing{#1}{?}{}}

% The numbered location handler is there because we need to be downward
% compatible. So, in fact there can be multiple handlers active at the
% same time, but only the current one does something.

\setnewconstant\c_strc_pagenumbers_state_realpage\plustwo % counter state : 0=stop, 1=start, 2=start and visible
\setnewconstant\c_strc_pagenumbers_state_userpage\plustwo % counter state : 0=stop, 1=start, 2=start and visible
\setnewconstant\c_strc_pagenumbers_state_subpage \plustwo % counter state : 0=stop, 1=start, 2=start and visible
\setnewconstant\c_strc_pagenumbers_state         \plusone % general number: 0=invisible, 1=visible

\unexpanded\def\strc_pagenumbers_check_state_change#1#2%
  {\edef\m_strc_pagenumbers_state_new{\namedcounterparameter#1\c!state}%
   \ifx\m_strc_pagenumbers_state_new\m_strc_pagenumbers_state_old \else
     #2\ifx\m_strc_pagenumbers_state_new\v!start\plustwo\else\zerocount\fi
   \fi}


\appendtoks % todo: set state: none, start, stop, reset
    \strc_pagenumbers_check_state_change\s!realpage\c_strc_pagenumbers_state_realpage
\to \everysetuprealpagenumber

\appendtoks % todo: set state: none, start, stop, reset
    \strc_pagenumbers_check_state_change\s!userpage\c_strc_pagenumbers_state_userpage
\to \everysetupuserpagenumber

\appendtoks % todo: set state: none, start, stop, reset
    \strc_pagenumbers_check_state_change\s!subpage\c_strc_pagenumbers_state_subpage
\to \everysetupsubpagenumber

\appendtoks % todo: set state: none, start, stop, reset
    \doifelse{\directpagenumberingparameter\c!state}\v!start
      {\c_strc_pagenumbers_state\plusone  }%
      {\c_strc_pagenumbers_state\zerocount}%
\to \everysetuppagenumbering

% Done

% \c!way=\v!by\v!part
% \c!text=
% \v!chapter\v!number=\v!no
% \v!part\v!number=\v!yes
% \c!numberseparator=--
% \c!conversion=\v!numbers

\setuppagenumbering
  [\c!alternative=\v!singlesided,
   \c!location={\v!header,\v!middle},
   \c!width=, % in geval van \v!marginedge
   \c!left=,
   \c!right=,
   \c!page=\v!last,
   \c!textseparator=\tfskip,
   \c!state=\v!start,
   \c!command=,
   \c!strut=\v!yes,
   \c!style=, % empty, otherwise conflict
   \c!color=]

% just for downward compatbility

\appendtoks
    \edef\askeduserpagenumber{\namedcounterparameter\s!userpage\c!number}%
    \ifx\askeduserpagenumber\empty \else
      \normalexpanded{\setuppagenumber[\c!start=\askeduserpagenumber,\c!number=]}%
      \userpageno\strc_counters_raw\s!userpage
    \fi
\to \everysetupuserpagenumber % todo: set state: none, start, stop, reset

\appendtoks
    \edef\askedsubpagenumber{\namedcounterparameter\s!subpage\c!number}%
    \ifx\askedsubpagenumber\empty \else
      \normalexpanded{\setupsubpagenumber[\c!start=\askedsubpagenumber,\c!number=]}%
      \subpageno\strc_counters_raw\s!subpage\relax
    \fi
\to \everysetupsubpagenumber % todo: set state: none, start, stop, reset

% \setuplayout[width=300pt,backspace=4cm]
% \setuppagenumbering [alternative=doublesided]
% \setupuserpagenumber[start=2]
% \starttext \dorecurse{20}{\input knuth \par} \stoptext

\unexpanded\def\strc_pagenumbers_check_change_shift
  {\userpageno\strc_counters_raw\s!userpage\relax
   \ifnum\realpageno=\plusone
     \ifodd\userpageno
     \else
       \global\pagenoshift\plusone
     \fi
   \fi}

\appendtoks % todo: set state: none, start, stop, reset
    % this makes starting at an even page possible
    \strc_pagenumbers_check_change_shift
\to \everysetupuserpagenumber

\appendtoks % todo: set state: none, start, stop, reset
    % this makes starting at an even page possible
    \strc_pagenumbers_check_change_shift
\to \everysetuppagenumbering

\initializepagecounters

\stopcontextdefinitioncode

\protect \endinput