# The '.is if' handler of pgfkeys

Let us start with an example from the pgf manual, version 2.10, page 495:

\pgfkeys{/flat world/.is if=theworldisflat}
\pgfkeys{/flat world=false}
\def\x#1{% code-A
\iftheworldisflat
Flat
\else
Round?
\fi
#1%
}


I have two questions concerning this example.

(1) Can I set \newif\iftheworldisflat within the command \pgfkeys or, better still, get \pgfkeys to declare the boolean for me automatically. The latter is what obtains in xkeyval package.

(2) How do I pass a code (eg, code-A above) directly to the key flat world within \pgfkeys without a de tour? Is this possible? Something like the following appears to me to be better, since it allows the callback to be executed whenever the key is set.

\pgfkeys{%
/my family/my keys/.cd,
keya/.is if={<boolean>}{<callback>},
}


(3) Why did pgfkeys use \edef here:

\def\pgfkeys@non@outer@newif@#1#2{%
\expandafter\edef\csname #2true\endcsname
{\noexpand\let\noexpand#1=\noexpand\iftrue}%
\expandafter\edef\csname #2false\endcsname
{\noexpand\let\noexpand#1=\noexpand\iffalse}%
\csname #2false\endcsname
}


EDIT

What I have tried is for something like the following. I am still thinking of how to optimize the code; that's why I am yet to post it:

\pgfkeys{%
/my family/my keys/.cd,
keya/.is if=boola,
keyb/.is if=keyb, % Acceptable, but why repeat 'keyb'?
% Use 'keyc' as the boolean and allow the user to provide a default value.
% No need to provide a boolean or repeat 'keyc' on the right hand side of '='.
keyc/.is bool={true}{\def\y##1{##1-True-#1}}{\def\y##1{##1-False-#1}},
}

-
\edef there is not needed, of course. Change \edef into \def and remove all \noexpand tokens: it will behave exactly the same. At least if \let, \iftrue and \iffalse are the primitives when the code is executed: I don't think they are ever redefined. – egreg Apr 15 '12 at 22:52
@AhmedMusa: That edit changes the question quite considerably. Originally it had the flavour of "How do I do this?" but now it reads "I can do this, what should I be aware of?". The two are very different. I, for one, would not have attempted answering the second! Though I would be (am) very interested in the answer. If you could explain a little more when asking your questions it would be really helpful. I find that I mentally sort questions into "Questions where I might be able to help" and "Questions where I might learn something". If you add a bit more context, it would help a lot. – Loop Space Apr 21 '12 at 16:23
@AndrewStacey: OK, thanks. – Ahmed Musa Apr 21 '12 at 17:24
Good. Now that I've figured you out (in a manner of speaking), I'm looking forward to learning from your questions. – Loop Space Apr 21 '12 at 19:03

Unlike xkeyval, pgfkeys is designed to work not only with LaTeX. In the plain format, \newif is outer, and so it cannot (easily) be used within \pgfkeys. Thus you have to set up your \if... macros outside of keyval set up with pgfkeys.

On the second question, that is not how the .is if handler works. You could of course redefine it (very risky), or more safely create your own handler to do what you want.

On the third part, using \noexpand inside an \edef is one way to get outer control sequences into other macros, something that is usually not allowed. Once again, this is a safety precaution more for plain TeX than LaTeX or ConTeXt.

-
pgfkeys has LaTeX-style \pgfkeys@non@outer@newif. Thanks, I will just define my own new handler. – Ahmed Musa Apr 15 '12 at 19:36
@AhmedMusa But let us know about your handler! – cjorssen Apr 15 '12 at 19:59
I still don't understand why the \edef: it's \newif that's outer, but it's not used there and, for instance, \expandafter\def\csname #1true\endcsname{\let#1=\iftrue} is exactly the same as what's obtained with the \edef. – egreg Apr 15 '12 at 22:49
@egreg But what if #1 is define and is outer? My take is that is why the \edef is used. – Joseph Wright Apr 16 '12 at 5:47
Oh. I see my name here. Did I write that method? I do not remember any particular reason for an \edef. The only thing I remember is that I once stumbled over the \outer problem in some plain-text compatibility problem and fixed it somehow. I remember vaguely that I copy-pasted code from somewhere else. I also know that some portions of pgf use \csname .. \endcsname since such constructions seem to ignore \outer flags. – Christian Feuersänger Apr 21 '12 at 21:53

I've also thought for a long time for a better .is if solution that does not require me to invent a new name every time that I want to create a new boolean option: /my package/fancy option already is quite a nice name, doesn't clash with other packages, and is definitely better than writing \if@mpkg@@fan@opt everywhere. Furthermore, as a nice added plus, you can also define options which make the value of a toggle depend on another toggle (see example bellow).

Building on top of etoolbox toggles, I've implemented my solution as a new .is toggle handler, and created a small package ezkeys which is basically a single file with lots of comments and a bit of code defining a few new pgfkey handlers.

As an example usage, toggles are defined using

\pgfkeys{
my package/.is family,
my package,
numbered/.is toggle,
in toc/.is toggle,
in toc/if numbered/.code = \linktoggle{/my package/in toc}{/my package/numbered}
}


Options are then selected as usual

\pgfkeys{
my package,
numbered = false,
in toc = if numbered % will be true or false depending on numbered
}


And then one can write conditionals such as:

\iftoggle{/my package/numbered}{numbered is true}{numbered is false}
\iftoggle{/my package/in toc}{in toc is true}{in toc is false}

-

Here are my proposed .is if handlers. The code at pgfkeys-patch is required for the following to compile.

\documentclass{article}
\usepackage{pgfkeys}
\usepackage{pgfkeys-patch}
\makeatletter
% Syntaxes:
% <key>.is if with no callback=<default>
% <key>.is if with 1 callback={<default>}{<callback>}
% <key>.is if with 2 callbacks={<default>}{<callback-1>}{<callback-2>}
% In this way I know I am dealing with <key> alone, and not with another
% (auxiliary) boolean as in the conventional '.is if' handler.
\pgfkeys{%
/handlers/.is if with no callback/.code={%
\pgfkeys@boolean@handler{#1}{##1}{}{}%
},
/handlers/.is if with 1 callback/.code 2 args={%
\pgfkeys@boolean@handler{#1}{##1}{#2}{}%
},
/handlers/.is if with one callback/.style 2 args={%
\pgfkeyscurrentpath/.is if with 1 callback={#1}{#2}%
},
/handlers/.is if with 2 callbacks/.code n args={3}{%
\pgfkeys@boolean@handler{#1}{##1}{#2}{#3}%
}
}
\def\pgfkeys@savresname#1{%
\ifcase#1\relax
\let\pgfkeyssavedkey=\pgfkeyscurrentkey
\let\pgfkeyssavedname=\pgfkeyscurrentname
\edef\pgfkeyssavedpath{\pgfkeyscurrentpath}%
\edef\pgfkeyscurrentkey{\pgfkeyscurrentpath}%
\pgfkeys@split@path
\let\pgfkeyssavednameb\pgfkeyscurrentname
\or
\let\pgfkeyscurrentkey=\pgfkeyssavedkey
\let\pgfkeyscurrentname=\pgfkeyssavedname
% Don't do \edef\pgfkeyscurrentpath{\pgfkeyssavedpath} here, since
% \pgfkeyscurrentpath is determined by \pgfkeys@pathtoks.
\else
\pgfkeys@error{Number '#1' out of range [0,1]}%
\fi
}
\def\pgfkeys@boolean@handler#1#2#3#4{%
\pgfkeys@savresname{0}%
% Let's not redefine 'if\pgfkeyscurrentname' if it already exists,
% so that we don't change its current state:
\ifpgfkeyscsdef{if\pgfkeyscurrentname}{}{%
% Can use \outer-defined \newif:
\csname newif\expandafter\endcsname
\csname if\pgfkeyscurrentname\endcsname
}%
\begingroup
\def\reserved@a{\pgfkeyssavedpath/.code=}%
\expandafter\expandafter\expandafter\endgroup\expandafter\expandafter
\expandafter\pgfkeysalso\expandafter\expandafter\expandafter{%
\expandafter\reserved@a\expandafter\pgfkeys@boolean@handler@b
\expandafter{\pgfkeyssavednameb}{#2}{#3}{#4},
\pgfkeyssavedpath/.default=#1%
}%
\pgfkeys@savresname{1}%
}
\def\pgfkeys@boolean@handler@b#1#2#3#4{%
\ifpgfkeysxin{,\pgfkeystrimspace{#2},}{,true,false,}{%
\ifpgfkeyscsdef{#1#2}{%
\csname#1#2\endcsname
\ifpgfkeysblank{#3#4}{}{\ifpgfkeysbool{#1}{#3}{#4}}%
}{%
\pgfkeysvalueof{/errors/boolean expected/.@cmd}%
\pgfkeyscurrentkey{#2}\pgfeov
}%
}{%
\pgfkeysvalueof{/errors/boolean expected/.@cmd}%
\pgfkeyscurrentkey{#2}\pgfeov
}%
}
\makeatother


Examples:

\pgfkeys{%
/my family/.cd,
flatworlda/.is if with no callback=true,
flatworldb/.is if with 1 callback={true}{\def\x##1{##1T#1}},
flatworldc/.is if with 2 callbacks={false}{\def\x##1{##1T#1}}{\def\x##1{##1F#1}},
% pgfkeys package doesn't seem to like the idea of optional argument, otherwise I
% would have preferred something like the following, in which the default
% value 'true' is optional:
% flatworlda/.is if with no callback=[true]
}

\begin{document}
\pgfkeys{/my family/flatworlda=false}
\pgfkeys{/my family/flatworldb=true}
\pgfkeys{/my family/flatworldc=false}
\edef\x{%
%  \ifflatworlda
\ifflatworldb
%  \ifflatworldc
Is flat%
\else
Is round%
\fi
}
\pgfkeys{/my family/flatworldb=true}
\ifflatworldb
true
\else
false
\fi
\end{document}

-