As Andrew remarked in his answer a solution for multicols is rather more complicated as the "current" column is not at all known while doing the typesetting. So a rather complicated approach with multiple typesetting runs (using the .aux
file) is needed.
Below is a first draft of a solution providing \docolaction{left}{middle}{right}
for executing conditional code depending on the column type. If the column type is not yet known the first column is assumed (as default).
\begin{filecontents}{mccolaction.sty}
%
% \begin{macrocode}
\ProvidesPackage{mccolaction}
[2013/05/05 v0.9b column actions for multicolumn formatting (FMi)]
% \end{macrocode}
%
% \begin{macrocode}
\RequirePackage{etoolbox}
\RequirePackage{multicol}[2011/12/20]
% \end{macrocode}
%
% Determining the current column in multicols is difficult because
% (in contrast to the twocolumn mode of standard LaTeX) the
% multicols columns are determined very late in the game and due to
% the balancing routine it is not known where a piece of text is
% going to end up at the time the text is typeset. Only afterwards,
% when everything has be typeset into a single long galley, that
% galley is split into individual columns (at the very end in a
% possibly huge set of trials to balance the column material.
%
% Therefore the approach taken here is to write out a single line
% into the .aux file whenever a column is finally typeset:
%\begin{verbatim}
% \mc@col@status{<number>}
%\end{verbatim}
% The number in the argument denotes the different kind of column: 1
% for left column 2 for any middle column and 3 for the final column.
%
% We only set this up for the LR typesetting case here, something
% similar could be done for the RL version:
% \begin{macrocode}
\patchcmd{\LR@column@boxes}{\box\count@}
{\protected@write\@auxout{}{\string\mc@col@status
{\ifmc@firstcol 1\else 2\fi}}%
\mc@firstcolfalse
\box\count@}
{\typeout{juhu!}}{\typeout{oje!}}%
\patchcmd{\LR@column@boxes}{\box\mult@rightbox}
{\protected@write\@auxout{}{\string\mc@col@status{3}}%
\box\mult@rightbox}%
{\typeout{juhu!}}{\typeout{oje!}}%
\newif\ifmc@firstcol
\mc@firstcoltrue
% \end{macrocode}
%
% Need to reinitiate \verb=\mc@align@columns= as this was let to
% the old definition of \verb=\LR@column@boxes=.
%
% \begin{macrocode}
\LRmulticolcolumns
% Whenever we want to do something that depends on the current
% column we execute \verb=\docolaction=. This command takes one
% optional and three mandatory arguments. The mandatory ones denote
% what to do if this is a ``left'', ``middle'', or ``right'' column
% and the optional one is simply there to say what to do if we don't
% know (default is to use the ``left'' column action in that case).
%
% We use one counter \verb=\mc@col@check@num= to generate us unique
% label names. Each time we execute \verb=\docolaction= we increment
% this counter to get a new name.
% \begin{macrocode}
\newcount\mc@col@check@num
% \end{macrocode}
% The generated ``labels'' are named
% \verb=\mc@col-\the\mc@col@check@num= and they hold as values the
% numbers 1, 2, or 3 denoting the current column type.
% \begin{macrocode}
\newcommand\docolaction[4][1]{%
\global\advance\mc@col@check@num\@ne
\edef\mc@col@type{\expandafter\ifx
\csname mc@col-\the\mc@col@check@num\endcsname\relax
0\else
\csname mc@col-\the\mc@col@check@num\endcsname
\fi}%
% \end{macrocode}
% We prefix with 0 so that an unknown label (that returns
% \verb=\relax=) will result in case 0
% \begin{macrocode}
\ifcase \mc@col@type\relax
% \end{macrocode}
% If column is unknown we use the default action or the action
% denoted by the optional argument (so that arg can take the value
% 1, 2, 3)
% \begin{macrocode}
\ifcase #1\or #2\or#3\or#4\fi % 0 not known use first col as default
\or
% \end{macrocode}
% Otherwise we know (or think we know) that this is a first, middle,
% or last column:
% \begin{macrocode}
#2% % 1 First col
\or
#3% % 2 any middle col
\or
#4% % 3 last col
\else
\ERROR
\fi
% \end{macrocode}
% But how does the column number get associated with our label? We
% do do this by writing another line into the aux file at this point:
% \begin{macrocode}
\edef\next{\write\@auxout
{\string\mc@set@col@status{mc@col-\the\mc@col@check@num}%
{\mc@col@type}}}%
\next
}
% \end{macrocode}
%
% Because of extra data writing to the aux file the aux file will
% now contain something like the following after the document is
% processed the first time:
%\begin{verbatim}
%\relax
%\mc@col@status{1}
%\mc@set@col@status{lcol-1}{0}
%\mc@col@status{2}
%\mc@set@col@status{lcol-2}{0}
%\mc@col@status{3}
%\mc@set@col@status{lcol-3}{0}
%\mc@col@status{1}
%\mc@col@status{2}
%\mc@col@status{3}
%\mc@set@col@status{lcol-4}{0}
%\end{verbatim}
% The \verb=\mc@col@status= line denotes the column type and has been
% writting out just before corresponding the column box was placed
% onto the page.
% The\verb=\mc@set@col@status= lines have been written out as part
% of shipping the column boxes out, e.g.,
% \verb=\mc@set@col@status{lcol-1}{0}= was therefore somewhere within
% the first column as it appears between \verb=\mc@col@status{1}=
% and \verb=\mc@col@status{2}=
% The second argument in that line is the value used in the previous
% run (or zero if there was no previous run. We can use this to
% determine if a rerun is necessary.
%
% Thus with this knowledge we can set things up to get the labels
% working.
%
% When the aux file is read in \verb=\mc@col@status= is used to set
% \verb=\mc@curr@col@status=:
%
% \begin{macrocode}
\def\mc@col@status#1{\gdef\mc@curr@col@status{#1}}
% \end{macrocode}
% And when \verb=\mc@set@col@status= is executed we can simply set
% up the label by associating it with the \verb=\mc@curr@col@status=
% and ignore the second argument:
% \begin{macrocode}
\def\mc@set@col@status#1#2{%
\global\expandafter\let\csname #1\endcsname\mc@curr@col@status}
% \end{macrocode}
%
% The above definition is being used when the \texttt{.aux} file is
% read in at the beginning. At the end we need a different
% definition to test if another typesetting run is needed. There we
% compare the value used in the current run (stored in the second
% argument) with the value used on the next run. If those two values
% differ we set \verb=@tempswa= to false which will trigger the
% ``Label(s) may have changed'' warning.
% \begin{macrocode}
\AtEndDocument{\def\mc@set@col@status#1#2{%
\ifnum #2=\mc@curr@col@status\else
\@tempswatrue
\fi}%
}
% \end{macrocode}
\end{filecontents}
\documentclass{article}
\usepackage{mccolaction}
\usepackage{wrapfig}
\usepackage{lipsum}
% An application of \docolaction. We put the whole wrapfigure into the
% args so that the internal label used is placed after wrapfigure.
\newcommand\pquote[2]{%
\docolaction
{\begin{wrapfigure}[#1]{l}[0.2\columnwidth]{0.4\columnwidth}%
\raggedright\large\bfseries #2\end{wrapfigure}}%
{\begin{wrapfigure}[#1]{l}[0pt]{0.4\columnwidth}%
\raggedright\large\bfseries #2\end{wrapfigure}}%
{\begin{wrapfigure}[#1]{r}[0.2\columnwidth]{0.4\columnwidth}%
\raggedright\large\bfseries #2\end{wrapfigure}}%
\ignorespaces
}
\setlength\columnseprule{.7pt}
\setlength\emergencystretch{2em}
\begin{document}
\begin{multicols}{3}
\lipsum[1]
\pquote{4}{Lorem ipsum!}
\lipsum[2]
\pquote{5}{Dolor sit amet!}
\lipsum[4-5]
\pquote{4}{Mauris ut est.}
\lipsum[6-7]
Only a few words left \ldots
Here the pquote comes in the middle of the paragraph for a change
\pquote{6}{Final test related to the edge}
as we can see. Only a few words left so this drops off the column \ldots
\end{multicols}
\end{document}
If we run this file we get
The second page then shows that not all will be perfect but that is in this case due to the fact that I deliberately placed the \pquote
too close to the end.
If we change the file to use 4 columns we end up with one wrapfigure cut across columns, clearly something where the two packages do not work together well enough to resolve this automatically.
In summary, I would say it works well enough. In combination with something like wrapfigure
it will need some help in places, but this is to be expected as wrapfigure
has no clue about multicols splitting up the columns later on.
Update
I slightly improved the code, so that it now also keeps track of any changes in the column types. If due to that the need for another LaTeX run is detected, it will generate the well-known warning "Label(s) may have changed. Rerun to get cross-references right."
Thus output like the following should only happen if the user ignores that warning.
Update II
The above version of mccolaction
was written against the SVN version which is not on CTAN at the moment. In answer Column break only if within first column of multicol I provided an updated package that works with both versions of multicol
.
Update III
The functionality described above is now available in multicol version 1.8 and above. It is not automatically enabled (as it is costly) --- you have to request it using the option colaction
. Afterwards \docolaction
is enabled for use in your document.