# How to define a key=value that has an equivalent boolean value key

Assume you're defining a key=value align for some environment realign that you'll use in the following way:

\usepackage{xkeyval}% http://ctan.org/pkg/xkeyval
\newenvironment{realign}[1][]
{\setkeys{fam}{#1}\myalignment}% \begin{realign}
{}% \end{realign}


You define the align choice key to set \myalignment by using (taken from the xkeyval documentation):

\define@choicekey*{fam}{align}[\val\nr]{left,center,right}{%
\ifcase\nr\relax
\def\myalignment{\raggedright}% align=left
\or
\def\myalignment{\centering}% align=center
\or
\def\myalignment{\raggedleft}% align=right
\fi
}


However, now you have the awesome idea of creating corresponding boolean keys left, center and right such that left|center|right=true is equivalent to align=left|center|right. Is it possible to define the boolean keys inside a macro definition, for example, via something like \newboolkey{left}:

\newcommand{\newboolkey}[1]{
\define@boolkey{fam}[@bool@]{#1}[true]{%
\csname if@bool@#1\endcsname%
\setkeys{fam}{align=#1}%
\fi%
}%
}


This doesn't work, since the argument #1 of \newboolkey doesn't translate properly to the definition of the left key.

Of course, removing \define@boolkey from \newboolkey and explicitly stating the key (instead of #1) works. However, I'm not interested in creating a boolean key in this way (by hand). I'd like to use a macro definition to do this since it should be automated and flexible.

Here's a non-working minimal example:

\documentclass{article}
\usepackage{lipsum}% http://ctan.org/pkg/lipsum
\usepackage{xkeyval}% http://ctan.org/pkg/xkeyval
\newenvironment{realign}[1][]
{\setkeys{fam}{#1}\myalignment}% \begin{realign}
{}% \end{realign}
\makeatletter
\define@choicekey*{fam}{align}[\val\nr]{left,center,right}{%
\ifcase\nr\relax
\def\myalignment{\raggedright}% align=left
\or
\def\myalignment{\centering}% align=center
\or
\def\myalignment{\raggedleft}% align=right
\fi
}
\newcommand{\newboolkey}[1]{
\define@boolkey{fam}[@bool@]{#1}[true]{%
\csname if@bool@#1\endcsname%
\setkeys{fam}{align=#1}%
\fi%
}%
}

\makeatother
\begin{document}
\begin{realign}[align=left]
\lipsum[1]
\end{realign}

\newboolkey{left}% Create a boolean equivalent for align=left
\makeatletter
\if@bool@left\else% Check if key is defined.
\texttt{left} key defined
\fi

\begin{realign}[left]% Doesn't work
\lipsum[1]
\end{realign}
\end{document}

-
You forgot a \myalignment in your definition of realign. (I recommend an other keyval package -- kvoptions ;-) ) – Marco Daniel Apr 27 '12 at 19:49
@MarcoDaniel: I am curious: why do you recommend not using xkeyval? – Gonzalo Medina Apr 27 '12 at 23:56
@GonzaloMedina: The setting of a family and a prefix by \SetupKeyvalOptions are very interesting. The package kvoptions uses kvsetkeys. I have the feeling it is more user friendly. However it doesn't provide \define@choicekey. In my next package I will use l3keys ;-) – Marco Daniel Apr 28 '12 at 8:03

Marco's already posted a version, but this is I think a bit closer to what you were trying, just fixed the #1/##1 confusion.

\documentclass{article}
\usepackage{lipsum}% http://ctan.org/pkg/lipsum
\usepackage{xkeyval}% http://ctan.org/pkg/xkeyval
\newenvironment{realign}[1][]
{\let\myalignment\relax\setkeys{fam}{#1}\myalignment}% \begin{realign}
{}% \end{realign}
\makeatletter
\define@choicekey*{fam}{align}[\val\nr]{left,center,right}{%
\ifcase\nr\relax
\def\myalignment{\raggedright}% align=left
\or
\def\myalignment{\centering}% align=center
\or
\def\myalignment{\raggedleft}% align=right
\fi
}
\newcommand{\newboolkey}[1]{
\define@boolkey{fam}[@bool@]{#1}[true]{%
\csname if##1\endcsname
\setkeys{fam}{align=#1}%
\csname fi\endcsname
}%
}

\makeatother
\begin{document}
\begin{realign}[align=left]
\lipsum[1]
\end{realign}

\newboolkey{left}% Create a boolean equivalent for align=left
\makeatletter
\if@bool@left\else% Check if key is defined.
\texttt{left} key defined
\fi

\begin{realign}[left]% Doesn't work
\lipsum[1]
\end{realign}
\end{document}

-
Nice trick ;-) -- I thought about \iftrue but couldn't use it ;-) So I took \in@ ;-) – Marco Daniel Apr 27 '12 at 19:59
So why is that you cannot combine a \csname if...\endcsname with a regular \fi and have to use \csname fi\endcsname? – Werner Apr 27 '12 at 20:49
@Werner I didn't look at exactly xkeyval was doing here but \csname if...\endcsname ...\fi is "an accident waiting to happen" if it appears within a conditional \ifxxx construct in a false branch or unused ifcase branch it will mess up the nesting as TeX will not see the \csname if... as an if but it will see the \fi so the \fi will end some random outer context rather than the intended inner if test. – David Carlisle Apr 27 '12 at 21:05

You can test the argument of define@boolkey. It's easier to handle:

\newcommand{\newboolkey}[1]{%
\define@boolkey{fam}[@bool@]{#1}[true]{%
\in@{true}{##1}
\ifin@
\setkeys{fam}{align=#1}%
\fi
}%
}


If you want to use your code you can use:

\newcommand{\newboolkey}[1]{
\define@boolkey{fam}[@bool@]{#1}[true]{%
\csname if@bool@#1\endcsname%
\setkeys{fam}{align=#1}%
\csname  fi \endcsname%
}%
}


The complete code which works for me:

\setcounter{errorcontextlines}{999}
\documentclass{article}
\usepackage{lipsum}% http://ctan.org/pkg/lipsum
\usepackage{xkeyval}% http://ctan.org/pkg/xkeyval
\newenvironment{realign}[1][]%
{\setkeys{fam}{#1}\myalignment}% \begin{realign}
{}% \end{realign}
\makeatletter
\let\myalignment\relax
\define@choicekey*{fam}{align}[\val\nr]{left,center,right}{%
\ifcase\nr\relax
\def\myalignment{\raggedright}% align=left
\or
\def\myalignment{\centering}% align=center
\or
\def\myalignment{\raggedleft}% align=right
\fi
}
\newcommand{\newboolkey}[1]{%
\define@boolkey{fam}[@bool@]{#1}[true]{%
\in@{true}{##1}
\ifin@
\setkeys{fam}{align=#1}%
\fi
}%
}

\makeatother
\begin{document}
\begin{realign}[align=center]
\lipsum[1]
\end{realign}

\hrulefill

\newboolkey{left}% Create a boolean equivalent for align=left

\begin{realign}[left]
\lipsum[1]
\end{realign}
\end{document}

-

I saw that you wanted to define more than one boolean key. I decided to use ltxkeys package. I would have used key environment for this task, but here is what you wanted. I would also first have thought of the command \ltxkeys@declarekeys.

\documentclass{article}
\usepackage{lipsum}
\usepackage{ltxkeys}
\makeatletter
\newenvironment{realign}[1][]{%
\let\myalignment\relax
\ltxkeys@setkeys[WN]{fam}{#1}\myalignment
}{}
% 1. The *-form of \ltxkeys@choicekey will convert the user input to lowercase
%    before checking the value against the nominations and executing the callbacks.
% 2. The !-form will fully expand the user input before checking it against
%    the nominations.
% 3. The +-form expects two callbacks (one for when the user input is valid,
%    and the other for when the user input isn't in the nominations).

% \val and \mpcalign hold the same content.
\ltxkeys@choicekey*+![WN]{fam}[mpc]{align}[\val\nr]{%
center/.code=\let\myalignment\centering,
left/.code=\let\myalignment\raggedright,
right/.code=\let\myalignment\raggedleft
}[center]{%
% Callback for when value is correct, ie, in the list {left,right,center}.
}{%
\@latexerr{Invalid value for key 'align'}\@ehd
}
% I thought you meant to define more than one boolean key:
\ltxkeys@boolkeys[WN]{fam}[mpb]{left,right,center}[true]{%
\ifboolFT{mpb\ltxkeys@tkey}{}{%
\cptexpanded{\ltxkeys@setkeys[WN]{fam}{align=\ltxkeys@tkey}}%
}%
}
\let\ltxsetkeys\ltxkeys@setkeys
\makeatother
\begin{document}
\begin{realign}[align=left]
\lipsum[1]
\end{realign}
\begin{realign}[align=right]
\lipsum[1]
\end{realign}
\ltxsetkeys[WN]{fam}{right=true}
\ltxsetkeys[WN]{fam}{left=false}
\def\say#1{%
\cptdocommalist{#1}{%
\endgraf Key \texttt{##1} set
\ifboolTF{mpb##1}{true}{false}.
}%
}
\say{left,right,center}
\end{document}


-