% varwidth.sty  v 0.9a  Mar 2003  Donald Arseneau asnd@triumf.ca
%
% Copyright 2003 by Donald Arseneau (asnd@triumf.ca).
% This software is released under the terms of the LaTeX Project Public 
% License  (ftp://ctan.tug.org/tex-archive/macros/latex/base/lppl.txt).
% (Essentially: Free to use, copy, distribute (sell) and change, but, if
% changed, the name must be changed.)
%
% The varwidth environment is based on minipage, and takes the same
% parameters, but the specified width is just a maximum value -- the
% environment will be typeset with a narrower "natural" width if
% possible.
%
% In a varwidth environment, paragraph line-breaks are chosen
% according to the specified width, but each line is reset to 
% match a narrower natural width, if there is one.
%
% The \narrowragged command works like \raggedright, but produces 
% generally narrower lines in paragraphs, but more text in the last 
% line (the lines have more-equal lengths).
%
% This version works fine, but there are still many questions about
% how it would work best.  Should there be a version that avoids the
% usual minipage formatting style?
%
% Numbered equations are not handled well, especially with leqno.
% AMSmath environments have not been tried, and undoubtedly fail.
%
% To do:  Extend v-list wrappers to handle all e-TeX primitives.
%         (pdfTeX too?)
%         Capture marks and floats, propagating them out of the box
%         Support numbered equations, including ams math.
% 

\ProvidesPackage{varwidth}[2003/03/10 ver 0.9a; \space 
    Variable-width minipages]

\newcommand\narrowragged{\rightskip \z@ plus .25\hsize 
  \@rightskip\rightskip \parfillskip\z@ plus .15\hsize
  \sloppy }

\newbox\@vwid@box

% The varwidth environment is based on minipage, and takes the same
% parameters, but the specified width is only a limit -- a narrower
% natural width may be used.  \varwidth uses \minipage.

\def\varwidth{\let\@minipagerestore\@vwid@setup \minipage}

% Many things may appear on vertical lists that can't be re-processed,
% so they have to be modified. 

\def\@vwid@setup{%
  % several things can't appear in vertical mode, so they may get 
  % a \vbox wrapped around them.
  \let\@bsphack\@vwid@bsphack % \label and others
  \let\mark\@gobble % Marks disappear in minipages anyway
  \let\@special\@vwid@special % \color and others
  \let\addtocontents\@vwid@addtocontents % \addcontentsline
  % Shifted boxes (\parshape,\hangindent) will have their shifts
  % indicated in a separate box.
  \let\@hangfrom\@vwid@hangfrom % hanging indents
  \let\list\@vwid@list
  \let\endtrivlist\@vwid@endtrivlist
  \postdisplaypenalty\@vwid@posteqp
  \predisplaypenalty\@vwid@preeqp
  \def\@eqnnum{\aftergroup\@vwid@afterva\@@vwid@eqnnum}%
  \global\@vwid@roff\z@  \global\@vwid@loff\z@
  % Begin an inner minipage-like vertical box (in \@tempboxa)
  \let\@minipagerestore\@@vwid@minipagerestore \@minipagerestore
  \setbox\@tempboxa\vbox\bgroup\begingroup
  % Flag the top of the list
  \penalty\@vwid@toppen
}

\let\@@vwid@minipagerestore\@minipagerestore

%  At end of varwidth environment.
\def\endvarwidth{\par\@@par
   % Handle minipage-style notes.
   \ifvoid\@mpfootins\else
     \vskip\skip\@mpfootins
     \normalcolor
     \@vwid@wrap\footnoterule
     \unvbox\@mpfootins
   \fi
   \unskip
   \endgroup\egroup % got my \@tempboxa
%   {\showoutput\showbox\@tempboxa}%
   % in a discarded box, sift through list measuring max width.
   \begingroup\setbox\z@\vbox\bgroup
%\message{-------------------------------------------------------------}%
%\message{First pass; hsize=\the\hsize...  }%{\tracingall\showlists}%%
     \unvcopy\@tempboxa
     \@tempdima-\maxdimen
     \let\@vwid@resetb\@vwid@measure
     \let\@vwid@append\relax
     \sift@deathcycles\z@
     \@vwid@sift
     \xdef\@vwid@{\the\@tempdima}%
   \egroup\endgroup
   % Done measuring.  Now empty \@tempboxa onto current vertical list
   % which is the contents of a minipage environment
%\message{Got natural width \@vwid@. }%
   \unvbox\@tempboxa
   % If the natural width is narrower, then go back through the list
   % reboxing and moving everything into \@vwid@box; then spill \@vwid@box
   \ifdim\@vwid@<\hsize
     \hsize\@vwid@
     \setbox\@vwid@box\vbox{}%
     \sift@deathcycles\z@
%\message{----------------------------------------------------------------}%
%\message{Second pass; hsize=\the\hsize...  }%{\tracingall\showlists}%
     \@vwid@sift
     \unvbox\@vwid@box
   \fi
   % end the minipage environment
   \endminipage}

%
% Here are definitions for sifting through the vertical list, either
% measuring things or reboxing them.  
%
% Penalties used as signals to the vertical-list processor:

\mathchardef\@vwid@posteqp  17321 % Penalty below equations
\mathchardef\@vwid@preeqp   17322 % Penalty above equations
\mathchardef\@vwid@postnump 17323 % Penalty below numbered equations
\mathchardef\@vwid@toppen   17324 % Penalty marking top of vertical list
\mathchardef\@vwid@offsets  17325 % Penalty below special h-offsets box
\mathchardef\@vwid@postw    17326 % Penalty below a \vbox-wrapped object

\newcount\sift@deathcycles

\def\@vwid@sift{%
  \skip@\lastskip\unskip
  \dimen@\lastkern\unkern
  \count@\lastpenalty\unpenalty
  \setbox\z@\lastbox
%{\showoutput\showbox\z@}%
  \ifvoid\z@ \advance\sift@deathcycles\@ne \else \sift@deathcycles\z@ \fi
  \ifnum\sift@deathcycles>33 
    \let\@vwid@sift\relax
    \PackageWarning{varwidth}{Failed to reprocess entire contents}%
  \fi
%\message{\the\sift@deathcycles: skip \the\skip@; kern \the\dimen@; penalty \the\count@. }%
%\ifhbox\z@\setbox99\hbox to0pt{\unhcopy\z@}\fi  % = message
  \ifnum\count@=\@vwid@preeqp \@vwid@eqmodefalse\fi
%\ifnum\count@=\@vwid@preeqp \message{End equation mode. }\fi
  \ifnum\count@=\@vwid@posteqp \@vwid@eqmodetrue\fi
%\ifnum\count@=\@vwid@posteqp\message{Begin equation mode. }\fi
%\if@vwid@eqmode {\showoutput\showbox\z@}\fi
  \ifnum\count@=\@vwid@toppen % finished
    \let\@vwid@sift\relax
  \else\ifnum\count@=\@vwid@offsets
    \@vwid@setoffsets
  \else
    \ifnum\count@=\@vwid@postw
    \else
      \@vwid@resetb % reset box \z@ or measure it
    \fi
    \@vwid@append
  \fi\fi
  \@vwid@sift}

\def\@vwid@setoffsets{%
 \setbox\z@=\hbox{\unhbox\z@
  \global\@vwid@roff\lastkern\unkern
  \global\@vwid@loff\lastkern\unkern}%
%\message{Set offsets to \the\@vwid@loff,  \the\@vwid@roff. }%
}

\def\@vwid@append{% Append contents of box \z@ and glue to \@vwid@box
  \setbox\@vwid@box\vbox{%
    \unvbox\z@
    \ifdim\dimen@=\z@\else \kern\dimen@ \fi
    \vskip\skip@
    \unvbox\@vwid@box
  }%{\tracingall\showbox\@vwid@box}%
}

%  reset box \z@ to \hsize, applying shifts, and wrap in vbox
%  Don't worry about numbered equations because we won't get
%  here if there are any.
\def\@vwid@resetb{%
  \setbox\z@\vbox\bgroup
    \ifvoid\z@
    \else
       \ifvbox\z@
         \box\z@
       \else % \hbox
         \@tempdima\hsize
         \advance\@tempdima-\@vwid@roff
         \advance\@tempdima-\@vwid@loff
         \advance\@tempdima-\p@
         \ifdim\wd\z@>\@tempdima % full-width line; rebox it
%\message{An ordinary line or alignment. }%
            \hbox to\hsize
              {\kern\@vwid@loff \unhbox\z@ \kern\@vwid@roff}%
         \else % an equation or direct \hbox
           \if@vwid@eqmode % re-center unnumbered equations
%\message{A centered equation hsize=\the\hsize. }%
              \hbox to\hsize
                {\hskip\@vwid@loff\@plus1fil
                 \unhbox\z@ \hskip\@vwid@roff\@plus1fil}%
           \else % plain narrow \hbox; leave it as-is
              \box\z@
    \fi\fi\fi\fi
  \egroup}

\def\@vwid@measure{%
  \ifvoid\z@
  \else
    % numbered equations not part of alignments can't be reset,
    % so force retention of full width.
    \ifnum\count@=\@vwid@postnump \ifdim\wd\z@<\linewidth
      \ifdim\@tempdima<\linewidth \@tempdima\linewidth \fi
    \fi\fi
    \ifhbox\z@
      \setbox\z@=\hbox
        {\kern\@vwid@loff \unhbox\z@ \kern\@vwid@roff}%
    \fi
    \ifdim\wd\z@>\@tempdima \@tempdima\wd\z@ \fi
  \fi}

\newdimen\@vwid@loff
\newdimen\@vwid@roff

\let\@@bsphack\@bsphack
\let\@@esphack\@esphack
\let\@@esphack\@Esphack

\def\@vwid@bsphack{\@@bsphack
  \ifx\@vwid@wrap\@firstofone
    \bgroup
  \else
    \ifvmode
      \setbox\@vwid@box \vbox\bgroup \vbox\bgroup
      \let\@vwid@wrap\@firstofone
      \def\@esphack{\@vwid@esphack\@@esphack}%
      \def\@Esphack{\@vwid@esphack\@@Esphack}%
    \fi
  \fi}

\def\@vwid@esphack{\egroup
  \ifx\@vwid@wrap\@firstofone\else
    \egroup % end outer box
    \unvbox\@vwid@box % put inner box on list without lineskip
    \penalty\@vwid@postw
  \fi}

% \vbox Wrapper for misc vlist items
\long\def\@vwid@wrap{\relax
  \ifvmode\expandafter\@vwid@dowrap \else \expandafter\@firstofone \fi}
\long\def\@vwid@dowrap#1{%
  \setbox\@vwid@box \vbox{\vbox{\let\@vwid@wrap\@firstofone
    #1}\penalty\@vwid@postw
  }\unvbox\@vwid@box }

\let\@@vwid@special\special
\let\@@vwid@addtocontents\addtocontents
\let\@@vwid@list\list
\let\@@vwid@endtrivlist\endtrivlist
\let\@@vwid@eqnnum\@eqnnum

\long\def\@vwid@special#1{\@vwid@wrap{\@@vwid@special{#1}}}
\long\def\@vwid@addtocontents#1#2{\@vwid@wrap{\@@vwid@addtocontents{#1}{#2}}}

\long\def\@vwid@hangfrom#1{\par
  \setbox\@tempboxa\hbox{{#1}}%
  \setbox\@vwid@box \vbox{\hbox{\kern\z@ \kern\z@
   }\penalty\@vwid@offsets}\unvbox\@vwid@box
  \def\par{\relax\ifhmode\unskip\fi
    \vadjust{\hbox{\kern\hangindent\kern\z@}\penalty\@vwid@offsets}%
    \@restorepar\par}%
  \hangindent \wd\@tempboxa\noindent\box\@tempboxa}

\def\@vwid@list{\@vwid@setlist\@@vwid@list}
\def\@vwid@endtrivlist{\@vwid@setlist\@@vwid@endtrivlist}

\def\@vwid@setlist{\relax\ifhmode \unskip\expandafter\vadjust\fi
  {\setbox\@vwid@box \vbox{\hbox{%
    \advance\hsize-\linewidth \advance\hsize-\@totalleftmargin
    \kern\@totalleftmargin  \kern\hsize}%
    \penalty\@vwid@offsets}%
   \unvbox\@vwid@box}}

\newif\if@vwid@eqmode

\def\@vwid@afterva{\vadjust{\penalty\@vwid@postnump}}

%  Should I do this? ...

\@ifundefined{newcolumntype}{}{%
 \@ifundefined{NC@rewrite@V}{
 \newcolumntype{V}[1]{%
   >{\begin{varwidth}[t]{#1}\narrowragged\let\\\tabularnewline}%
   l%
   <{\@finalstrut\@arstrutbox\end{varwidth}}}
 }{}
}

