In my quest for a better solution for How to change appearance of a particular line when using \lstincludelisting
, I came across the linebackground=
option Martin Scharrer added to the listings package in his answer to Creating a zebra effect using listings.
The basic idea is to have a macro
% \btLstHL<overlay spec>{range list}
\newcommand<>{\btLstHL}[1]{%
\only#2{\btIfInRange{\value{lstnumber}}{#1}{\color{blue!30}}{}}%
}%
that expands to a \color{blue!30}
if the current line fits into the range or to \empty
(?) otherwise. This is then used together with linebackground
\begin{lstlisting}[language=C, gobble=6,linebackgroundcolor={%
\btLstHL{4}%
\btLstHL<1>{1-2,5-6}%
\btLstHL<2>{7}%
}]
/**
* Prints Hello World.
**/
#include <stdio.h>
int main(void) {
printf("Hello World!");
return 0;
}
\end{lstlisting}
This, however, does not work as intended, as all nonselected lines now get a white background:
The culprit, I guess, is in the implementation of lstlinebackground
and how btLstHL
gets expanded in its processing.:
\lst@Key{linebackgroundcolor}{}{%
\def\lst@linebgrdcolor{#1}%
}
\newcommand{\lst@linebgrd}{%
\ifx\lst@linebgrdcolor\empty\else % PROBLEM: for \btLstHL does not expand to \empty ???
\rlap{%
\lst@basicstyle
\color{-.}% By default use the opposite (`-`) of the current color (`.`) as background
\lst@linebgrdcolor{%
\kern-\dimexpr\lst@linebgrdsep\relax%
\lst@linebgrdcmd{\lst@linebgrdwidth}{\lst@linebgrdheight}{\lst@linebgrddepth}%
}%
}%
\fi
}
Apparently, \btLstHL
expands to "nothing", but not \empty
for nonselected lines, which causes \lst@linebgrd
to highlight the background, but with its default color, which is the opposite of the current text color (black).
And at this point I am lost :-(
So how to define \btLstHL
that it expands to \empty
if the line is not within the given range?
Complete MWE:
\documentclass{beamer}
% beamer class setup
\usecolortheme{rose}
\setbeamertemplate{navigation symbols}{}
% we use UTF8
\usepackage[utf8]{inputenc}
% font setup
\usepackage[T1]{fontenc}
\usepackage[scaled=0.85]{beramono}
\usepackage{xcolor,listings, pgffor}
\makeatletter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \btIfInRange{number}{range list}{TRUE}{FALSE}
%
% Test if int number <number> is element of a (comma separated) list of ranges
% (such as: {1,3-5,7,10-12,14}) and processes <TRUE> or <FALSE> respectively
\newcount\bt@rangea
\newcount\bt@rangeb
\newcommand\btIfInRange[2]{%
\global\let\bt@inrange\@secondoftwo%
\edef\bt@rangelist{#2}%
\foreach \range in \bt@rangelist {%
\afterassignment\bt@getrangeb%
\bt@rangea=0\range\relax%
\pgfmathtruncatemacro\result{ ( #1 >= \bt@rangea) && (#1 <= \bt@rangeb) }%
\ifnum\result=1\relax%
\breakforeach%
\global\let\bt@inrange\@firstoftwo%
\fi%
}%
\bt@inrange%
}
\newcommand\bt@getrangeb{%
\@ifnextchar\relax%
{\bt@rangeb=\bt@rangea}%
{\@getrangeb}%
}
\def\@getrangeb-#1\relax{%
\ifx\relax#1\relax%
\bt@rangeb=100000% \maxdimen is too large for pgfmath
\else%
\bt@rangeb=#1\relax%
\fi%
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \btLstHL<overlay spec>{range list}
%
\newcommand<>{\btLstHL}[1]{%
\only#2{\btIfInRange{\value{lstnumber}}{#1}{\color{blue!30}}{}}%
}%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \btInputEmph<overlay spec>[listing options]{range list}{file name}
%
\newcommand<>{\btLstInputEmph}[3][\empty]{%
\only#4{%
\lstset{linebackgroundcolor=\btLstHL{#2}}%
\lstinputlisting{#3}%
}% \only
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% lstlinebgrd.sty
%% see: https://tex.stackexchange.com/questions/18969/creating-a-zebra-effect-using-listings/18989#18989
%%
%% This small package is not yet published/not commonly available.
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Patch line number key to call line background macro
\lst@Key{numbers}{none}{%
\def\lst@PlaceNumber{\lst@linebgrd}%
\lstKV@SwitchCases{#1}%
{none&\\%
left&\def\lst@PlaceNumber{\llap{\normalfont
\lst@numberstyle{\thelstnumber}\kern\lst@numbersep}\lst@linebgrd}\\%
right&\def\lst@PlaceNumber{\rlap{\normalfont
\kern\linewidth \kern\lst@numbersep
\lst@numberstyle{\thelstnumber}}\lst@linebgrd}%
}{\PackageError{Listings}{Numbers #1 unknown}\@ehc}}
% New keys
\lst@Key{linebackgroundcolor}{}{%
\def\lst@linebgrdcolor{#1}%
}
\lst@Key{linebackgroundsep}{0pt}{%
\def\lst@linebgrdsep{#1}%
}
\lst@Key{linebackgroundwidth}{\linewidth}{%
\def\lst@linebgrdwidth{#1}%
}
\lst@Key{linebackgroundheight}{\ht\strutbox}{%
\def\lst@linebgrdheight{#1}%
}
\lst@Key{linebackgrounddepth}{\dp\strutbox}{%
\def\lst@linebgrddepth{#1}%
}
\lst@Key{linebackgroundcmd}{\color@block}{%
\def\lst@linebgrdcmd{#1}%
}
% Line Background macro
\newcommand{\lst@linebgrd}{%
\ifx\lst@linebgrdcolor\empty\else
\rlap{%
\lst@basicstyle
\color{-.}% By default use the opposite (`-`) of the current color (`.`) as background
\lst@linebgrdcolor{%
\kern-\dimexpr\lst@linebgrdsep\relax%
\lst@linebgrdcmd{\lst@linebgrdwidth}{\lst@linebgrdheight}{\lst@linebgrddepth}%
}%
}%
\fi
}
\makeatother
\begin{document}
\begin{frame}[fragile]{Problem}
\begin{alertblock}{A cool app; unfortunately it has a white background}
\begin{lstlisting}[language=C, gobble=6,linebackgroundcolor={%
\btLstHL{4}%
\btLstHL<1>{1-2,5-6}%
\btLstHL<2>{7}%
}]
/**
* Prints Hello World.
**/
#include <stdio.h>
int main(void) {
printf("Hello World!");
return 0;
}
\end{lstlisting}
\end{alertblock}
\end{frame}
\end{document}
\btLstHL
to expand to\empty
. If you change your MWE to use\empty
as the linebackgroundcolor you get the same problem with white lines. To make it expand to \empty, you would only need to place that in the 4th argument, instead of leaving it actually empty as it is now.\btIfInRange
(among them{}
,{\empty}
,\empty
) without any success. On the other hand, if one writesbackgroundcolor={}
this works as expected (that is, the\ifx\lst@linebgrdcolor\empty
in\lst@linebgrd
takes the if-branch), so I concluded that{}
vs.\empty
is not the root of the issue here. I assume that\ifx
does not really expand its argument, but merrily checks for some kind of equivalence or so :-(\ifx
goes to the true branch if and only if Both arguments are macros AND their first level expansion is identical AND they have the same status with respect to \long and \outer. Source. The first level expansion is most likely where it goes wrong. Not sure how to remedy that, yet.\show\lst@linebgrdcolor%
right above theifx
test in the definition of\lst@linebgrd
. Also, the true branch of your conditional doesn't have a token. Not sure if that's legal or not.