% Copyright 2019 by Christian Feuersaenger
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Public License.
%
% See the file doc/generic/pgf/licenses/LICENSE for more details.
%
% This file contains additions to pgfkeys.code.tex (loaded
% automatically at the end of pgfkeys.code.tex)
%
% Improvements:
%
% - option filtering
%   you provide
%     - a boolean predicate
%     - a "filter handler" which will be invoked for non-matching options,
%     - a key=value list as usual.
%   The filter sets only matching options and invokes the handler for
%   unmatching ones.
%
% - Fast family support as in xkeyval.
%   - A key like /my tree/my option  can be associated with /my family
%   - You can efficiently set keys which belong to a set of "active"
%     families.
%  Remaining options can be collected into a macro.

% WARNING:
% this file overwrites
% \pgfkeys@addpath
% \pgfkeys@nevermind
%
% all other features are 'additive'


% these implementations will be switched dynamically with their
% filtered versions (in \pgfkeys@install@filter@and@invoke)
\let\pgfkeys@orig@case@one=\pgfkeys@case@one
\let\pgfkeys@orig@@set=\pgfkeys@@set
\let\pgfkeys@orig@@qset=\pgfkeys@@qset
\let\pgfkeys@orig@try=\pgfkeys@try
\let\pgfkeys@orig@unknown=\pgfkeys@unknown

\newif\ifpgfkeysfilteringisactive
\newif\ifpgfkeysfiltercontinue
\let\pgfkeys@key@predicate=\pgfkeys@empty
\let\pgfkeys@filtered@handler=\pgfkeys@empty
\newtoks\pgfkeys@tmptoks

% Performs 'filtered' key settings.
%
% For every option for which the path prefixing has already been done,
% the current key filter predicate will be invoked. If the predicate
% returns true, pgfkeys will process this key in the normal manner.
% If not, a key filter handler will be invoked.
%
% The *predicate* and *handler* semantics are as follows:
% PRECONDITION:
%         1. The variables
%             \pgfkeyscurrentkey (full path including name)
%             \pgfkeyscurrentkeyRAW (the current key as it has been
%                                    found in the key=value list
%                                    without path modification)
%             \pgfkeyscurrentvalue
%             are all set. If the current key is a handler variable,
%                 \pgfkeyscurrentname
%             and
%                 \pgfkeyscurrentpath
%             are also set.
%         2. The type of option has already been checked, that means
%             case (1) whether '.@cmd' exists
%             case (2) whether the key as such has its value
%             case (3) it is a handler like '.code', '.cd' or whatever.
%             case (0) it is unknown.
%             The actual case number (0-3) will be provided as contents
%             of the macro
%                 \pgfkeyscasenumber.
%             It is always a one-character token, so it may be compared
%             with both, \ifnum or \if.
%             Please note that unknown options will be processed with
%             the usual '.unknown' handlers unless the key filter takes
%             control over unknown options as well.
%
% POSTCONDITION:
%         the predicate sets
%             \pgfkeysfiltercontinuetrue
%         or
%             \pgfkeysfiltercontinuefalse
%         (the boolean \ifpgfkeysfiltercontinue).
% Depending on this 'if', option processing will be continued or
% skipped.
%
% The handler can do anything with the option, for example collect
% unmatched ones.
%
% ATTENTION: \pgfkeysfiltered can't be nested (yet). Use the
% \pgfkeyspredicateAND if you need multiple predicates at once.
% Nesting would produce unexpected results because the current filter
% state can't be stored/restored without TeX-groups, therefor it is
% disabled.
%
% ATTENTION: you can't filter error messages.
%
% REMARK: In case (3), the macros \pgfkeyscurrentpath and
% \pgfkeyscurrentname have already been computed, you do not need to
% invoke \pgfkeyssplitpath. In any other case, neither path nor name
% are required for the options processing - if you need them for
% predicates/handlers, you need to compute them by hand.
%
%
% Example:
%   \def\unmatched{}
%   \pgfkeys{/pgf/key filters/is descendant of/.install key filter=/my group}
%   \pgfkeys{/pgf/key filter handlers/append filtered to/.install key filter handler=\unmatched}
%   \pgfkeysfiltered%
%       {/my group/option 1=value 1,/tikz/option 2=value 2}
% ->
%  will set '/my group/option 1' as usual, but '/tikz/option 2' will not
%  be set. Instead, it will be appended to '\unmatched' such that
%  \unmatched = {/tikz/option 2=value2}
%  after the operation.
%
% Arguments:
% #1:  key-value list.
\def\pgfkeysfiltered{%
    % produce
    % '{<default path>}'
    % each expanded exactly one time into the register:
    \expandafter\pgfkeysfiltered@@install\expandafter{\pgfkeysdefaultpath}%
}

% #1: old value of default path.
% #2: key-value-list.
\long\def\pgfkeysfiltered@@install#1#2{%
    \pgfkeys@install@filter@and@invoke{%
        \let\pgfkeysdefaultpath\pgfkeys@root%
        \pgfkeys@parse#2,\pgfkeys@mainstop%
        \def\pgfkeysdefaultpath{#1}%
    }%
}

% Assuming that macro #1 contains a key=value list, this command
% performs an \pgfkeysalso command for the content of macro #1.
%
% It can be used in conjunction with
%   - \pgfkeysfiltered and
%   - \pgfkeysappendfilterednamestomacro:
% the first pass fill only process options matching the filter, then,
% a \pgfkeysalsofrom can be used at a later time to set the remaining
% options.
%
% Example:
% \pgfkeys{/my group/.cd,/utils/exec=\pgfkeysalsofrom\filtered}
\long\def\pgfkeysalsofrom#1{%
    \expandafter\pgfkeysalso\expandafter{#1}%
}

% The same as \pgfkeysalsofrom, but it invokes \pgfkeysalsofiltered.
\long\def\pgfkeysalsofilteredfrom#1{%
    \expandafter\pgfkeysalsofiltered\expandafter{#1}%
}

% #1 = options
\long\def\pgfkeysalsofiltered#1{%
    \pgfkeys@install@filter@and@invoke{\pgfkeysalso{#1}}%
}%

% The same like \pgfkeysfiltered, but with quick search path setting.
%
% #1: default path
% #2: key-value-pairs
\long\def\pgfqkeysfiltered#1{%
    \expandafter\pgfqkeysfiltered@@install\expandafter{\pgfkeysdefaultpath}{#1}%
}

% #1: old value of default path.
% #2: default path
% #3: key-value-list.
\long\def\pgfqkeysfiltered@@install#1#2#3{%
    \pgfkeys@install@filter@and@invoke{%
        \def\pgfkeysdefaultpath{#2/}\pgfkeys@parse#3,\pgfkeys@mainstop\def\pgfkeysdefaultpath{#1}%
    }%
}

% Family management
%
% The family code provides the following features:
% 1. every key can be associated with 0 or 1 'family'.
% 2. Families are a loose association which are independent of the key
% hierarchy.
%   For example, /my tree/key1  can belong to family /tikz.
%
% 3. It is possible to "activate" or "deactivate" single families.
%    Furthermore, it is possible to set only keys which belong to
%    active families (using \pgfkeysfiltered).
% 4. Runtime complexities:
%    If you have N options and you only want to process K active
%    families, the runtime is O( N + K ):
%      - activate every family O(K)
%      - use \pgfkeyshasactivefamily as filter predicate O(N)
%      - deactivate every family O(K)


% Activates family #1.
%
% #1 maybe a macro.
\def\pgfkeysactivatefamily#1{%
    \pgfkeysiffamilydefined
        {#1}%
        {\csname pgfk@#1/familyactivetrue\endcsname}%
        {\pgfkeysvalueof{/errors/family unknown/.@cmd}{#1}\pgfeov}%
%\message{[ACTIVATING FAMILY #1]}%
}

% Deactivates family #1.
%
% #1 maybe a macro.
\def\pgfkeysdeactivatefamily#1{%
    \pgfkeysiffamilydefined
        {#1}%
        {\csname pgfk@#1/familyactivefalse\endcsname}%
        {\pgfkeysvalueof{/errors/family unknown/.@cmd}{#1}\pgfeov}%
%\message{[DEACTIVATING FAMILY #1]}%
}

% Activates all families in the comma separated list #1.
%
% It will also generate code for deactivation of all those families
% into the command #2.
%
% #1: a comma-separated list of fully qualified family names.
% #2: a command which will be filled with a deactivate-all command.
\def\pgfkeysactivatefamilies#1#2{%
    \pgfkeyssavekeyfilterstateto\pgfkeys@cur@state
    \expandafter\pgfkeysactivatefamilies@impl\expandafter{\pgfkeys@cur@state}{#1}{#2}%
}
% #1: commands needed to restore the old filtering state
% #2: family name list
% #3: macro name for de-activate command
\def\pgfkeysactivatefamilies@impl#1#2#3{%
    \pgfkeysinstallkeyfilter{/pgf/key filters/false}{}%
    \let#3=\pgfkeys@empty%
    \def\pgfkeys@filtered@handler{\pgfkeys@family@activate@handler{#3}}%
    \pgfkeysalsofiltered{#2}%
    #1%
}

\def\pgfkeys@family@activate@handler#1{%
    \pgfkeysactivatefamily{\pgfkeyscurrentkey}%
    % produce
    %   <old list> '\pgfkeysdeactivatefamily{' <current key> '}'
    \pgfkeys@tmptoks=\expandafter\expandafter\expandafter{\expandafter#1\expandafter\pgfkeysdeactivatefamily\expandafter{\pgfkeyscurrentkey}}%
    \edef#1{\the\pgfkeys@tmptoks}%
}

% If for testing whether a family exists.
%
% #1 = fully qualified family name
% #2 = if-case
% #3 = else-case
%
% Description:
%
% If the family exists, #2 will be executed. Otherwise, #3 will be
% called.
\long\def\pgfkeysiffamilydefined#1#2#3{\ifcsname ifpgfk@#1/familyactive\endcsname#2\else#3\fi}

% Sets the TeX boolean
%   \ifpgfkeysfiltercontinue := ( family #1 is active )
%
% Argument:
% #1 the family name. Maybe a macro.
\def\pgfkeysisfamilyactive#1{%
    \pgfkeysiffamilydefined{#1}{%
        \expandafter\let\expandafter\ifpgfkeysfiltercontinue\csname ifpgfk@#1/familyactive\endcsname
    }{%
        \pgfkeysvalueof{/errors/family unknown/.@cmd}{#1}\pgfeov%
        \expandafter\expandafter\expandafter\let\csname ifpgfkeysfiltercontinue\endcsname\csname iffalse\endcsname
    }%
}%

% Retrieve the family of full key #1 into macro #2.
%
% Will set the TeX boolean \ifpgfkeyssuccess to whether the full key
% really has a family.
%
% The family for any key is stored in the sub-key #1/family.
%
% Parameters:
% #1:  the full key name for which the family is requested. Maybe a
%      macro.
% #2:  a macro name which will be filled with the result.
\def\pgfkeysgetfamily#1#2{%
    \pgfkeysifdefined{#1/family}{\pgfkeysgetvalue{#1/family}{#2}\pgfkeyssuccesstrue}{\pgfkeyssuccessfalse}%
}

% Equivalent to \pgfkeys{#1/.belongs to family=#2}
\def\pgfkeyssetfamily#1#2{%
    \pgfkeysiffamilydefined{#2}{%
        \pgfkeyssetvalue{#1/family}{#2}%
    }{%
        \pgfkeysalso{/errors/family unknown={#2}}%
    }%
}%


% Sets \ifpgfkeysfiltercontinue to true iff the current key belongs to
% the /errors tree.
\long\def\pgfkeys@cur@is@descendant@of@errors{%
    \expandafter\pgfkeys@cur@is@descendant@of@errors@impl\pgfkeyscurrentkey/errors\pgf@@eov
}%
\long\def\pgfkeys@cur@is@descendant@of@errors@impl#1/errors#2\pgf@@eov{%
    \def\pgfkeyspred@TMP{#1}%
    \ifx\pgfkeyspred@TMP\pgfkeys@empty
%\message{[SPECIAL CHECK] '\pgfkeyscurrentkey' is descendant of '/errors': TRUE.}%
        \pgfkeysfiltercontinuetrue
    \else
%\message{[SPECIAL CHECK] '\pgfkeyscurrentkey' is descendant of '/errors': FALSE.}%
        \pgfkeysfiltercontinuefalse
    \fi
}%

% \pgfkeysinterruptkeyfilter
% ...
% \endpgfkeysinterruptkeyfilter
% temporarily interrupts key filtering and enables it in
% \endpgfkeysinterruptkeyfilter.
%
% If key filtering it not active, this has no effect at all.
%
% REMARK:
% \pgfkeysinterruptkeyfilter...\endpgfkeysinterruptkeyfilter does NOT
% introduce a \TeX-group.
\def\pgfkeysinterruptkeyfilter{%
    \ifpgfkeysfilteringisactive
        \let\pgfkeys@case@one=\pgfkeys@orig@case@one
        \let\pgfkeys@try=\pgfkeys@orig@try
        \let\pgfkeys@unknown=\pgfkeys@orig@unknown
    \fi
}

\def\endpgfkeysinterruptkeyfilter{%
    \ifpgfkeysfilteringisactive
        \let\pgfkeys@case@one=\pgfkeys@case@one@filtered
        \let\pgfkeys@try=\pgfkeys@try@filtered
        \let\pgfkeys@unknown=\pgfkeys@unknown@filtered
    \fi
}

% Activates families #1, calls \pgfkeysfiltered and deactivates the
% families afterwards.
%
% REMARK: you need to install a family-based key filter predicate
% manually to benefit from the activated families!
%
% #1: comma separated family list
% #2: key-value pairs
%
% @see \pgfkeysactivatefamiliesandfilteroptions
\def\pgfkeysactivatefamiliesandfilteroptions#1#2{%
    \pgfkeysactivatefamilies{#1}{\pgfkeys@family@deactivation}%
    \pgfkeysfiltered{#2}%
    \pgfkeys@family@deactivation
}

% The "quick" variant of \pgfkeysactivatefamiliesandfilteroptions: it
% also assigns a default path.
%
% #1: comma separated family list
% #2: default path
% #3: key-value pairs
\def\pgfqkeysactivatefamiliesandfilteroptions#1#2#3{%
    \pgfkeysactivatefamilies{#1}{\pgfkeys@family@deactivation}%
    \pgfqkeysfiltered{#2}{#3}%
    \pgfkeys@family@deactivation
}

% Public version of split path:
\def\pgfkeyssplitpath{\pgfkeys@split@path}%


% The same as \pgfkeysactivatefamiliesandfilteroptions but just for
% ONE family.
%
% #1: family (maybe a macro)
% #2: key-value pairs
\def\pgfkeysactivatesinglefamilyandfilteroptions#1#2{%
    \pgfkeysactivatefamily{#1}%
    \pgfkeysfiltered{#2}%
    \pgfkeysdeactivatefamily{#1}%
}

% The "quick" variant of \pgfkeysactivatesinglefamilyandfilteroptions
%
% #1: family (maybe a macro)
% #2: default path
% #3: key-value pairs
\def\pgfqkeysactivatesinglefamilyandfilteroptions#1#2#3{%
    \pgfkeysactivatefamily{#1}%
    \pgfqkeysfiltered{#2}{#3}%
    \pgfkeysdeactivatefamily{#1}%
}

% Installs the key filter '#1' with argument '#2'.
% This is equivalent to
%    \pgfkeys{#1/.install key filter=#2}
%
% The current values of the key filter handler is stored into the public
% macros
% \pgfkeyscurrentkeyfilter
% and
% \pgfkeyscurrentkeyfilterargs
%
% #1: a full key name; may be a macro
% #2: optional arguments for the key. If the key expects more than one
% argument, supply '{{first}{second}}'
\def\pgfkeysinstallkeyfilter#1#2{%
    \pgfkeysifdefined{#1/.@cmd}{%
        \edef\pgfkeyscurrentkeyfilter{#1}%
        \def\pgfkeyscurrentkeyfilterargs{#2}%
        \pgfkeysgetvalue{#1/.@cmd}{\pgfkeys@key@predicate@}%
        \def\pgfkeys@key@predicate{\pgfkeys@key@predicate@#2\pgfeov}%
    }{%
        \pgfkeysvalueof{/errors/no such key filter/.@cmd}{#1}{#2}\pgfeov%
    }%
}

% Installs the key filter handler '#1' with argument '#2'.
% This is equivalent to
%    \pgfkeys{#1/.install key filter handler=#2}
%
% The current values of the key filter handler is stored into the public
% macros
% \pgfkeyscurrentkeyfilterhandler
% and
% \pgfkeyscurrentkeyfilterhandlerargs
%
% #1: a full key name; may be a macro
% #2: optional arguments for the handler. If the handler expects more than one
% argument, supply '{{first}{second}}'
\def\pgfkeysinstallkeyfilterhandler#1#2{%
    \pgfkeysifdefined{#1/.@cmd}{%
        \edef\pgfkeyscurrentkeyfilterhandler{#1}%
        \def\pgfkeyscurrentkeyfilterhandlerargs{#2}%
        \pgfkeysgetvalue{#1/.@cmd}{\pgfkeys@filtered@handler@}%
        \def\pgfkeys@filtered@handler{\pgfkeys@filtered@handler@#2\pgfeov}%
    }{%
        \pgfkeysvalueof{/errors/no such key filter handler/.@cmd}{#1}{#2}\pgfeov%
    }%
}

% Creates a macro which contains commands to re-activate the current
% key filter and key filter handler. It can be used to temporarily
% switch the key filter.
\def\pgfkeyssavekeyfilterstateto#1{%
    % produce the string
    % \pgfkeysinstallkeyfilter{...}{...}
    % \pgfkeysinstallkeyfilterhandler{...}{...}
    % where each argument is expanded once
    % FIXME: Do the same with less overhead!
    \pgfkeys@tmptoks={\pgfkeysinstallkeyfilter}%
    \pgfkeys@tmptoks=\expandafter\expandafter\expandafter{\expandafter\the\expandafter\pgfkeys@tmptoks\expandafter{\pgfkeyscurrentkeyfilter}}%
    \pgfkeys@tmptoks=\expandafter\expandafter\expandafter{\expandafter\the\expandafter\pgfkeys@tmptoks\expandafter{\pgfkeyscurrentkeyfilterargs}\pgfkeysinstallkeyfilterhandler}%
    \pgfkeys@tmptoks=\expandafter\expandafter\expandafter{\expandafter\the\expandafter\pgfkeys@tmptoks\expandafter{\pgfkeyscurrentkeyfilterhandler}}%
    \pgfkeys@tmptoks=\expandafter\expandafter\expandafter{\expandafter\the\expandafter\pgfkeys@tmptoks\expandafter{\pgfkeyscurrentkeyfilterhandlerargs}}%
    \edef#1{%
        \the\pgfkeys@tmptoks
    }%
}


\pgfkeys{%
    /errors/family unknown/.code=\pgfkeys@error{%
        Sorry, I do not know family '#1' and can't work with any assoicated family handling. Perhaps you misspelled it?},
    /errors/no such key filter/.code 2 args=\pgfkeys@error{Sorry, there is no such key filter '#1'.},
    /errors/no such key filter handler/.code 2 args=\pgfkeys@error{Sorry, there is no such key filter handler '#1'.},
    % HANDLERS:
    %
    % .is family should
    %  1. '.cd' into the families' path,
    %  2. define booleans to activate/deactive the family
    %     (see \pgfkeysisfamilyactive)
    %  3. make sure that \pgfkeyshasactivefamily returns true for
    %     the family itself.
    /handlers/.is family/.append code={%
        %\newif is an \outer macro in plain tex, so this here is not portable:
        %\expandafter\newif\csname if\pgfkeyscurrentpath/familyactive\endcsname
        \edef\pgfkeyspred@TMP{pgfk@\pgfkeyscurrentpath/familyactive}%
        \expandafter\pgfkeys@non@outer@newif\expandafter{\pgfkeyspred@TMP}%
        \edef\pgfkeyspred@TMP{\pgfkeyscurrentpath/.belongs to family=\pgfkeyscurrentpath}%
        \expandafter\pgfkeysalso\expandafter{\pgfkeyspred@TMP}%
    },%
    /handlers/.activate family/.code=\pgfkeysactivatefamily{\pgfkeyscurrentpath},
    /handlers/.deactivate family/.code=\pgfkeysdeactivatefamily{\pgfkeyscurrentpath},
    /handlers/.belongs to family/.code={\pgfkeyssetfamily{\pgfkeyscurrentpath}{#1}},%
    %
    %
    % An addition to the '.try' and '.retry' handlers:
    %
    % It is the same as '.retry', but if the option is still unknown, the
    % usual handlers for unknown keys will be invoked.
    /handlers/.lastretry/.code={%
        \ifpgfkeyssuccess\else
            \pgfkeys@try
            \ifpgfkeyssuccess\else
                \pgfkeys@split@path%
                \pgfkeys@unknown
            \fi
        \fi
    },
    %
    %
    /handlers/.install key filter/.code={%
        \pgfkeysinstallkeyfilter{\pgfkeyscurrentpath}{#1}%
    },%
    /handlers/.install key filter handler/.code={%
        \pgfkeysinstallkeyfilterhandler{\pgfkeyscurrentpath}{#1}%
    },%
    %
    %
    % KEY FILTER HANDLERS:
    %
    /pgf/key filter handlers/append filtered to/.code={%
        % Produce
        %  <orig key> '={' <value> '}'
        % where both, the key and the value are expanded just ONCE:
        \pgfkeys@tmptoks=\expandafter\expandafter\expandafter{\expandafter\pgfkeyscurrentkeyRAW\expandafter=\expandafter{\pgfkeyscurrentvalue}}%
        \ifx#1\pgfkeys@empty
        \else
            % Produce <old list> ','  <orig key> '={' <value> '}'
            \pgfkeys@tmptoks=\expandafter\expandafter\expandafter{\expandafter#1\expandafter,\the\pgfkeys@tmptoks}%
        \fi
        \edef#1{\the\pgfkeys@tmptoks}%
    },%
    /pgf/key filter handlers/ignore/.code={},
    /pgf/key filter handlers/ignore/.install key filter handler,
    /pgf/key filter handlers/log/.code={%
        \pgf@typeout{LOG: the option '\pgfkeyscurrentkey' (was originally '\pgfkeyscurrentkeyRAW') (case \pgfkeyscasenumber) has not been processed due to pgfkeysfiltered.}%
    },
    %
    %
    % KEY FILTER PREDICATES:
    %
    % Returns true if the currently processed key belongs to an active family.
    % A family is active if it has been activated before.
    /pgf/key filters/active families/.code={%
        \if\pgfkeyscasenumber0%
            % unknown options shall be processed with the
            % unknown-handlers.
            \pgfkeysfiltercontinuetrue
        \else
            \if\pgfkeyscasenumber3%
                \pgfkeysgetfamily\pgfkeyscurrentpath\pgfkeyspred@TMP
            \else
                \pgfkeysgetfamily\pgfkeyscurrentkey\pgfkeyspred@TMP
            \fi
            \ifpgfkeyssuccess
                \pgfkeysisfamilyactive{\pgfkeyspred@TMP}%
            \else% Ok, it does not belong to any family.
                \pgfkeysfiltercontinuefalse
            \fi
        \fi
    },%
    %
    %
    %
    % This filter works as follows:
    % 1. if the current key belongs to a family:
    %    return whether its family is active,
    % 2. if the current key does NOT belong to a family:
    %    return the result of criterion '#1',
    % 3. the current key is unknown:
    %    return the result of criterion '#2'.
    %
    % Arguments:
    % #1: the predicate which will be invoked in case 2.
    %     It will be invoked with the current case number as argument.
    % #2: the predicate which will be invoked in case 3 with
    %     the current case number as argument.
    /pgf/key filters/active families or no family/.code 2 args={%
        \if\pgfkeyscasenumber0%
            \pgfkeysevalkeyfilterwith{#2}%
        \else
            \if\pgfkeyscasenumber3%
                \pgfkeysgetfamily\pgfkeyscurrentpath\pgfkeyspred@TMP
            \else
                \pgfkeysgetfamily\pgfkeyscurrentkey\pgfkeyspred@TMP
            \fi
            \ifpgfkeyssuccess
                \pgfkeysisfamilyactive{\pgfkeyspred@TMP}%
            \else% Ok, it does not belong to any family.
                \pgfkeysevalkeyfilterwith{#1}%
            \fi
        \fi
    },
    /pgf/key filters/active families or no family DEBUG/.code 2 args={%
        \if\pgfkeyscasenumber0%
            \pgf@typeout{[pgfkeyshasactivefamilyornofamily(\pgfkeyscurrentkey, \pgfkeyscasenumber) invoking unknown handler '#2']}%
            \pgfkeysevalkeyfilterwith{#2}%
        \else
            \if\pgfkeyscasenumber3%
                \pgfkeysgetfamily\pgfkeyscurrentpath\pgfkeyspred@TMP
            \else
                \pgfkeysgetfamily\pgfkeyscurrentkey\pgfkeyspred@TMP
            \fi
            \ifpgfkeyssuccess
                \pgfkeysisfamilyactive{\pgfkeyspred@TMP}%
                \ifpgfkeysfiltercontinue
                    \pgf@typeout{[pgfkeyshasactivefamilyornofamily(\pgfkeyscurrentkey, \pgfkeyscasenumber) family is ACTIVE]}%
                \else
                    \pgf@typeout{[pgfkeyshasactivefamilyornofamily(\pgfkeyscurrentkey, \pgfkeyscasenumber) family is NOT active.]}%
                \fi
            \else% Ok, it does not belong to any family.
                \pgf@typeout{[pgfkeyshasactivefamilyornofamily(\pgfkeyscurrentkey, \pgfkeyscasenumber) invoking has-no-family-handler '#1']}%
                \pgfkeysevalkeyfilterwith{#1}%
            \fi
        \fi
    },
    %
    % A (faster) shortcut for
    % /pgf/key filters/active families or no family=
    %   {/pgf/keys filters/false}
    %   {/pgf/keys filters/false}
    /pgf/key filters/active families and known/.code={%
        \if\pgfkeyscasenumber0%
            \pgfkeysfiltercontinuefalse
        \else
            \if\pgfkeyscasenumber3%
                \pgfkeysgetfamily\pgfkeyscurrentpath\pgfkeyspred@TMP
            \else
                \pgfkeysgetfamily\pgfkeyscurrentkey\pgfkeyspred@TMP
            \fi
            \ifpgfkeyssuccess
                \pgfkeysisfamilyactive{\pgfkeyspred@TMP}%
            \else% Ok, it does not belong to any family.
                \pgfkeysfiltercontinuefalse
            \fi
        \fi
    },
    % A (faster) shortcut for
    % /pgf/key filters/active families or no family=
    %   {/pgf/key filters/is descendant of=#1}% for keys without family
    %   {/pgf/keys filters/false}
    /pgf/key filters/active families or descendants of/.code={%
        \if\pgfkeyscasenumber0%
            \pgfkeysfiltercontinuefalse
        \else
            \if\pgfkeyscasenumber3%
                \pgfkeysgetfamily\pgfkeyscurrentpath\pgfkeyspred@TMP
            \else
                \pgfkeysgetfamily\pgfkeyscurrentkey\pgfkeyspred@TMP
            \fi
            \ifpgfkeyssuccess
                \pgfkeysisfamilyactive{\pgfkeyspred@TMP}%
            \else% Ok, it does not belong to any family.
                % the 'is descendant of' implementation has been
                % COPY PASTED here:
                %
                % string prefix comparison:
                \def\pgfkeysisdescendantof@impl##1#1##2\pgf@@eov{%
                    \def\pgfkeyspred@TMP{##1}%
                    \ifx\pgfkeyspred@TMP\pgfkeys@empty
                        \pgfkeysfiltercontinuetrue
                    \else
                        \pgfkeysfiltercontinuefalse
                    \fi
                }%
                \expandafter\pgfkeysisdescendantof@impl\pgfkeyscurrentkey#1\pgf@@eov
            \fi
        \fi
    },
    %
    % Processes only options which are children of #1.
    % Example:
    %   is descendant of/.install key filter=/foo
    % will be true for
    %   /foo/bar/x=y
    %   /foo/.cd
    %   /foo/bar/.style=...
    % but not for
    %   /bar/foo/...
    /pgf/key filters/is descendant of/.code={%
        \if\pgfkeyscasenumber0%
%\message{'\pgfkeyscurrentkey' (case \pgfkeyscasenumber) is UNKNOWN. Calling unknown handler.}%
            % unknown options shall be processed with the
            % unknown-handlers.
            \pgfkeysfiltercontinuetrue
        \else
            % string prefix comparison:
            % [ note : this has been COPY-PASTED to
            %   |active families or descendants of| ]
            \def\pgfkeysisdescendantof@impl##1#1##2\pgf@@eov{%
                \def\pgfkeyspred@TMP{##1}%
                \ifx\pgfkeyspred@TMP\pgfkeys@empty
%\message{'\pgfkeyscurrentkey' (case \pgfkeyscasenumber) is descendant of '#1': TRUE.}%
                    \pgfkeysfiltercontinuetrue
                \else
%\message{'\pgfkeyscurrentkey' (case \pgfkeyscasenumber) is descendant of '#1': FALSE.}%
                    \pgfkeysfiltercontinuefalse
                \fi
            }%
            \expandafter\pgfkeysisdescendantof@impl\pgfkeyscurrentkey#1\pgf@@eov
        \fi
    },%
    %
    %
    %
    % Returns true if the currently processed full key equals #2.
    /pgf/key filters/equals/.code={%
        \if\pgfkeyscasenumber0%
            % Unknown option:
            \pgfkeysfiltercontinuetrue
        \else
            \def\pgfkeyspred@TMP{#1}%
            \ifx\pgfkeyscurrentkey\pgfkeyspred@TMP
                \pgfkeysfiltercontinuetrue
            \else
                \pgfkeysfiltercontinuefalse
            \fi
        \fi
    },%
    %
    %
    % Argument #1 can be any other (evaluated) filter predicate, its logical return
    % value will be inverted.
    % Example:
    %   not/.install key filter={is descendant of=/tikz}
    % will install a key filter which evaluates 'is descendant of' with argument
    % '/tikz' and returns the logical negation of the result.
    %
    /pgf/key filters/not/.code={%
        \pgfkeysevalkeyfilterwith{#1}%
        \ifpgfkeysfiltercontinue
            \pgfkeysfiltercontinuefalse
        \else
            \pgfkeysfiltercontinuetrue
        \fi
    },%
    /pgf/key filters/and/.code 2 args={%
        \pgfkeysevalkeyfilterwith{#1}%
        \ifpgfkeysfiltercontinue
            \pgfkeysevalkeyfilterwith{#2}%
        \fi
    },%
    /pgf/key filters/or/.code 2 args={%
        \pgfkeysevalkeyfilterwith{#1}%
        \ifpgfkeysfiltercontinue
        \else
            \pgfkeysevalkeyfilterwith{#2}%
        \fi
    },%
    /pgf/key filters/true/.code={\pgfkeysfiltercontinuetrue},%
    /pgf/key filters/true/.install key filter,
    /pgf/key filters/false/.code={%
        \pgfkeysfiltercontinuefalse
    },%
    %
    % Returns false if the current key is unknown, which avoids calling
    % the unknown handlers.
    /pgf/key filters/defined/.code={%
        \if\pgfkeyscasenumber0%
            \pgfkeysfiltercontinuefalse
        \else
            \pgfkeysfiltercontinuetrue
        \fi
    },
}%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Private IMPLEMENTATION
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


% This command does THE SAME work as \pgfkeys@case@one,
% but it applies filtering whenever it identified the type of an
% option.
\def\pgfkeys@case@one@filtered{%
    \pgfkeys@cur@is@descendant@of@errors
    \ifpgfkeysfiltercontinue
        \pgfkeys@orig@case@one
    \else
        \pgfkeysfiltercontinuetrue
        \pgfkeysifdefined{\pgfkeyscurrentkey/.@cmd}{%
            % CASE ONE: a command option
            \def\pgfkeyscasenumber{1}%
            \pgfkeys@key@predicate%
            \ifpgfkeysfiltercontinue
%\message{PROCESSING KEY \pgfkeyscurrentkey!}%
                \pgfkeysgetvalue{\pgfkeyscurrentkey/.@cmd}{\pgfkeys@code}%
                \expandafter\pgfkeys@code\pgfkeyscurrentvalue\pgfeov
            \else
%\message{FILTERED OUT  KEY \pgfkeyscurrentkey!}%
                \pgfkeys@filtered@handler%
            \fi
        }{%
            % CASE TWO: a normal value option
            \pgfkeysifdefined{\pgfkeyscurrentkey}{%
                \def\pgfkeyscasenumber{2}%
                \pgfkeys@key@predicate%
                \ifpgfkeysfiltercontinue
%\message{PROCESSING KEY \pgfkeyscurrentkey!}%
                    \pgfkeys@case@two@extern
                \else
%\message{FILTERED OUT  KEY \pgfkeyscurrentkey!}%
                    \pgfkeys@filtered@handler%
                \fi
            }{%
                \pgfkeys@split@path
                % CASE THREE: a handler
                \pgfkeysifdefined{/handlers/\pgfkeyscurrentname/.@cmd}{%
                    \pgfkeys@ifexecutehandler{%
                        \def\pgfkeyscasenumber{3}%
                        \pgfkeys@key@predicate%
                        \ifpgfkeysfiltercontinue
%\message{PROCESSING KEY \pgfkeyscurrentkey!}%
                            \pgfkeysgetvalue{/handlers/\pgfkeyscurrentname/.@cmd}{\pgfkeys@code}%
                            \expandafter\pgfkeys@code\pgfkeyscurrentvalue\pgfeov
                        \else
%\message{FILTERED OUT  KEY \pgfkeyscurrentkey!}%
                            \pgfkeys@filtered@handler%
                        \fi
                    }{%
                        \pgfkeys@unknown
                    }%
                }{%
                    \pgfkeys@unknown
                }%
            }%
        }%
    \fi
}%

\def\pgfkeys@unknown@filtered{%
    % CASE ZERO: an unknown option.
    \def\pgfkeyscasenumber{0}%
    \pgfkeys@key@predicate%
    \ifpgfkeysfiltercontinue
%\message{PROCESSING  KEY \pgfkeyscurrentkey!}%
        % start normal 'unknown' handlers:
        \pgfkeys@orig@unknown
    \else
%\message{FILTERED OUT  KEY \pgfkeyscurrentkey!}%
        \pgfkeys@filtered@handler%
    \fi
}

% Does the same as \pgfkeys@try, but it also invokes the key filters.
\def\pgfkeys@try@filtered{%
    \ifpgfkeysfiltercontinue
        \pgfkeys@orig@try
    \else
        \pgfkeysfiltercontinuetrue
        \edef\pgfkeyscurrentkey{\pgfkeyscurrentpath}% make sure that \pgfkeys@code doesn't know about 'try'. Important for .is choice
        \ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text% Hmm... no value
            \pgfkeysifdefined{\pgfkeyscurrentpath/.@def}%
            {\pgfkeysgetvalue{\pgfkeyscurrentpath/.@def}{\pgfkeyscurrentvalue}}
            {}% no default, so leave it
        \fi%
        \pgfkeysifdefined{\pgfkeyscurrentpath/.@cmd}%
        {%
            % CASE ONE: a command option
            \def\pgfkeyscasenumber{1}%
            \pgfkeys@key@predicate%
            \ifpgfkeysfiltercontinue
                \pgfkeysgetvalue{\pgfkeyscurrentkey/.@cmd}{\pgfkeys@code}%
                \expandafter\pgfkeys@code\pgfkeyscurrentvalue\pgfeov%
            \else
                \pgfkeys@filtered@handler%
            \fi
            \pgfkeyssuccesstrue%
        }%
        {%
            \pgfkeysifdefined{\pgfkeyscurrentpath}%
            {% CASE TWO: a normal value option
                \def\pgfkeyscasenumber{2}%
                \pgfkeys@key@predicate%
                \ifpgfkeysfiltercontinue
                    \ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text%
                        \pgfkeysvalueof{\pgfkeyscurrentpath}%
                    \else%
                        \pgfkeyslet{\pgfkeyscurrentpath}\pgfkeyscurrentvalue%
                    \fi%
                \else
                    \pgfkeys@filtered@handler%
                \fi
                \pgfkeyssuccesstrue%
            }%
            {%
                \pgfkeys@split@path%
                \pgfkeysifdefined{/handlers/\pgfkeyscurrentname/.@cmd}{%
                    % CASE THREE: a handled key
                    %
                    % in the standard configuration, this check here is redundant
                    % because pgfkeys@ifexecutehandler === true.
                    % It is only interesting for 'handle only existing'.
                    \pgfkeys@ifexecutehandler{%
                        \def\pgfkeyscasenumber{3}%
                        \pgfkeys@key@predicate%
                        \ifpgfkeysfiltercontinue
%\message{PROCESSING KEY \pgfkeyscurrentkey!}%
                            \pgfkeysgetvalue{/handlers/\pgfkeyscurrentname/.@cmd}{\pgfkeys@code}%
                            \expandafter\pgfkeys@code\pgfkeyscurrentvalue\pgfeov
                        \else
%\message{FILTERED OUT  KEY \pgfkeyscurrentkey!}%
                            \pgfkeys@filtered@handler%
                        \fi
                        \pgfkeyssuccesstrue%
                    }{%
                        \pgfkeyssuccessfalse
                    }%
                }{%
                    \pgfkeyssuccessfalse
                }%
            }%
        }%
    \fi
}


% #1 the code to invoke after init and before cleanup
\long\def\pgfkeys@install@filter@and@invoke#1{%
    \ifpgfkeysfilteringisactive
        \pgfkeys@error{Sorry, nested calls to key filtering routines are not allowed. (reason: It is not possible to properly restore the previous filtering state after returning from the nested call)}%
    \fi
    \pgfkeysfilteringisactivetrue
    \let\pgfkeys@case@one=\pgfkeys@case@one@filtered
    \let\pgfkeys@try=\pgfkeys@try@filtered
    \let\pgfkeys@unknown=\pgfkeys@unknown@filtered
    #1%
    \let\pgfkeys@case@one=\pgfkeys@orig@case@one
    \let\pgfkeys@try=\pgfkeys@orig@try
    \let\pgfkeys@unknown=\pgfkeys@orig@unknown
    \pgfkeysfilteringisactivefalse
}


%--------------------------------------------------
% \def\pgfkeys@eval@key@filter@subroutine@case@one{%
%     \pgfkeysifdefined{\pgfkeyscurrentkey/.@cmd}{%
%         \pgfkeysgetvalue{\pgfkeyscurrentkey/.@cmd}{\pgfkeys@code}%
%         \let\pgfkeyspred@TMP=\pgfkeyscurrentvalue
%         \pgfkeys@eval@key@filter@subroutine@restorestate
%         \expandafter\pgfkeys@code\pgfkeyspred@TMP\pgfeov
%     }{%
%         \pgfkeysvalueof{/errors/no such key filter/.@cmd}\pgfkeyscurrentkey\pgfkeyscurrentvalue\pgfeov%
%     }%
% }
% % THIS VERSION IS TOO SLOW. See below.
% \def\pgfkeysevalkeyfilterwith#1{%
%     \edef\pgfkeys@eval@key@filter@subroutine@restorestate{%
%         \noexpand\def\noexpand\pgfkeyscurrentkey{\pgfkeyscurrentkey}%
%         \noexpand\def\noexpand\pgfkeyscurrentkeyRAW{\pgfkeyscurrentkeyRAW}%
%         \noexpand\def\noexpand\pgfkeyscurrentname{\pgfkeyscurrentname}%
%         \noexpand\def\noexpand\pgfkeyscurrentvalue{\pgfkeyscurrentvalue}%
%         \noexpand\pgfkeys@pathtoks={\pgfkeyscurrentpath}%
%     }%
%     \pgfkeysinterruptkeyfilter
%     \let\pgfkeys@case@one=\pgfkeys@eval@key@filter@subroutine@case@one
%     \pgfkeysalso{#1}%
%     \endpgfkeysinterruptkeyfilter% this here also restored \pgfkeys@case@one.
%     \pgfkeys@eval@key@filter@subroutine@restorestate
% }%
%--------------------------------------------------


% Evaluates a key filter '#1'. Example:
% \pgfkeysevalkeyfilterwith{/pgf/key filters/equals=/tikz}
%
% \pgfkeysevalkeyfilterwith works only if key filtering is
% active.
%
% The argument '#1' MUST be a FULL KEY.
%
% The implementation employs a subset of the \pgfkeysalso code.
\long\def\pgfkeysevalkeyfilterwith#1{%
    \pgfkeys@eval@key@filter@subroutine@unpack#1=\pgfkeysnovalue=\pgfkeys@stop
}%
\long\def\pgfkeys@eval@key@filter@subroutine@unpack#1=#2=#3\pgfkeys@stop{%
    \pgfkeys@spdef\pgfkeyspred@TMP{#1}%
    \edef\pgfkeyspred@TMP{\pgfkeyspred@TMP}%
    \pgfkeys@spdef\pgfkeyspred@TMPB{#2}% TMPB=value
    \ifx\pgfkeyspred@TMPB\pgfkeysnovalue@text% Hmm... no value
        \pgfkeysifdefined{\pgfkeyspred@TMP/.@def}%
        {\pgfkeysgetvalue{\pgfkeyspred@TMP/.@def}{\pgfkeyspred@TMPB}}
        {}% no default, so leave it
    \fi%
    \ifx\pgfkeyspred@TMPB\pgfkeysvaluerequired%
        \pgfkeysvalueof{/errors/value required/.@cmd}\pgfkeyspred@TMP\pgfkeyspred@TMPB\pgfeov%
    \else%
        \pgfkeysifdefined{\pgfkeyspred@TMP/.@cmd}{%
            \pgfkeysgetvalue{\pgfkeyspred@TMP/.@cmd}{\pgfkeys@code}%
            \expandafter\pgfkeys@code\pgfkeyspred@TMPB\pgfeov
        }{%
            \pgfkeysvalueof{/errors/no such key filter/.@cmd}\pgfkeyspred@TMP\pgfkeyspred@TMPB\pgfeov%
        }%
    \fi%
}


\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
}%

% For latex and context, this here has the same effect as a \newif
% applied to 'if#1'. For plain tex, it has also the same effect, but
% it is not an \outer macro as the plain-tex \newif.
\def\pgfkeys@non@outer@newif#1{%
    \expandafter\pgfkeys@non@outer@newif@\csname if#1\endcsname{#1}%
}
\endinput
