% \begin{meta-comment} % % $Id: mdwlist.dtx,v 1.1 1996/11/19 20:52:26 mdw Exp $ % % Various list-related things % % (c) 1996 Mark Wooding % %----- Revision history ----------------------------------------------------- % % $Log: mdwlist.dtx,v $ % Revision 1.1 1996/11/19 20:52:26 mdw % Initial revision % % % \end{meta-comment} % % \begin{meta-comment} <general public licence> %% %% mdwlist package -- various list-related things %% Copyright (c) 1996 Mark Wooding %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; either version 2 of the License, or %% (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. %% % \end{meta-comment} % % \begin{meta-comment} <Package preambles> %<+package>\NeedsTeXFormat{LaTeX2e} %<+package>\ProvidesPackage{mdwlist} %<+package> [1996/05/02 1.1 Various list-related things] % \end{meta-comment} % % \CheckSum{179} %% \CharacterTable %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z %% Digits \0\1\2\3\4\5\6\7\8\9 %% Exclamation \! Double quote \" Hash (number) \# %% Dollar \$ Percent \% Ampersand \& %% Acute accent \' Left paren \( Right paren \) %% Asterisk \* Plus \+ Comma \, %% Minus \- Point \. Solidus \/ %% Colon \: Semicolon \; Less than \< %% Equals \= Greater than \> Question mark \? %% Commercial at \@ Left bracket \[ Backslash \\ %% Right bracket \] Circumflex \^ Underscore \_ %% Grave accent \` Left brace \{ Vertical bar \| %% Right brace \} Tilde \~} %% % % \begin{meta-comment} % %<*driver> \input{mdwtools} \describespackage{mdwlist} \def\defaultdesc{% \desclabelwidth{80pt}% \desclabelstyle\nextlinelabel% \def\makelabel{\bfseries}% } \newenvironment{cmdlist} {\basedescript{\let\makelabel\cmd}} {\endbasedescript} \mdwdoc %</driver> % % \end{meta-comment} % % \section{User guide} % % This package provides some vaguely useful list-related commands and % environments: % \begin{itemize*} % \item A way of building \env{description}-like environments. % \item Commands for making `compacted' versions of list environments % \item A method for suspending and resuming enumerated lists. % \end{itemize*} % % \subsection{Description list handling} % % Different sorts of description-type lists require different sorts of % formatting: I think that's fairly obvious. There are essentially three % different attributes which should be changable: % \begin{itemize*} % \item the indentation of the items being described, % \item the handling of labels which don't fit properly, and % \item the style used to typeset the label text. % \end{itemize*} % The first two items should usually be decided for all description-like % lists in the document, to ensure consistency of appearance. The last % depends much more on the content of the labels. % % \DescribeEnv{basedescript} % The \env{basedescript} environment acts as a `skeleton' for description % environments. It takes one argument, which contains declarations to % be performed while constructing the list. I'd consider it unusual for % the \env{basedescript} environment to be used in the main text: it's % intended to be used to build other environments. % % The declarations which can be used to define description-type environments % include all of those which are allowed when setting up a list (see the % \LaTeX\ book for information here). Some others, which apply specifically % to description lists, are also provided: % % \begin{itemize} % % \item \DescribeMacro{\desclabelwidth} % The \syntax{"\\desclabelwidth{"<length>"}"} declaration sets labels % to be left-aligned, with a standard width of \<length>; the item % text is indented by \<length> plus the value of |\labelsep|. % % \item \DescribeMacro{\desclabelstyle} % The label style determines how overlong labels are typeset. A style % may be set using the \syntax{"\\desclabelstyle{"<style>"}"} % declaration. The following \<style>s are provided: % \begin{cmdlist} % \item [\nextlinelabel] If the label is too wide to fit next to the % first line of text, then it is placed on a line by itself; % the main text is started on the next line with the usual % indentation. % \item [\multilinelabel] The label is typeset in a parbox with the % appropriate width; if it won't fit on one line, then the % text will be split onto subsequent lines. % \item [\pushlabel] If the label is too wide to fit in the space % allocated to it, the start of the item's text will be `pushed' % over to the right to provide space for the label. This is % the standard \LaTeX\ \env{description} behaviour. % \end{cmdlist} % % \item \DescribeMacro{\makelabel} % The |\makelabel| command is responsible for typesetting a label. % It is given one argument, which is the text given as an argument % to the |\item| command; it should typeset it appropriately. The % text will then be arranged appropriately according to the chosen % label style. This command should be redefined using |\renewcommand|. % % \end{itemize} % % \begin{figure} % \begin{demo}[w]{Various labelling styles} %\begin{basedescript}{\desclabelstyle{\nextlinelabel}} %\item [Short label] This is a short item, although it has quite a % lot of text attached to it. %\item [Slightly longer label text] This is a rather longer piece % of text, with a correspondingly slightly longer label. %\end{basedescript} %\medskip %\begin{basedescript}{\desclabelstyle{\multilinelabel}} %\item [Short label] This is a short item, although it has quite a % lot of text attached to it. %\item [Slightly longer label text] This is a rather longer piece % of text, with a correspondingly slightly longer label. %\end{basedescript} %\medskip %\begin{basedescript}{\desclabelstyle{\pushlabel}} %\item [Short label] This is a short item, although it has quite a % lot of text attached to it. %\item [Slightly longer label text] This is a rather longer piece % of text, with a correspondingly slightly longer label. %\end{basedescript} % \end{demo} % \end{figure} % % \DescribeMacro{\defaultdesc} % To allow document designers to control the global appearance of description % lists, the |\defaultdesc| command may be redefined; it is called while % setting up a new \env{basedescript} list, before performing the user's % declarations. By default, it attempts to emulate the standard \LaTeX\ % \env{description} environment:\footnote{^^A % This is a slightly sanitised version of the real definition, which is % given in the implementation section of this document.} % \begin{listing} %\providecommand{\defaultdesc}{% % \desclabelstyle{\pushlabel}% % \renewcommand{\makelabel}[1]{\bfseries##1}% % \setlength{\labelwidth}{0pt}% %} % \end{listing} % Unfortunately, \LaTeX\ doesn't provide a means for overriding a command % which may or may not have been defined yet; in this case, I'd probably % recommend using the \TeX\ primitive |\def| to redefine |\defaultdesc|. % % If you want to redefine the \env{description} environment in terms of % the commands in this package, the following method is recommended: % \begin{listing} %\renewenvironment{description}{% % \begin{basedescript}{% % \renewcommand{\makelabel}[1]{\bfseries##1}% % }% %}{% % \end{basedescript}% %} % \end{listing} % This ensures that labels are typeset in bold, as is usual, but other % properties of the list are determined by the overall document style. % % \subsection{Compacted lists} % % \LaTeX\ tends to leave a certain amount of vertical space between list % items. While this is normally correct for lists in which the items are % several lines long, it tends to look odd if all or almost all the items % are only one line long. % % \DescribeMacro{\makecompactlist} % The command % \syntax{"\\makecompactlist{"<new-env-name>"}{"<old-env-name>"}"} % defines a new environment \<new-env-name> to be a `compacted' version of % the existing environment \<old-env-name>; i.e., the two environments are % the same except that the compacted version leaves no space between items % or paragraphs within the list. % % \DescribeEnv{itemize*} % \DescribeEnv{enumerate*} % \DescribeEnv{description*} % So that the most common cases are already handled, the package creates % compacted $*$-variants of the \env{itemize}, \env{enumerate} and % \env{description} environments. These were created using the commands % \begin{listing} %\makecompactlist{itemize*}{itemize} %\makecompactlist{enumerate*}{enumerate} %\makecompactlist{description*}{description} % \end{listing} % % Some list environments accept arguments. You can pass an argument to a % list environment using an optional argument to its compact variant. For % example, % \begin{listing} %\begin{foolist*}[{someargument}] % \end{listing} % % \subsection{Suspending and resuming list environments} % % \DescribeMacro{\suspend} % \DescribeMacro{\resume} % The |\suspend| and |\resume| commands allow you to temporarily end a list % environment and then pick it up where you left off. The syntax is fairly % simple: % % \begin{grammar} % % <suspend-cmd> ::= \[[ % "\\suspend" % \begin{stack} \\ "[" <name> "]" \end{stack} "{" <env-name> "}" % \]]% % % <resume-cmd> ::= \[[ % "\\resume" % \begin{stack} \\ "[" <name> "]" \end{stack} "{" <env-name> "}" % \begin{stack} \\ "[" <text> "]" \end{stack} % \]]% % % \end{grammar} % % The \<env-name> is the name of the environment; this will more often than % not be the \env{enumerate} environment. The \<name> is a magic name you % can use to identify the suspended environment; if you don't specify this, % the environment name is used instead. % % \begin{demo}{Suspended environments} %Here's some initial text. It's %not very interesting. %\begin{enumerate*} %\item This is an item. %\item This is another. %\suspend{enumerate*} %Some more commentry text. %\resume{enumerate*} %\item Another item. %\end{enumerate*} % \end{demo} % % You can pass arguments to a resumed list environment through the second % optional argument of the |\resume| command. If, for example, you're using % David Carlisle's \package{enumerate} package, you could say something like % \begin{listing} %\begin{enumerate}[\bfseries{Item} i] %\item An item %\item Another item %\suspend{enumerate} %Some intervening text. %\resume{enumerate}[{[\bfseries{Item} i]}] %\item Yet another item %\end{enumerate} % \end{listing} % % \implementation % % \section{Implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \subsection{Description lists} % % \subsubsection{Label styles} % % \begin{macro}{\nextlinelabel} % % The idea here is that if the label is too long to fit in its box, we put % it on its own line and start the text of the item on the next. I've % used |\sbox| here to capture colour changes properly, even though I have % deep moral objections to the use of \LaTeX\ boxing commands. Anyway, % I capture the text in box~0 and compare its width to the amount of space % I have in the label box. If there's enough, I can just unbox the box; % otherwise I build a vbox containing the label text and an empty hbox -- % |\baselineskip| glue inserted between the two boxes makes sure we get % the correct spacing between the two lines, and the vboxness of the vbox % ensures that the baseline of my strange thing is the baseline of the % \emph{bottom} box. I then bash the vbox on the nose, so as to make its % width zero, and leave that as the result. Either way, I then add glue % to left align whatever it is I've created. % % \begin{macrocode} \def\nextlinelabel#1{% \sbox\z@{#1}% \ifdim\wd\z@>\labelwidth% \setbox\z@\vbox{\box\z@\hbox{}}% \wd\z@\z@% \box\z@% \else% \unhbox\z@% \fi% \hfil% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\multilinelabel} % % A different idea -- make the label text wrap around onto the next line if % it's too long. This is really easy, actually. I use a parbox to contain % the label text, set to be ragged right, because there won't be enough % space to do proper justification. There's also a funny hskip there -- % this is because \TeX\ only hyphenates things it finds sitting \emph{after} % glue items. The parbox is top-aligned, so the label text and the item % run downwards together. I put the result in box~0, and remove the depth, % so as not to make the top line of the item text look really strange. % % All this leaves a little problem, though: if the item text isn't very long, % the label might go further down the page than the main item, and possibly % collide with the label below. I must confess that I'm not actually sure % how to deal with this possibility, so I just hope it doesn't happen. % % By the way, I don't have moral objections to |\parbox|. % % \begin{macrocode} \def\multilinelabel#1{% \setbox\z@\hbox{% \parbox[t]\labelwidth{\raggedright\hskip\z@skip#1}% }% \dp\z@\z@% \box\z@% \hfil% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\pushlabel} % % Now we implement the old style behaviour -- if the label is too wide, we % just push the first line of the item further over to the right. This % is really very easy indeed -- we just stick some |\hfil| space on the % right hand side (to left align if the label comes up too short). The % `push' behaviour is handled automatically by \LaTeX's item handling. % % \begin{macrocode} \def\pushlabel#1{{#1}\hfil} % \end{macrocode} % % \end{macro} % % \subsubsection{The main environment} % % \begin{macro}{\desclabelstyle} % % This is a declaration intended to be used only in the argument to the % \env{basedescript} environment. It sets the label style for the list. % All we do is take the argument and assign it to a magic control sequence % which \env{basedescript} will understand later. % % \begin{macrocode} \def\desclabelstyle#1{\def\desc@labelstyle{#1}} % \end{macrocode} % % \end{macro} % % \begin{macro}{\desclabelwidth} % % We set the label width and various other bits of information which will % make all the bits of the description line up beautifully. We set % |\labelwidth| to the value we're given (using |\setlength|, so that % people can use the \package{calc} package if they so wish), and make % the |\leftmargin| equal $|\labelwidth|+|\labelsep|$. % % \begin{macrocode} \def\desclabelwidth#1{% \setlength\labelwidth{#1}% \leftmargin\labelwidth% \advance\leftmargin\labelsep% } % \end{macrocode} % % \end{macro} % % \begin{environment}{basedescript} % % This is the new description environment. It does almost everything you % could want from a description environment, I think. The argument is a % collection of declarations to be performed while setting up the list. % % This environment isn't really intended to be used by users -- it's here % so that you can define other description environments in terms of it, % % The environment is defined in two bits -- the `start' bit here simply % starts the list and inserts the user declarations in an appropriate % point, although sensible details will be inerted if the argument was % empty. % % \begin{macrocode} \def\basedescript#1{% % \end{macrocode} % % We must start the list. If the |\item| command's optional argument is % missing, we should just leave a blank space, I think. % % \begin{macrocode} \list{}{% % \end{macrocode} % % So far, so good. Now put in some default declarations. I'll use a % separate macro for this, so that the global appearance of lists can be % configured. % % \begin{macrocode} \defaultdesc% % \end{macrocode} % % Now we do the user's declarations. % % \begin{macrocode} #1% % \end{macrocode} % % Now set up the other parts of the list. We set |\itemindent| so that the % label is up against the current left margin. (The standard version % actually leaves the label hanging to the left of the margin by a % distance of |\labelsep| for a reason I can't quite comprehend -- there's % an |\hspace{\labelsep}| in the standard |\makelabel| to compensate for % this. Strange\dots) % % To make the label start in the right place, the text of the item must % start a distance of $|\labelwidth|+|\labelsep|$ from the (pre-list) left % hand margin; this means that we must set |\itemindent| to be % $|\labelwidth|+|\labelsep|-|\leftmargin|$. Time for some \TeX\ arithmetic. % % \begin{macrocode} \itemindent\labelwidth% \advance\itemindent\labelsep% \advance\itemindent-\leftmargin% % \end{macrocode} % % Now we must set up the label typesetting. We'll take the |\makelabel| % provided by the user, remember it, and then redefine |\makelabel| in % terms of the |\desclabelstyle| and the saved |\makelabel|. % % \begin{macrocode} \let\desc@makelabel\makelabel% \def\makelabel##1{\desc@labelstyle{\desc@makelabel{##1}}}% % \end{macrocode} % % I can't think of anything else which needs doing, so I'll call it a day % there. % % \begin{macrocode} }% } % \end{macrocode} % % Now we define the `end-bit' of the environment. Since all we need to do % is to close the list, we can be ever-so slightly clever and use |\let|. % % \begin{macrocode} \let\endbasedescript\endlist % \end{macrocode} % % Note that with these definitions, the standard \env{description} % environment can be emulated by saying simply: % \begin{listing} %\renewenvironment{description}{% % \begin{basedescript}{}% %}{% % \end{basedescript} %} % \end{listing} % % \end{environment} % % \begin{macro}{\defaultdesc} % % Now to set up the standard description appearance. In the absence % of any other declarations, the label will `push' the text out the way if % the text is too long. The standard |\labelsep| and |\leftmargin| are not % our problem. We typeset the label text in bold by default. Also, % |\labelwidth| is cleared to 0\,pt, because this is what \LaTeX's usual % \env{description} does. % % \begin{macrocode} \providecommand\defaultdesc{% \desclabelstyle\pushlabel% \def\makelabel##1{\bfseries##1}% \labelwidth\z@% } % \end{macrocode} % % \end{macro} % % \subsubsection{An example} % % \begin{environment}{note} % % The \env{note} environment is a simple application of the general % description list shown above. It typesets the label (by default, the % text `\textbf{note}') at the left margin, and the note text indented by % the width of the label. % % The code is simple -- we take the environment's argument (which may have % been omitted), store it in a box (using |\sbox| again, to handle colour % changes correctly), set the label width from the width of the box, and % then create a single item containing the label text. The text of the % environment then appears in exactly the desired place. % % I've not used |\newcommand| here, for the following reasons: % \begin{itemize} % % \item I don't like it much, to be honest. % % \item Until very recently, |\newcommand| only allowed you to define % `long' commands, where new paragraphs were allowed to be started % in command arguments; this removes a useful check which traps % common errors like missing out `|}|' characters. I'd prefer to % be compatible with older \LaTeX s than to use the new |\newcommand| % which provides a $*$-form to work around this restriction. % % \end{itemize} % % \begin{macrocode} \def\note{\@ifnextchar[\note@i{\note@i[Note:]}} \def\note@i[#1]{% \basedescript{% \sbox\z@{\makelabel{#1}}% \desclabelwidth{\wd\z@}% }% \item[\box\z@]% } \let\endnote\endbasedescript % \end{macrocode} % % \end{environment} % % % \subsection{Compacted environments} % % Normal lists tend to have rather too much space between items if all or % most of the item texts are one line or less each. We therefore define % a macro |\makecompactlist| whuch creates `compacted' versions of existing % environments. % % \begin{macro}{\makecompactlist} % % We're given two arguments: the name of the new environment to create, and % the name of the existing list environment to create. % % The first thing to do is to ensure that the environment we're creating is % actually valid (i.e., it doesn't exist already, and it has a sensible % name). We can do this with the internal \LaTeX\ macro |\@ifdefinable|. % % \begin{macrocode} \def\makecompactlist#1#2{% \expandafter\@ifdefinable\csname#1\endcsname% {\makecompactlist@i{#1}{#2}}% } % \end{macrocode} % % We also ought to ensure that the other environment already exists. This % isn't too tricky. We'll steal \LaTeX's error and message for this. % % \begin{macrocode} \def\makecompactlist@i#1#2{% \@ifundefined{#2}{\me@err{Environment `#2' not defined}\@ehc}{}% % \end{macrocode} % % The main work for starting a compact list is done elsewhere. % % \begin{macrocode} \@namedef{#1}{\@compact@list{#2}}% % \end{macrocode} % % Now to define the end of the environment; this isn't terribly difficult. % % \begin{macrocode} \expandafter\let\csname end#1\expandafter\endcsname% \csname end#2\endcsname% % \end{macrocode} % % That's a compacted environment created. Easy, no? % % \begin{macrocode} } % \end{macrocode} % % The general case macro has to try slurping some arguments, calling the % underlying environment, and removing vertical space. % % \begin{macrocode} \def\@compact@list#1{\@testopt{\@compact@list@i{#1}}{}} \def\@compact@list@i#1[#2]{% \@nameuse{#1}#2% \parskip\z@% \itemsep\z@% }% % \end{macrocode} % % \end{macro} % % \begin{environment}{itemize*} % \begin{environment}{enumerate*} % \begin{environment}{description*} % % Let's build some compacted environments now. These are easy now that % we've done all the work above. % % \begin{macrocode} \makecompactlist{itemize*}{itemize} \makecompactlist{enumerate*}{enumerate} \makecompactlist{description*}{description} % \end{macrocode} % % \end{environment} % \end{environment} % \end{environment} % % % \subsection{Suspending and resuming lists} % % This is nowhere near perfect; it relies a lot on the goodwill of the user, % although it seems to work fairly well. % % \begin{macro}{\suspend} % % The only thing that needs saving here is the list counter, whose name % is stored in |\@listctr|. When I get a request to save the counter, I'll % build a macro which will restore it when the environment is restored later. % % The first thing to do is to handle the optional argument. |\@dblarg| will % sort this out, giving me a copy of the mandatory argument if there's no % optional one provided. % % \begin{macrocode} \def\suspend{\@dblarg\suspend@i} % \end{macrocode} % % That's all we need to do here. % % \begin{macrocode} \def\suspend@i[#1]#2{% % \end{macrocode} % % Now I have a little problem; when I |\end| the environment, it will close % off the grouping level, and the counter value will be forgotten. This is % bad. I'll store all my definitions into a macro, and build the |\end| % command into it; that way, everything will be expanded correctly. This % requires the use of |\edef|, which means I must be a little careful. % % \begin{macrocode} \edef\@tempa{% % \end{macrocode} % % The first thing to do is to end the environment. I don't want |\end| % expanded yet, so I'll use |\noexpand|. % % \begin{macrocode} \noexpand\end{#2}% % \end{macrocode} % % Now I must define the `resume' macro. I'll use |\csname| to build the % named identifier into the name, so it won't go wrong (maybe). There's % a little fun here to make the control sequence name but not expand it % here. % % \begin{macrocode} \def\expandafter\noexpand\csname resume.#1\endcsname{% % \end{macrocode} % % The counter name is hidden inside |\@listctr|, so the actual counter is % called `|\csname c@\@listctr\endcsname|'. I'll use |\the| to read its % current value, and assign it to the counter when the macro is used later. % % \begin{macrocode} \csname c@\@listctr\endcsname\the\csname c@\@listctr\endcsname% % \end{macrocode} % % That's all we need to do there. Now close the macros and run them. % % \begin{macrocode} }% }% \@tempa% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\resume} % % Resuming environments is much easier. Since I use |\csname| to build the % name, nothing happens if you try to resume environments which weren't % suspended. I'll trap this and raise an error. Provide an optional % argument for collecting arguments to the target list. % % \begin{macrocode} \def\resume{\@dblarg\resume@i} \def\resume@i[#1]#2{\@testopt{\resume@ii{#1}{#2}}{}} \def\resume@ii#1#2[#3]{% \begin{#2}#3% \@ifundefined{resume.#1}{\ml@err@resume}{\@nameuse{resume.#1}}% } % \end{macrocode} % % \end{macro} % % That's all there is. % % \begin{macrocode} %</package> % \end{macrocode} % % \hfill Mark Wooding, \today % % \Finale % \endinput