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