pack-com.mkxl /size: 34 Kb    last modification: 2023-12-21 09:44
1%D \module
2%D   [       file=pack-com, % used to be in core-mis,
3%D        version=20120111,
4%D          title=\CONTEXT\ Packing Macros,
5%D       subtitle=Combinations,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14\writestatus{loading}{ConTeXt Packaging Macros / Combinations}
15
16\unprotect
17
18% \startfloatcombination will be redone ... we can decouple the floatcontent
19% and caption and pass them to combinations so that we get better fit when the
20% caption is wider than the float, testcase:
21%
22% \startfloatcombination [2*2]
23%    \placefigure[local]{alpha}{\externalfigure[cow.pdf][width=1cm]}%
24%    \placefigure[local]{beta} {\externalfigure[cow.pdf][width=2cm]}%
25%    \placefigure[local]{gamma}{\externalfigure[cow.pdf][width=3cm]}
26%    \placefigure[local]{delta}{\externalfigure[cow.pdf][width=4cm]}
27% \stopfloatcombination
28
29%D We could of course map combinations onto one of the table mechanisms but as it
30%D has served us well for ages we keep this one. The code has been cleaned up a bit
31%D and mkiv'd.
32%D
33%D Okay ... I might luafy this one eventually.
34
35% \startcombination      {alpha} {a} {beta} {b} \stopcombination
36% \startcombination[2*1] {alpha} {a} {beta} {b} \stopcombination
37% \startcombination[1*2] {alpha} {a} {beta} {b} \stopcombination
38% \startcombination[2]   {alpha} {a} {beta} {b} \stopcombination
39% \startcombination[2]   \combination {alpha} {a} \combination{beta} {b} \stopcombination
40
41%D We do support some structure but the order matters and currently it's only window
42%D dressing:
43
44%D \starttyping
45%D \let\startcontent\bgroup
46%D \let\stopcontent \egroup
47%D \let\startcaption\bgroup
48%D \let\stopcaption \egroup
49%D \stoptyping
50%D
51%D Of course we should have started with more structure as it would simply the code.
52%D
53%D \starttyping
54%D \startcombination
55%D     \startcontent
56%D         \externalfigure[cow]
57%D     \stopcontent
58%D     \startcaption
59%D         Some cow.
60%D     \stopcaption
61%D     \startcontent
62%D         \externalfigure[cow]
63%D     \stopcontent
64%D     \startcaption
65%D         The same cow.
66%D     \stopcaption
67%D \stopcombination
68%D \stoptyping
69
70\ifdefined\dotagcombination \else \aliased\let\dotagcombination\relax \fi
71
72\newsystemmode{combination}
73
74\appendtoks
75    \globalresetsystemmode{combination}%
76\to \everyinsidefloat
77
78\newinteger  \c_pack_combinations_nesting        % local
79
80\newinteger  \c_pack_combinations_x              % global
81\newinteger  \c_pack_combinations_y              % global
82\newinteger  \c_pack_combinations_x_saved
83\newinteger  \c_pack_combinations_y_saved
84\newinteger  \c_pack_combinations_max            % global
85\newinteger  \c_pack_combinations_max_saved
86\newdimension\d_pack_combinations_ht             % global
87\newdimension\d_pack_combinations_ht_saved
88\newbox      \b_pack_combinations_captions       % global % can go
89\newbox      \b_pack_combinations_captions_saved
90\newbox      \b_pack_combinations_temp           % global % can go
91\newbox      \b_pack_combinations_temp_saved
92\newbox      \b_pack_combinations_content        % local
93\newbox      \b_pack_combinations_content_saved
94\newbox      \b_pack_combinations_caption        % local
95\newbox      \b_pack_combinations_caption_saved
96
97\installcorenamespace{combination}
98
99\installcommandhandler \??combination {combination} \??combination
100
101\initializeboxstack{\??combination captions}
102\initializeboxstack{\??combination temp}
103
104\c_strc_constructions_define_commands\conditionalfalse
105
106\def\pack_combinations_push
107  {\advanceby\c_pack_combinations_nesting\plusone
108   \ifnum\c_pack_combinations_nesting>\plusone
109     \c_pack_combinations_x_saved  \c_pack_combinations_x
110     \c_pack_combinations_y_saved  \c_pack_combinations_y
111     \c_pack_combinations_max_saved\c_pack_combinations_max
112     \d_pack_combinations_ht_saved \d_pack_combinations_ht
113     \setbox\b_pack_combinations_captions_saved\box\b_pack_combinations_captions
114     \setbox\b_pack_combinations_temp_saved    \box\b_pack_combinations_temp
115     \setbox\b_pack_combinations_content_saved \box\b_pack_combinations_content
116     \setbox\b_pack_combinations_caption_saved \box\b_pack_combinations_caption
117   \else
118     \globalsetsystemmode{combination}% why global
119   \fi}
120
121\def\pack_combinations_pop
122  {\ifnum\c_pack_combinations_nesting>\plusone
123     \global\c_pack_combinations_x  \c_pack_combinations_x_saved
124     \global\c_pack_combinations_y  \c_pack_combinations_y_saved
125     \global\c_pack_combinations_max\c_pack_combinations_max_saved
126     \global\d_pack_combinations_ht \d_pack_combinations_ht_saved
127     \global\setbox\b_pack_combinations_captions\box\b_pack_combinations_captions_saved
128     \global\setbox\b_pack_combinations_temp    \box\b_pack_combinations_temp_saved
129     \setbox\b_pack_combinations_content        \box\b_pack_combinations_content_saved
130     \setbox\b_pack_combinations_caption        \box\b_pack_combinations_caption_saved
131   \else
132     \globalresetsystemmode{combination}% why global
133   \fi
134   \advanceby\c_pack_combinations_nesting\minusone}
135
136\definelabel
137  [\v!combination] % handy for configuring
138  [\c!numberconversion=\v!character,
139   \c!text=]
140
141\c_strc_constructions_define_commands\conditionaltrue
142
143\setupcombination
144  [\c!width=\v!fit,
145   \c!height=\v!fit,
146   \c!distance=\emwidth,
147   \c!location=\v!bottom, % can be something {top,left}
148   \c!before=\blank,
149   \c!after=,
150   \c!inbetween={\blank[\v!medium]},
151  %\c!style=,
152  %\c!color=,
153   \c!nx=2, % new
154   \c!ny=1, % new
155   \c!align=\v!middle]
156
157\let\setupcombinations\setupcombination % for the moment (we might distinguish)
158
159\installcorenamespace{combinationlocation}
160\installcorenamespace{combinationalternative}
161
162\appendtoks
163    \c_strc_constructions_define_commands\conditionalfalse
164    \normalexpanded
165      {\definelabel
166         [\v!combination:\currentcombination]%
167         [\v!combination\ifempty\currentcombinationparent\else:\currentcombinationparent\fi]}%
168         [\s!counter=\currentcombination,\c!levels=1]%
169    \c_strc_constructions_define_commands\conditionaltrue
170\to \everydefinecombination
171
172\defcsname\??combinationlocation\v!left  \endcsname{\let\m_pack_combinations_leftfiller \relax}
173\defcsname\??combinationlocation\v!right \endcsname{\let\m_pack_combinations_rightfiller\relax}
174\defcsname\??combinationlocation\v!top   \endcsname{\let\m_pack_combinations_valigner   \depthonlybox}
175\defcsname\??combinationlocation\v!middle\endcsname{\let\m_pack_combinations_valigner   \halfwaybox}
176
177\def\pack_combinations_location_reset
178  {\let\m_pack_combinations_rightfiller\hfil
179   \let\m_pack_combinations_leftfiller \hfil
180   \let\m_pack_combinations_valigner   \firstofoneargument}
181
182\pack_combinations_location_reset
183
184\def\pack_combinations_location_step#1%
185  {\csname\??combinationlocation#1\endcsname}
186
187% formally ok:
188%
189% \protected\def\stopcombination
190%   {\egroup
191%    \egroup}
192%
193% more robust:
194%
195% \protected\def\stopcombination
196%   {{}{}{}{}{}{}{}{}% catches (at most 4) missing entries
197%    \egroup
198%    \egroup}
199%
200% even better:
201%
202% \protected\def\stopcombination
203%   {\bgroup
204%    \scratchtoks{{}}%
205%    \dorecurse\c_pack_combinations_y
206%      {\toksapp{{}{}}}%
207%    \expandafter\egroup\the\scratchtoks
208%    \egroup
209%    \dostoptagged
210%    \egroup}
211%
212% faster
213
214\ifdefined\startcontent \else \aliased\let\startcontent\relax \fi
215\ifdefined\stopcontent  \else \aliased\let\stopcontent \relax \fi
216\ifdefined\startcaption \else \aliased\let\startcaption\relax \fi
217\ifdefined\stopcaption  \else \aliased\let\stopcaption \relax \fi
218
219\protected\def\pack_common_content_start{\bgroup\ignorespaces}
220\protected\def\pack_common_content_stop {\removeunwantedspaces\egroup}
221\protected\def\pack_common_caption_start{\bgroup\ignorespaces}
222\protected\def\pack_common_caption_stop {\removeunwantedspaces\egroup}
223
224\newtoks\everycombination
225
226\aliased\lettonothing\combination
227
228\lettonothing\p_nx_ny
229
230\installcorenamespace{combinationmethod}
231
232% \defcsname\??combinationmethod:\v!start\endcsname
233%   {}
234%
235% \defcsname\??combinationmethod\endcsname
236%   {\vbox}
237%
238% \defcsname\??combinationmethod:\v!stop\endcsname
239%   {}
240
241\permanent\tolerant\protected\def\startcombination[#S#1]#*[#S#2]% can be simplified
242  {\bgroup % so we can grab a group
243   \pack_combinations_push
244   \cdef\currentcombination{#1}%
245   \edef\p_nx_ny{#2}%
246   %
247   \ifempty\p_nx_ny
248     \ifhastok={#1}%
249       \lettonothing\currentcombination
250       \setupcurrentcombination[#1]%
251       \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
252     \orelse\ifhastok*\currentcombination
253       \edef\p_nx_ny{\currentcombination*\plusone*}%
254       \lettonothing\currentcombination
255     \orelse\ifchknum\currentcombination\or
256       \edef\p_nx_ny{\currentcombination*\plusone*}%
257       \lettonothing\currentcombination
258     \else
259       \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
260     \fi
261   \orelse\ifhastok={#2}%
262     \setupcurrentcombination[#2]%
263     \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
264   \else
265     \edef\p_nx_ny{\p_nx_ny*\plusone*}%
266   \fi
267   \begincsname\??combinationmethod\combinationparameter\c!method:\v!start\endcsname
268   %
269% test first:
270%
271%    \ifempty\p_nx_ny
272%      \ifhastok={#1}%
273%        \lettonothing\currentcombination
274%        \setupcurrentcombination[#1]%
275%        \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
276%      \orelse\ifhastok*{\currentcombination}%
277%        \edef\p_nx_ny{\currentcombination*\plusone*}%
278%        \lettonothing\currentcombination
279%      \orelse\ifchknum\currentcombination\or
280%        \edef\p_nx_ny{\currentcombination*\plusone*}%
281%        \lettonothing\currentcombination
282%      \else
283%        \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
284%      \fi
285%    \orelse\ifhastok={#2}%
286%      \setupcurrentcombination[#2]%
287%      \edef\p_nx_ny{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
288%    \else
289%      \edef\p_nx_ny{\p_nx_ny*\plusone*}%
290%    \fi
291   %
292   \forgetall
293   %
294   \expand\everycombination
295   %
296   \enforced\let\startcontent\pack_common_content_start
297   \enforced\let\stopcontent \pack_common_content_stop
298   \enforced\let\startcaption\pack_common_caption_start
299   \enforced\let\stopcaption \pack_common_caption_stop
300   %
301   \edef\p_height  {\combinationparameter\c!height}%
302   \edef\p_width   {\combinationparameter\c!width}%
303   \edef\p_location{\combinationparameter\c!location}%
304   \edef\p_distance{\combinationparameter\c!distance}%
305   %
306   \pack_combinations_location_reset
307   \rawprocesscommacommand[\p_location]\pack_combinations_location_step
308   %
309   \dostarttaggedchained\t!combination\currentcombination\??combination
310%    \vbox \ifx\p_height\v!fit\else to \p_height \fi \bgroup
311   \ifcsname\??combinationmethod\combinationparameter\c!method\endcsname
312     \lastnamedcs\else\vbox
313   \fi\ifx\p_height\v!fit\else to \p_height \fi \bgroup
314   \enforced\lettonothing\combination % permits \combination{}{} handy for cld
315   \normalexpanded{\pack_combinations_start_indeed[\p_nx_ny]}}
316
317\permanent\protected\def\stopcombination
318  {\bgroup\normalexpanded{\egroup{}\ntimes{{}{}}\c_pack_combinations_y}% brr
319   \dostoptagged
320   \egroup
321   \begincsname\??combinationmethod\combinationparameter\c!method:\v!stop\endcsname
322   \pack_combinations_pop
323   \egroup}
324
325\let\pack_combinations_check_x_y\relax
326
327\aliased\let\combinationwidth\!!zeropoint
328
329\protected\def\pack_combinations_start_indeed[#1*#2*#3]%
330  {\global\c_pack_combinations_x#1\relax
331   \global\c_pack_combinations_y#2\relax
332   \setexpandedcombinationparameter\c!nx{\the\c_pack_combinations_x}% in case we access it
333   \setexpandedcombinationparameter\c!ny{\the\c_pack_combinations_y}% in case we access it
334   \pack_combinations_check_x_y
335   \dotagcombination
336   \global\setbox\b_pack_combinations_captions\emptybox
337   \global\c_pack_combinations_max\c_pack_combinations_x
338   \multiplyby\c_pack_combinations_y\c_pack_combinations_x
339   \tabskip\zeroskip
340   \enforced\permanent\protected\edef\combinationwidth % \immutable
341     {\the\dimexpr
342        (\hsize-\numexpr\c_pack_combinations_x-\plusone\relax\dimexpr\combinationparameter\c!distance\relax)/\c_pack_combinations_x
343      \relax}%
344   \halign \ifx\p_width\v!fit\else to \p_width \fi \bgroup % repetitive preamble
345 % \halign noskips \ifx\p_width\v!fit\else to \p_width \fi \bgroup % repetitive preamble
346   \aligntab
347   \m_pack_combinations_leftfiller
348   \aligncontent
349   \m_pack_combinations_rightfiller
350   \aligntab
351   \tabskip\zeropoint \s!plus 1fill % \fillskip
352   \aligncontent
353   \cr
354   \pack_combinations_pickup}
355
356%D I've first considered using a constructor directly but it's more overhead and
357%D some settings conflict with already used combination settings so instead we plug
358%D in labels. This also permits extensions later on.
359
360\appendtoks
361    \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
362\to \everydefinecombination
363
364\def\pack_combinations_pickup
365  {\dostarttagged\t!combinationpair\empty     % better make this text
366   \dostarttagged\t!combinationcontent\empty
367   \expandafterpars\pack_combinations_pickup_content_indeed}
368
369\def\pack_combinations_pickup_content_indeed
370  {\dowithnextboxcs\pack_combinations_pickup_content\hbox}
371
372\def\pack_combinations_pickup_content % we want to add struts but still ignore an empty box
373  {\dostoptagged
374   \setbox\b_pack_combinations_content\box\nextbox
375   \dostarttagged\t!combinationcaption\empty
376   \expandnamespacemacro\??combinationalternative\p_pack_combinations_alternative\v!text}
377
378\defcsname\??combinationalternative\v!text\endcsname
379  {\expandafterpars\pack_combinations_alternative_text_indeed}
380
381\defcsname\??combinationalternative\v!label\endcsname
382  {\expandafterpars\pack_combinations_alternative_label_indeed}
383
384\def\pack_combinations_alternative_text_indeed
385  {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
386     \afterassignment\pack_combinations_caption_first
387     \let\nexttoken=}
388
389\def\pack_combinations_alternative_label_indeed
390  {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
391     \hsize\wd\b_pack_combinations_content
392     \usealignparameter\combinationparameter
393     \usecombinationstyleandcolor\c!style\c!color
394     \begstrut
395       \normalexpanded{\strc_labels_command[\v!combination\ifempty\currentcombination\else:\currentcombination\fi]}%
396     \endstrut
397   \egroup}
398
399\appendtoks
400    \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
401    \ifx\p_pack_combinations_alternative\v!label
402      \ifcstok{\combinationparameter\c!continue}\v!yes\else
403        \normalexpanded{\strc_labels_reset{\v!combination\ifempty\currentcombination\else:\currentcombination\fi}{1}}%
404     \fi
405    \fi
406\to \everycombination
407
408\def\pack_combinations_pickup_caption
409  {\dostoptagged
410   \dostoptagged
411   \setbox\b_pack_combinations_caption\box\nextbox
412   \pack_combinations_pickup_package_pair}
413
414\def\pack_combinations_caption_first
415  {\futurelet\nexttoken\pack_combinations_caption_second}
416
417\def\pack_combinations_caption_second
418  {\ifx\nexttoken\egroup
419     % the caption is empty
420   \orelse\ifx\nexttoken\stopcaption % language
421     % the caption is empty (new per 2014-05-24)
422   \orelse\ifx\nexttoken\pack_combinations_float_hack_b
423     % make sure we honor empty captions (new per 2023-04-20)
424   \else
425     % todo: \p_pack_combinations_alternative\v!none: no style, strut etc
426     \hsize\wd\b_pack_combinations_content
427     \usealignparameter\combinationparameter
428     \usecombinationstyleandcolor\c!style\c!color
429     \bgroup
430     \ifcstok{\combinationparameter\c!strut}\v!no
431       % make sure we have empty captions (new per 2023-04-20)
432       \aftergroup\egroup
433     \else
434       \aftergroup\endstrut
435       \aftergroup\egroup
436       \begstrut
437     \fi
438   \fi}
439
440\def\pack_combinations_pickup_package_pair % we need to store the caption row
441  {\vbox
442     {\forgetall
443      \m_pack_combinations_valigner{\box\b_pack_combinations_content}%
444      % we need to save the caption for a next alignment line
445      \pack_combinations_save_caption}%
446   \ifnum\c_pack_combinations_y>\plusone
447     \global\advanceby\c_pack_combinations_y\minusone
448     \global\advanceby\c_pack_combinations_x\minusone
449     \ifcase\c_pack_combinations_x
450       \doubleexpandafter\pack_combinations_pickup_package_pair_a
451     \else
452       \doubleexpandafter\pack_combinations_pickup_package_pair_b
453     \fi
454   \else
455     \singleexpandafter\pack_combinations_pickup_package_pair_c
456   \fi}
457
458\def\pack_combinations_pickup_package_pair_a
459  {\cr
460   \pack_combinations_flush_captions
461   \noalign
462     {\forgetall
463      \global\setbox\b_pack_combinations_captions\emptybox
464      \nointerlineskip
465      \combinationparameter\c!after
466      \combinationparameter\c!before
467      \vss
468      \nointerlineskip}%
469   \global\c_pack_combinations_x\c_pack_combinations_max
470   \pack_combinations_pickup}
471
472\def\pack_combinations_pickup_package_pair_b
473  {\aligntab
474   \aligntab
475   \aligntab
476   \kern\p_distance
477   \aligntab
478   \pack_combinations_pickup}
479
480\def\pack_combinations_pickup_package_pair_c
481  {\cr
482   \pack_combinations_flush_captions
483   \egroup}
484
485\installcorenamespace{combinationcaption}
486
487\def\pack_combinations_save_caption
488  {\ifdim\htdp\b_pack_combinations_caption>\d_pack_combinations_ht
489     \global\d_pack_combinations_ht\htdp\b_pack_combinations_caption
490   \fi
491   \savebox{\??combinationcaption:\the\c_pack_combinations_nesting}{\the\c_pack_combinations_x}{\box\b_pack_combinations_caption}}
492
493\let\pack_combinations_flush_captions_indeed\relax
494
495\def\pack_combinations_flush_captions
496  {\noalign
497     {\ifdim\d_pack_combinations_ht>\zeropoint
498        \nointerlineskip % indeed
499        \combinationparameter\c!inbetween
500        \global\c_pack_combinations_x\c_pack_combinations_max
501        \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_yes
502      \else
503        \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_nop
504      \fi}%
505   \pack_combinations_flush_captions_indeed
506   \crcr}
507
508\def\pack_combinations_flush_captions_yes
509  {\vpack to \d_pack_combinations_ht\bgroup
510     \foundbox{\??combinationcaption:\the\c_pack_combinations_nesting}{\the\c_pack_combinations_x}%
511     \vss
512   \egroup
513   \global\advanceby\c_pack_combinations_x\minusone
514   \ifnum\c_pack_combinations_x>\zerocount % \c_pack_combinations_max
515     \expandafter\pack_combinations_flush_captions_yes_followup
516   \else
517     \global\d_pack_combinations_ht\zeropoint
518     \initializeboxstack{\??combinationcaption:\number-\c_pack_combinations_nesting}%
519   \fi}
520
521\let\pack_combinations_flush_captions_nop\donothing
522
523\def\pack_combinations_flush_captions_yes_followup
524  {\aligntab
525   \aligntab
526   \aligntab
527   \aligntab
528   \pack_combinations_flush_captions_indeed}
529
530%D \macros
531%D   {startfloatcombination}
532%D
533%D \setupexternalfigures[directory={../sample}]
534%D \startbuffer
535%D \placefigure
536%D   [left,none]
537%D   {}
538%D   {\startfloatcombination[2*2]
539%D      \placefigure{alpha}{\externalfigure[cow.pdf][width=1cm]}
540%D      \placefigure{beta} {\externalfigure[cow.pdf][width=2cm]}
541%D      \placefigure{gamma}{\externalfigure[cow.pdf][width=3cm]}
542%D      \placefigure{delta}{\externalfigure[cow.pdf][width=4cm]}
543%D    \stopfloatcombination}
544%D
545%D \input tufte
546%D \stopbuffer
547%D
548%D \typebuffer \getbuffer
549
550\definecombination
551  [\v!float]
552% [\c!strut=\v!no] % not needed, we intercept anyway
553
554\protected\def\pack_combinations_float_hack_a#1%
555  {\strc_floats_build_box_separate_split{#1}%
556   \box\b_strc_floats_separate_content}
557
558\protected\def\pack_combinations_float_hack_b#1%
559  {\box\b_strc_floats_separate_caption}
560
561\permanent\tolerant\protected\def\startfloatcombination[#1]#*[#2]%
562  {\ifinsidefloat\else\dontleavehmode\fi % tricky, floatcombinations fail to align well otherwise
563   \vbox\bgroup
564   \strc_floats_build_box_separate_set
565  %\insidecolumnstrue % trick, forces no centering, todo: proper switch/feature
566   \postcenterfloatmethod\zerocount
567   \forcelocalfloats
568   \enforced\permanent\protected\def\stopfloatcombination{\pack_combinations_stop_float{#1}}}
569
570\aliased\let\stopfloatcombination\relax
571
572\def\pack_combinations_float_check_x_y
573  {\ifnum\numexpr\c_pack_combinations_x*\c_pack_combinations_y\relax<\noflocalfloats\relax
574       \global\c_pack_combinations_x\noflocalfloats
575       \global\c_pack_combinations_y\plusone
576   \fi
577   \let\pack_combinations_check_x_y\relax}%
578
579\def\pack_combinations_stop_float#1%
580  {\scratchtoks\emptytoks
581   \dorecurse\noflocalfloats
582     {\appendetoks
583        {\pack_combinations_float_hack_a{\recurselevel}}%
584        {\pack_combinations_float_hack_b{\recurselevel}}%
585      \to\scratchtoks}% brrr
586   \let\pack_combinations_check_x_y\pack_combinations_float_check_x_y
587 % \normalexpanded{\startcombination[#1]\the\scratchtoks}\stopcombination
588   \normalexpanded{\startcombination[\v!float][#1]\the\scratchtoks}\stopcombination
589   \resetlocalfloats
590   \egroup}
591
592%D \macros
593%D   {definepairedbox, setuppairedbox, placepairedbox}
594%D
595%D Paired boxes, formally called legends, but from now on a legend is just an
596%D instance, are primarily meant for typesetting some text alongside an
597%D illustration. Although there is quite some variation possible, the functionality
598%D is kept simple, if only because in most cases such pairs are typeset sober.
599%D
600%D The location specification accepts a pair, where the first keyword specifies the
601%D arrangement, and the second one the alignment. The first key of the location pair
602%D is one of \type {left}, \type {right}, \type {top} or \type {bottom}, while the
603%D second key can also be \type {middle}.
604%D
605%D The first box is just collected in an horizontal box, but the second one is a
606%D vertical box that gets passed the bodyfont and alignment settings.
607%D
608%D In many cases the table builders can be used instead, but as this mechanism is a
609%D traditional \CONTEXT\ one we keep it around.
610
611%D \macros
612%D   {setuplegend, placelegend}
613%D
614%D It makes sense to typeset a legend to a figure in \TEX\ and not in a drawing
615%D package. The macro \type {\placelegend} combines a figure (or something else) and
616%D its legend. This command is just a paired box.
617%D
618%D The legend is placed according to \type {location}, being \type {bottom} or \type
619%D {right}. The macro macro is used as follows.
620%D
621%D \starttyping
622%D \placefigure
623%D   {whow}
624%D   {\placelegend
625%D      {\externalfigure[cow]}
626%D      {\starttabulate
627%D       \NC 1 \NC head \NC \NR
628%D       \NC 2 \NC legs \NC \NR
629%D       \NC 3 \NC tail \NC \NR
630%D       \stoptabulate}}
631%D
632%D \placefigure
633%D   {whow}
634%D   {\placelegend
635%D      {\externalfigure[cow]}
636%D      {\starttabulate[|l|l|l|l|]
637%D       \NC 1 \NC head \NC 3 \NC tail \NC \NR
638%D       \NC 2 \NC legs \NC   \NC      \NC \NR
639%D       \stoptabulate}}
640%D
641%D \placefigure
642%D   {whow}
643%D   {\placelegend[n=2]
644%D      {\externalfigure[cow]}
645%D      {\starttabulate
646%D       \NC 1 \NC head \NC \NR
647%D       \NC 2 \NC legs \NC \NR
648%D       \NC 3 \NC tail \NC \NR
649%D       \stoptabulate}}
650%D
651%D \placefigure
652%D   {whow}
653%D   {\placelegend[n=2]
654%D      {\externalfigure[cow]}
655%D      {head \par legs \par tail}}
656%D
657%D \placefigure
658%D   {whow}
659%D   {\placelegend[n=2]
660%D      {\externalfigure[cow]}
661%D      {\startitemize[packed]
662%D       \item head \item legs \item  tail \item belly \item horns
663%D       \stopitemize}}
664%D
665%D \placefigure
666%D   {whow}
667%D   {\placelegend[n=2,width=.8\hsize]
668%D      {\externalfigure[cow]}
669%D      {\startitemize[packed]
670%D       \item head \item legs \item  tail \item belly \item horns
671%D       \stopitemize}}
672%D
673%D \def\MytTestTwo#1#2%
674%D   {\placefigure
675%D      {whow}
676%D      {\placelegend[location={#1,#2}]
677%D         {\externalfigure[cow]}
678%D         {\starttabulate
679%D          \NC 1 \NC head \NC \NR
680%D          \NC 2 \NC legs \NC \NR
681%D          \NC 3 \NC tail \NC \NR
682%D          \stoptabulate}}}
683%D
684%D \def\MytTestOne#1{\processcommalist[left,right,top,bottom]{\MytTestTwo{#1}}}
685%D
686%D \processcommalist[left,right,top,bottom,middle]\MytTestOne
687%D \stoptyping
688%D
689%D More structure is also possible (the order matters!):
690%D
691%D \starttyping
692%D \startplacefigure[title=whow]
693%D    \startplacelegend[location={bottom,middle},color=red]
694%D         \startcontent
695%D             \externalfigure[cow]
696%D         \stopcontent
697%D         \startcaption
698%D             \starttabulate[|l|l|]
699%D                 \NC 1 \NC head \NC \NR
700%D                 \NC 2 \NC legs \NC \NR
701%D                 \NC 3 \NC tail \NC \NR
702%D             \stoptabulate
703%D         \stopcaption
704%D    \stopplacelegend
705%D \stopplacefigure
706%D \stoptyping
707
708% todo: natural size
709
710\newsystemmode{pairedbox}
711
712\appendtoks
713    \globalresetsystemmode{pairedbox}%
714\to \everyinsidefloat
715
716\installcorenamespace {pairedbox}
717
718\installcommandhandler \??pairedbox {pairedbox} \??pairedbox
719
720\setuppairedbox
721 [\c!n=1,
722  \c!distance=\bodyfontsize,
723 %\c!before=,
724 %\c!after=,
725 %\c!color=,
726 %\c!style=,
727  \c!inbetween={\blank[\v!medium]},
728  \c!width=\hsize,
729  \c!height=\vsize,
730  \c!maxwidth=\textwidth,   % \makeupwidth,
731  \c!maxheight=\textheight, % \makeupheight,
732 %\c!bodyfont=,
733 %\c!align=,
734  \c!location=\v!bottom]
735
736% watch the hsize/vsize tricks
737
738\newbox      \b_pack_pairedboxes_first
739\newbox      \b_pack_pairedboxes_second
740\newdimension\s_pack_pairedboxes_size
741
742\appendtoks
743   \frozen\protected\instance\edefcsname\e!setup\currentpairedbox\e!endsetup\endcsname{\setuppairedbox     [\currentpairedbox]}%
744   \frozen\protected\instance\edefcsname\e!place\currentpairedbox           \endcsname{\placepairedbox     [\currentpairedbox]}% one argument is mandate anyway
745   \frozen\protected\instance\edefcsname\e!start\e!place\currentpairedbox   \endcsname{\startplacepairedbox[\currentpairedbox]}% one argument is mandate anyway
746   \frozen\protected\instance\edefcsname\e!stop\e!place \currentpairedbox   \endcsname{\stopplacepairedbox                    }%
747\to \everydefinepairedbox
748
749\permanent\tolerant\protected\def\startplacepairedbox[#1]#*[#S#2]%
750  {\bgroup
751   \cdef\currentpairedbox{#1}%
752   \setupcurrentpairedbox[#2]%
753   \pairedboxparameter\c!before
754   \bgroup
755   \edef\p_location{\pairedboxparameter\c!location}%
756   \edef\p_n       {\pairedboxparameter\c!n}%
757   %
758   \enforced\let\startcontent\pack_common_content_start
759   \enforced\let\stopcontent \pack_common_content_stop
760   \enforced\let\startcaption\pack_common_caption_start
761   \enforced\let\stopcaption \pack_common_caption_stop
762   %
763   \globalsetsystemmode{pairedbox}%
764   \pack_pairedboxes_before
765   \expandafterpars\pack_pairedboxes_first_pickup}
766
767\permanent\protected\def\stopplacepairedbox{} % we just pick up two boxes
768
769\aliased\let\placepairedbox\startplacepairedbox    % we just pick up two boxes
770
771\def\pack_pairedboxes_first_pickup
772  {\dowithnextboxcs\pack_pairedboxes_first\hbox
773     \bgroup
774     \let\next=}
775
776\def\pack_pairedboxes_first
777  {\pack_pairedboxes_between
778   \expandafterpars\pack_pairedboxes_second_pickup}
779
780\def\pack_pairedboxes_second_pickup
781  {\dowithnextboxcs\pack_pairedboxes_second\vbox
782     \bgroup
783     \pack_pairedboxes_inside_second
784     \let\next=}
785
786\def\pack_pairedboxes_second
787  {\pack_pairedboxes_after
788   \egroup
789   \pairedboxparameter\c!after
790   \egroup}
791
792\newconditional\c_pack_pairedboxes_horizontal \c_pack_pairedboxes_horizontal\conditionaltrue
793
794\installcorenamespace{pairedboxnature}
795\installcorenamespace{pairedboxalign}
796
797\let\pack_pairedboxes_flush      \relax
798\let\pack_pairedboxes_fill_top   \relax
799\let\pack_pairedboxes_fill_bottom\relax
800
801\defcsname\??pairedboxnature\v!left\endcsname
802  {\c_pack_pairedboxes_horizontal\conditionaltrue
803   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_left}
804
805\defcsname\??pairedboxnature\v!right\endcsname
806  {\c_pack_pairedboxes_horizontal\conditionaltrue
807   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_right}
808
809\defcsname\??pairedboxnature\v!top\endcsname
810  {\c_pack_pairedboxes_horizontal\conditionalfalse
811   \let\pack_pairedboxes_fill_top\relax
812   \let\pack_pairedboxes_fill_bottom\vss
813   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_top}
814
815\defcsname\??pairedboxnature\v!bottom\endcsname
816  {\c_pack_pairedboxes_horizontal\conditionalfalse
817   \let\pack_pairedboxes_fill_top\vss
818   \let\pack_pairedboxes_fill_bottom\relax
819   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_bottom}
820
821\def\pack_pairedboxes_flush_left
822  {\box\b_pack_pairedboxes_second
823   \kern\pairedboxparameter\c!distance
824   \box\b_pack_pairedboxes_first}
825
826\def\pack_pairedboxes_flush_right
827  {\box\b_pack_pairedboxes_first
828   \kern\pairedboxparameter\c!distance
829   \box\b_pack_pairedboxes_second}
830
831\def\pack_pairedboxes_flush_top
832  {\box\b_pack_pairedboxes_second
833   \endgraf
834   \nointerlineskip
835   \pairedboxparameter\c!inbetween
836   \box\b_pack_pairedboxes_first}
837
838\def\pack_pairedboxes_flush_bottom
839  {\box\b_pack_pairedboxes_first
840   \endgraf
841   \nointerlineskip
842   \pairedboxparameter\c!inbetween
843   \box\b_pack_pairedboxes_second}
844
845\let\pack_pairedboxes_align_l\relax
846\let\pack_pairedboxes_align_r\relax
847\let\pack_pairedboxes_align_t\relax
848\let\pack_pairedboxes_align_b\relax
849
850\defcsname\??pairedboxalign\v!left\endcsname % 0
851  {\let\pack_pairedboxes_align_l\relax
852   \let\pack_pairedboxes_align_r\hss
853   \let\pack_pairedboxes_align_t\relax
854   \let\pack_pairedboxes_align_b\relax}
855
856\defcsname\??pairedboxalign\v!right\endcsname % 1
857  {\let\pack_pairedboxes_align_l\hss
858   \let\pack_pairedboxes_align_r\relax
859   \let\pack_pairedboxes_align_t\relax
860   \let\pack_pairedboxes_align_b\relax}
861
862\defcsname\??pairedboxalign\v!high\endcsname % 2
863  {\let\pack_pairedboxes_align_l\relax
864   \let\pack_pairedboxes_align_r\relax
865   \let\pack_pairedboxes_align_t\relax
866   \let\pack_pairedboxes_align_b\vss}
867
868\defcsname\??pairedboxalign\v!low\endcsname % 3
869  {\let\pack_pairedboxes_align_l\relax
870   \let\pack_pairedboxes_align_r\relax
871   \let\pack_pairedboxes_align_t\vss
872   \let\pack_pairedboxes_align_b\relax}
873
874\defcsname\??pairedboxalign\v!middle\endcsname % 4
875  {\let\pack_pairedboxes_align_l\hss
876   \let\pack_pairedboxes_align_r\hss
877   \let\pack_pairedboxes_align_t\vss
878   \let\pack_pairedboxes_align_b\vss}
879
880\defcsname\??pairedboxalign\v!bottom\endcsname{\csname\??pairedboxalign\v!low \endcsname}
881\defcsname\??pairedboxalign   \v!top\endcsname{\csname\??pairedboxalign\v!high\endcsname}
882
883\def\pack_pairedbox_valign#1{\setbox#1\vpack to \s_pack_pairedboxes_size{\pack_pairedboxes_align_t\box#1\pack_pairedboxes_align_b}}
884\def\pack_pairedbox_halign#1{\setbox#1\hpack to \s_pack_pairedboxes_size{\pack_pairedboxes_align_l\box#1\pack_pairedboxes_align_r}}
885
886\def\pack_pairedboxes_before
887  {\ifempty\p_location
888      \csname\??pairedboxnature\v!left  \endcsname
889      \csname\??pairedboxalign \v!middle\endcsname
890   \else
891     \getfromcommacommand[\p_location][1]%
892     \csname\??pairedboxnature
893       \ifcsname\??pairedboxnature\commalistelement\endcsname\commalistelement\else\v!left\fi
894     \endcsname
895     \getfromcommacommand[\p_location][2]%
896     \csname\??pairedboxalign
897       \ifcsname\??pairedboxalign\commalistelement\endcsname\commalistelement\else\v!middle\fi
898     \endcsname
899   \fi}
900
901\def\pack_pairedboxes_between
902  {\usebodyfontparameter\pairedboxparameter
903   \setbox\b_pack_pairedboxes_first\box\nextbox
904   \ifconditional\c_pack_pairedboxes_horizontal
905     \pack_pairedboxes_between_horizontal
906   \else
907     \pack_pairedboxes_between_vertical
908   \fi
909   \ifnum\p_n>\plusone
910     \setrigidcolumnhsize\hsize{\pairedboxparameter\c!distance}\p_n
911   \fi}
912
913\def\pack_pairedboxes_between_horizontal
914  {\scratchdistance\pairedboxparameter\c!distance
915   \scratchwidth\pairedboxparameter\c!maxwidth\relax
916   \setlocalhsize
917   \hsize\dimexpr\availablehsize-\wd\b_pack_pairedboxes_first-\scratchdistance\relax
918   \hsize\pairedboxparameter\c!width\relax % can be \hsize
919   \scratchdimen\dimexpr\wd\b_pack_pairedboxes_first+\scratchdistance\relax
920   \ifdim\dimexpr\hsize+\scratchdimen\relax>\scratchwidth
921     \hsize\dimexpr\scratchwidth-\scratchdimen\relax
922   \fi}
923
924\def\pack_pairedboxes_between_vertical
925  {\scratchwidth\pairedboxparameter\c!maxwidth\relax
926   \hsize\wd\b_pack_pairedboxes_first
927   \hsize\pairedboxparameter\c!width\relax % can be \hsize
928   \ifdim\hsize>\scratchwidth\relax
929     \hsize\scratchwidth
930   \fi}
931
932\def\pack_pairedboxes_after
933  {\setbox\b_pack_pairedboxes_second\vpack
934     {\ifnum\p_n>\plusone
935        \rigidcolumnbalance\nextbox
936      \else
937        \box\nextbox
938      \fi}%
939   \ifconditional\c_pack_pairedboxes_horizontal
940     \pack_pairedboxes_pack_horizontal
941   \else
942     \pack_pairedboxes_pack_vertical
943   \fi}
944
945\def\pack_pairedboxes_pack_horizontal
946  {\dontleavehmode\hbox\bgroup
947     \forgetall
948     \s_pack_pairedboxes_size\ht
949       \ifdim\ht\b_pack_pairedboxes_first>\ht\b_pack_pairedboxes_second
950         \b_pack_pairedboxes_first
951       \else
952         \b_pack_pairedboxes_second
953       \fi
954     \vsize\s_pack_pairedboxes_size
955     \ifdim\s_pack_pairedboxes_size<\pairedboxparameter\c!height\relax % can be \vsize
956       \s_pack_pairedboxes_size\pairedboxparameter\c!height
957     \fi
958     \ifdim\s_pack_pairedboxes_size>\pairedboxparameter\c!maxheight\relax
959       \s_pack_pairedboxes_size\pairedboxparameter\c!maxheight
960     \fi
961     \pack_pairedbox_valign\b_pack_pairedboxes_first
962     \pack_pairedbox_valign\b_pack_pairedboxes_second
963     \pack_pairedboxes_flush
964   \egroup}
965
966\def\pack_pairedboxes_pack_vertical
967  {\dontleavehmode\vpack\bgroup
968     \forgetall
969     \s_pack_pairedboxes_size\wd
970       \ifdim\wd\b_pack_pairedboxes_first>\wd\b_pack_pairedboxes_second
971         \b_pack_pairedboxes_first
972       \else
973         \b_pack_pairedboxes_second
974       \fi
975     \pack_pairedbox_halign\b_pack_pairedboxes_first
976     \pack_pairedbox_halign\b_pack_pairedboxes_second
977     \s_pack_pairedboxes_size\ht\b_pack_pairedboxes_second
978     \vsize\s_pack_pairedboxes_size
979     \ifdim\ht\b_pack_pairedboxes_second<\pairedboxparameter\c!height\relax % can be \vsize
980       \s_pack_pairedboxes_size\pairedboxparameter\c!height\relax % \relax needed
981     \fi
982     \ifdim\s_pack_pairedboxes_size>\pairedboxparameter\c!maxheight\relax % todo: totale hoogte
983       \s_pack_pairedboxes_size\pairedboxparameter\c!maxheight\relax % \relax needed
984     \fi
985     \ifdim\s_pack_pairedboxes_size>\ht\b_pack_pairedboxes_second
986       \setbox\b_pack_pairedboxes_second\vpack to \s_pack_pairedboxes_size
987         {\pack_pairedboxes_fill_top
988          \box\b_pack_pairedboxes_second
989          \pack_pairedboxes_fill_bottom}% \kern\zeropoint
990     \fi
991     \pack_pairedboxes_flush
992   \egroup}
993
994\def\pack_pairedboxes_inside_second
995  {\forgetall
996   \setupalign[\pairedboxparameter\c!align]%
997   \usepairedboxstyleandcolor\c!style\c!color
998   \tolerantTABLEbreaktrue % hm.
999   \blank[\v!disable]% use fast one
1000   \everypar{\begstrut}} % also flushers here (see bTABLE)
1001
1002\definepairedbox[\v!legend]
1003
1004\permanent\protected\def\placeontopofeachother{\bgroup\dowithnextboxcs\pack_topofeachother_one\hbox}
1005\permanent\protected\def\placesidebyside      {\bgroup\dowithnextboxcs\pack_sidebyside_one    \hbox}
1006
1007\def\pack_topofeachother_one{\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_topofeachother_two\hbox}
1008\def\pack_sidebyside_one    {\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_sidebyside_two    \hbox}
1009
1010\def\pack_topofeachother_two{\setbox\scratchboxtwo\box\nextbox
1011                             \halign{\hss\aligncontent\hss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
1012                             \egroup\egroup}
1013\def\pack_sidebyside_two    {\setbox\scratchboxtwo\box\nextbox
1014                             \valign{\vss\aligncontent\vss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
1015                             \egroup\egroup}
1016
1017\protect \endinput
1018