%D \module
%D   [       file=pack-obj,
%D        version=1998.01.15,
%D          title=\CONTEXT\ Packaging Macros,
%D       subtitle=Objects,
%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.

% todo, move more to mkiv, get rid of blabelgroup

\writestatus{loading}{ConTeXt Packaging Macros / Objects}

\unprotect

%D \macros
%D   {setobject,getobject,ifinobject}
%D
%D Boxes can be considered reuable objects. Unfortunaltely once
%D passed to the \DVI\ file, such objects cannot be reused. In
%D \PDF\ however, reusing is possible and sometimes even a
%D necessity. Therefore, \CONTEXT\ supports reusable objects.
%D
%D During the \TEX\ processing run, boxes can serve the purpose
%D of objects, and the \DVI\ driver module implements objects
%D using packed boxes.
%D
%D The \PDF\ and \PDFTEX\ driver modules implement objects
%D using \PDF\ forms. There is no (real) restriction on the
%D number of objects there.
%D
%D The first application of objects in \CONTEXT\ concerned
%D \METAPOST\ graphics and fill||in form fields. The first
%D application can save lots of bytes, while the latter use is
%D more a necessity than byte saving.
%D
%D \starttyping
%D \setobject{class}{name}\somebox{}
%D \getobject{class}{name}
%D \stoptyping
%D
%D Here \type{\somebox} can be whatever box specification suits
%D \TEX. We save the dimensions of an object, although some
%D drivers will do so themselves. This means that when for
%D instance using \PDFTEX\ we could save a hash entry plus some
%D 20+ memory locations per object by delegating this
%D housekeeping to the driver. The current approach permits
%D us to keep the box characteristic too.

\newif\ifinobject

\def\checkobjectreferences
  {\startnointerference
   \protectlabels
   \ifx\usedoutputdriver\currentoutput
     \doutilities{objectreferences}\jobname\empty\relax\relax
   \else
     % different format (will fails on \purenumber)
   \fi
   \global\let\checkobjectreferences\relax
   \stopnointerference}

\def\objectplaceholder{NOT YET FLUSHED}%

\def\presetobject#1#2% \global added
  {\blabelgroup
   \ifcsname\r!object#1::#2\endcsname\else
     \global\@EA\let\csname\r!object#1::#2\endcsname\objectplaceholder
   \fi
   \elabelgroup}

\def\dosetobject#1#2#3% \initializepaper this will move to \everyshipout
  {\initializepaper
   \blabelgroup
   \ifcsname\r!object#2::#3\endcsname
     \elabelgroup \expandafter\gobblefivearguments
   \else % tzt, overload internal referenced objects to save entries
     \elabelgroup \expandafter\dodosetobject
   \fi
   {#1}{#2}{#3}}

\def\resetobject#1#2%
  {\checkobjectreferences
   \letbeundefined{\r!object#1::#2}}

%D \macros
%D   {finalizeobjectbox}
%D
%D This one provides a hook for last minute object box processing
%D we need this in \MKIV.

\ifx\finalizeobjectbox\undefined
    \let\finalizeobjectbox\gobbleoneargument
\fi

%D Somehow there is a rounding error problem in either \PDFTEX\
%D or in viewers, or maybe it is conforming the specs. The next
%D variable compensate for it by removing the rather tight
%D clip.

\def\objectoffset{1cm}

% \def\dodosetobject#1#2#3%
%   {\bgroup
%    \inobjecttrue
%    \dowithnextbox{\dododosetobject{#1}{#2}{#3}\egroup}}

\def\dodosetobject#1#2#3%
  {\bgroup
   \globalpushmacro\crossreferenceobject \objectreferenced
   \inobjecttrue
   \dowithnextbox
     {\globalpopmacro\crossreferenceobject
      \dododosetobject{#1}{#2}{#3}\egroup}}

\def\dododosetobject#1#2#3%
  {\blabelgroup
   \dontshowcomposition % rather fuzzy in \setxvalue ... \hbox
   \scratchdimen\objectoffset
   \@EA\xdef\csname\r!object#2::#3\endcsname
     {\noexpand\dohandleobject{#2}{#3}%
        {\ifhbox\nextbox\hbox\else\vbox\fi}%
       %{\the\nextboxwd}{\the\nextboxht}{\the\nextboxdp}}%
        {\number\nextboxwd}{\number\nextboxht}{\number\nextboxdp}%
        {\number\scratchdimen}}%
   \expanded % freeze the dimensions since \dostartobject may use \nextbox
     {\dostartobject
        {#2}{#3}{\the\nextboxwd}{\the\nextboxht}{\the\nextboxdp}}%
   \ifcase#1\relax\else \ifdim\objectoffset>\zeropoint
     \setbox\nextbox\vbox spread 2\scratchdimen
       {\forgetall \offinterlineskip
        \vss\hbox spread 2\scratchdimen{\hss\flushnextbox\hss}\vss}%
   \fi \fi
   \flushnextbox
   \dostopobject
   \elabelgroup}

\def\getobject#1#2%
  {\blabelgroup
   \let\dohandleobject\dogetobject
   \csname\r!object#1::#2\endcsname}

% \def\dogetobject#1#2#3#4#5#6%
%   {\initializepaper
%    \forgetall
%    \dontshowcomposition
%    \setbox\scratchbox\vbox
%      {\doinsertobject{#1}{#2}}%
%    \setbox\scratchbox#3%
%      {\vbox to #5\scaledpoint
%         {\ifdim\ht\scratchbox>#5\scaledpoint
%     % or \ifdim\wd\scratchbox>#4\scaledpoint
%            \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss
%          \else
%            \vss\box\scratchbox
%          \fi}}%
%    \wd\scratchbox#4\scaledpoint
%    \ht\scratchbox#5\scaledpoint
%    \dp\scratchbox#6\scaledpoint
%    \box\scratchbox
%    \elabelgroup}

% \def\dogetobject#1#2#3#4#5#6#7%
%   {\initializepaper
%    \forgetall
%    \dontshowcomposition
%    \setbox\scratchbox\vbox
%      {\doinsertobject{#1}{#2}}%
%    \setbox\scratchbox#3%
%      {\vbox to #5\scaledpoint
%         {\ifdim\ht\scratchbox>#5\scaledpoint
%     % or \ifdim\wd\scratchbox>#4\scaledpoint
%            \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss
%          \else
%            \vss\box\scratchbox
%          \fi}}%
%    \scratchdimen#7\scaledpoint
%    \setbox\nextbox\hbox
%      {\hskip-\scratchdimen\lower\scratchdimen\flushnextbox}%
%    \wd\scratchbox#4\scaledpoint
%    \ht\scratchbox#5\scaledpoint
%    \dp\scratchbox#6\scaledpoint
%    \box\scratchbox
%    \elabelgroup}

\def\dogetobject#1#2#3#4#5#6#7% don't change this, should work for dvi & pdf
  {\initializepaper
   \forgetall
   \dontshowcomposition
   \setbox\scratchbox\vbox
     {\doinsertobject{#1}{#2}}%
   \setbox\scratchbox#3%
     {\vbox to #5\scaledpoint
        {\ifdim\ht\scratchbox>#5\scaledpoint
           \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss
         \else\ifdim\wd\scratchbox>#4\scaledpoint
           \vss\hbox to #4\scaledpoint{\hss\box\scratchbox\hss}\vss
         \else
          %\vss\box\scratchbox
           \vss\hbox to #4\scaledpoint{\box\scratchbox\hss}% fix Chof
         \fi\fi}}%
   \box\scratchbox
   \elabelgroup}

%D If needed one can ask for the dimensions of an object with:
%D
%D \starttyping
%D \getobjectdimensions{class}{name}
%D \stoptyping
%D
%D The results are reported in \type {\objectwidth}, \type
%D {\objectheight} and \type {\objectdepth}.

% \def\dogetobjectdimensions#1#2#3#4#5#6%
%   {\def\objectwidth {#4\s!sp}%
%    \def\objectheight{#5\s!sp}%
%    \def\objectdepth {#6\s!sp}}

\def\dogetobjectdimensions#1#2#3#4#5#6#7%
  {\def\objectwidth {#4\s!sp}%
   \def\objectheight{#5\s!sp}%
   \def\objectdepth {#6\s!sp}%
   \def\objectmargin{#7\s!sp}}

\def\getobjectdimensions#1#2%
  {\let\dohandleobject\dogetobjectdimensions
   \let\objectwidth \!!zeropoint
   \let\objectheight\!!zeropoint
   \let\objectdepth \!!zeropoint
   \labelcsname\r!object#1::#2\endcsname}

%D Apart from this kind of objects, that have typeset content,
%D we can have low level driver specific objects. Both types
%D can have references to internal representations, hidden for
%D the user. We keep track of such references by means of a
%D dedicated cross reference mechanism. Normally, objects are
%D defined before they are used, but forward referencing
%D sometimes occurs.
%D
%D \starttyping
%D \dosetobjectreference {class} {identifier} {reference value} {page}
%D \dogetobjectreference {class} {identifier} \csname
%D \stoptyping
%D
%D These commands are to be called by the \type{\startobject},
%D \type{\stopobject} and \type{\insertobject} specials.

\def\objectreferenced{\global\chardef\crossreferenceobject\plusone}
\def\driverreferenced{\global\chardef\crossreferenceobject\zerocount}

\objectreferenced

% no undefined test ! ! ! ! (pdftex fails on undefined objects)

\def\setobjectreferences
  {\def\objectreference##1##2##3##4%
     {\ifundefined{\r!driver##1::##2}%
        \setxvalue{\r!driver##1::##2}{{##3}{##4}}%
      \else
        \showmessage\m!references{31}{[##1 ##2=>##3/##4]}%
      \fi}}

\def\resetobjectreferences
  {\let\objectreference\gobblefourarguments}

\resetobjectreferences

\def\doregisterobjectreference#1#2#3%
  {\checkobjectreferences
   \blabelgroup
   \expanded{\writeutilitycommand{\noexpand\objectreference{#1}{#2}{#3}{\noexpand\realfolio}}}%
   \setxvalue{\r!driver#1::#2}{{#3}{\noexpand\realfolio}}%
   \elabelgroup}

\def\dooverloadobjectreference#1#2#3%
  {\checkobjectreferences
   \blabelgroup
   \setxvalue{\r!driver#1::#2}{{#3}{\noexpand\realfolio}}%
   \elabelgroup}

\def\dosetobjectreference
  {\ifcase\crossreferenceobject
     \objectreferenced
     \expandafter\dooverloadobjectreference
   \else
     \expandafter\doregisterobjectreference
   \fi}

\def\dosetdriverreference
  {\driverreferenced\dosetobjectreference}

\def\defaultobjectreference#1#2{0} % driver dependent
\def\defaultobjectpage     #1#2{\realfolio}

\def\dogetobjectreference    {\dodogetobjectreference\firstoftwoarguments\defaultobjectreference}
\def\dogetobjectreferencepage{\dodogetobjectreference\secondoftwoarguments\defaultobjectpage}

\def\dodogetobjectreference#1#2#3#4#5%
  {\checkobjectreferences
   \blabelgroup
   \ifundefined{\r!driver#3::#4}%
     \showmessage\m!references{30}{[#3 #4=>#3/#4]}%
     \xdef#5{#2{#3}{#4}}%
   \else
     \xdef#5{\@EAEAEA#1\csname\r!driver#3::#4\endcsname}%
   \fi
   \elabelgroup}

\def\setobject     {\driverreferenced\dosetobject1}
\def\settightobject{\driverreferenced\dosetobject0}

%D \macros
%D   {doifobjectfoundelse,doifobjectreferencefoundelse}
%D
%D To prevent redundant definition of objects, one can use
%D the next tests:
%D
%D \starttyping
%D \doifobjectfoundelse{class}{object}{do then}{do else}
%D \doifobjectreferencefoundelse{class}{object}{do then}{do else}
%D \stoptyping

\def\doifobjectfoundelse#1#2%
  {\blabelgroup \ifcsname\r!object#1::#2\endcsname
     \elabelgroup \expandafter\firstoftwoarguments
   \else
     \elabelgroup \expandafter\secondoftwoarguments
   \fi}

\def\doifobjectreferencefoundelse#1#2%
  {\checkobjectreferences
   \blabelgroup \ifcsname\r!driver#1::#2\endcsname
     \elabelgroup \expandafter\firstoftwoarguments
   \else
     \elabelgroup \expandafter\secondoftwoarguments
   \fi}

%D \macros
%D   {doifobjectssupportedelse}
%D
%D Starting with reuse of graphics, we will implement object
%D reuse when possible. To enable mechanisms to determine
%D what method to use, we provide:
%D
%D \starttyping
%D \doifobjectssupportedelse{true action}{false action}
%D \stoptyping
%D
%D As we can see, currently objects depend on the special
%D driver.

\newif\ifobjectssupported \objectssupportedtrue

\def\doifobjectssupportedelse
  {\ifobjectssupported
     \@EA\doifspecialavailableelse\@EA\doinsertobject
   \else
     \@EA\secondoftwoarguments
   \fi}

%D There is a conceptual problem here. Objects are not possible
%D in \DVI, unless faked like in \type {spec-dvi}. This means
%D that we must be careful in loading special drivers that do
%D support objects while we still want to be able to use the
%D \DVI\ output.

\protect \endinput
