%D \module
%D   [       file=core-fld,
%D        version=1997.05.18,
%D          title=\CONTEXT\ Core Macros,
%D       subtitle=Fill||in fields,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

% \appendtocommalist  versus \addtocommalist
%
% * as default trigger in radiofields ?
%
% beware: weblink plugin truncates on length, while save as doesn't;
% more precise: (1) first time right string is sent, (2)
% internal string truncated, (3) second time truncated
% string is sent.

\writestatus{loading}{Context Field Macros}

% messages

\definemessageconstant{fields}

\unprotect

%D First we hook fields into the (viewer based) layering mechanism
%D (implemented as properties).

\ifx\currentlayerproperty\undefined\else \let\currentlayerproperty\empty\fi

\appendtoks
  \doif\@@iafieldlayer\v!auto
    {\def\@@iafieldlayer{\currentlayerproperty}}%
\to \everysetupinteraction

\setupinteraction
  [\c!fieldlayer=\v!auto] % auto by default

%D Internal command, linked to \type{\definesymbol}.

\def\dogetfieldsymbol#1%
  {\getobject{SYM}{#1}}

\def\dopresetfieldsymbol#1%
  {\checkobjectreferences
   \doifobjectfoundelse{SYM}{#1}
     {}
     {\settightobject{SYM}{#1}\hbox{\symbol[#1]}%
      \flushatshipout
        {\setbox0\hbox{\hskip-\maxdimen\getobject{SYM}{#1}}%
         \smashbox0\box0}}}

\def\presetfieldsymbols[#1]% slow
  {\def\dopresetfieldsymbols##1%
     {\processcommalist[##1]\dopresetfieldsymbol}%
   \@EA\processcommalist\@EA[#1]\dopresetfieldsymbols}

\def\definedefaultsymbols
  {\definesymbol[defaultyes][$\times$]%
   \definesymbol[defaultno][$\cdot$]}

\def\resetfieldsymbol[#1]% for experimental usage only
  {\resetobject{SYM}{#1}}

%D The interface to the specials. DEFAULT NOG ANDERS

\def\preparefieldvariables % evt \def's at the outer level (test) or \edef's here for fast testing
  {\let\@@DriverFieldNumber         \@@fdn
   \let\@@DriverFieldStyle          \@@fdstyle
   \let\@@DriverFieldColor          \@@fdcolor
   \let\@@DriverFieldBackgroundColor\@@fdfieldbackgroundcolor
   \let\@@DriverFieldFrameColor     \@@fdfieldframecolor
   \let\@@DriverFieldLayer          \@@fdfieldlayer
   \let\@@DriverFieldOption         \@@fdoption
   \let\@@DriverFieldAlign          \@@fdalign
   \let\@@DriverFieldClickIn        \@@fdclickin
   \let\@@DriverFieldClickOut       \@@fdclickout
   \let\@@DriverFieldRegionIn       \@@fdregionin
   \let\@@DriverFieldRegionOut      \@@fdregionout
   \let\@@DriverFieldAfterKey       \@@fdafterkey
   \let\@@DriverFieldFormat         \@@fdformat
   \let\@@DriverFieldValidate       \@@fdvalidate
   \let\@@DriverFieldCalculate      \@@fdcalculate
   \let\@@DriverFieldFocusIn        \@@fdfocusin
   \let\@@DriverFieldFocusOut       \@@fdfocusout}

% todo : remove arguments, consider DriverField a namespace

\def\presetlinefield
  {\preparefieldvariables
   \dopresetlinefield
     {\@@DriverFieldName}
     {\@@DriverFieldWidth}
     {\@@DriverFieldHeight}
     {\@@DriverFieldDefault}
     {\@@DriverFieldNumber}
     {\@@DriverFieldStyle,\@@DriverFieldColor,\@@DriverFieldBackgroundColor,\@@DriverFieldFrameColor}
     {\@@DriverFieldOption}
     {\@@DriverFieldAlign}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\presettextfield
  {\preparefieldvariables
   \dopresettextfield
     {\@@DriverFieldName}
     {\@@DriverFieldWidth}
     {\@@DriverFieldHeight}
     {\@@DriverFieldDefault}
     {\@@DriverFieldNumber}
     {\@@DriverFieldStyle,\@@DriverFieldColor,\@@DriverFieldBackgroundColor,\@@DriverFieldFrameColor}
     {\@@DriverFieldOption}
     {\@@DriverFieldAlign}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\presetchoicefield
  {\preparefieldvariables
   \dopresetchoicefield
     {\@@DriverFieldName}
     {\@@DriverFieldWidth}
     {\@@DriverFieldHeight}
     {\@@DriverFieldDefault}
     {\@@DriverFieldStyle,\@@DriverFieldColor,\@@DriverFieldBackgroundColor,\@@DriverFieldFrameColor}
     {\@@DriverFieldOption}
     {\@@DriverFieldValues}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\presetpopupfield
  {\preparefieldvariables
   \dopresetpopupfield
     {\@@DriverFieldName}
     {\@@DriverFieldWidth}
     {\@@DriverFieldHeight}
     {\@@DriverFieldDefault}
     {\@@DriverFieldStyle,\@@DriverFieldColor,\@@DriverFieldBackgroundColor,\@@DriverFieldFrameColor}
     {\@@DriverFieldOption}
     {\@@DriverFieldValues}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\presetcombofield
  {\preparefieldvariables
   \dopresetcombofield
     {\@@DriverFieldName}
     {\@@DriverFieldWidth}
     {\@@DriverFieldHeight}
     {\@@DriverFieldDefault}
     {\@@DriverFieldStyle,\@@DriverFieldColor,\@@DriverFieldBackgroundColor,\@@DriverFieldFrameColor}
     {\@@DriverFieldOption}
     {\@@DriverFieldValues}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\presetcheckfield
  {\preparefieldvariables
   \presetfieldsymbols[\@@DriverFieldValues]%
   \dopresetcheckfield
     {\@@DriverFieldName}
     {\@@DriverFieldWidth}
     {\@@DriverFieldHeight}
     {\@@DriverFieldDefault}
     {\@@DriverFieldOption}
     {\@@DriverFieldValues}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\presetpushfield
  {\preparefieldvariables
   %\edef\@@DriverFieldValues{{\@@DriverFieldValues}}% makes sure {a,b,c} is passed
   \presetfieldsymbols[\@@DriverFieldValues]%
   \dopresetpushfield
     {\@@DriverFieldName}
     {\@@DriverFieldWidth}
     {\@@DriverFieldHeight}
     {\@@DriverFieldDefault}
     {\@@DriverFieldOption}
     {\@@DriverFieldValues}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\presetradiofield
  {\preparefieldvariables
   \presetfieldsymbols[\@@DriverFieldValues]%
   \dopresetradiofield
     {\@@DriverFieldName}
     {\@@DriverFieldWidth}
     {\@@DriverFieldHeight}
     {\@@DriverFieldDefault}
     {\@@DriverFieldOption}
     {\@@DriverFieldRoot}
     {\@@DriverFieldValues}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\presetradiorecord
  {\preparefieldvariables
   \dopresetradiorecord
     {\@@DriverFieldName}
     {\@@DriverFieldDefault}
     {\@@DriverFieldOption}
     {\@@DriverFieldKids}
     {\@@DriverFieldClickIn,\@@DriverFieldClickOut,\@@DriverFieldRegionIn,\@@DriverFieldRegionOut,%
      \@@DriverFieldAfterKey,\@@DriverFieldFormat,\@@DriverFieldValidate,\@@DriverFieldCalculate,%
      \@@DriverFieldFocusIn,\@@DriverFieldFocusOut}}

\def\setfieldmodes#1#2#3%
  {\xdef\@@DriverFieldMode{#1}%  % 0 1 2 3
   \xdef\@@DriverFieldFree{#2}%  % 0 1
   \xdef\@@DriverFieldAuto{#3}}  % 0 1

\newevery\everysetfield\relax

\def\doiffieldelse#1{\doifdefinedelse{fielddata#1}}

\def\setfield#1#2#3#4#5#6#7#8#9%
  {\bgroup
   \doglobal\increment\numberoffields
   \iftracefields
     \doglobal\addtocommalist{#1}\collectedfields
   \fi
   \the\everysetfield
   \setxvalue{fielddata#1}% kortere tag #7 needs expansion etc
     {\noexpand\dosetfield{#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}}%
   \egroup}

\def\dosetfield#1#2#3#4#5#6#7#8#9%
  {\xdef\@@DriverFieldName   {#1}%
   \xdef\@@DriverFieldType   {#2}%
   \xdef\@@DriverFieldRoot   {#3}%
   \xdef\@@DriverFieldParent {#4}%
   \xdef\@@DriverFieldKids   {#5}%
   \xdef\@@DriverFieldGroup  {#6}%
   \setfieldmodes             #7%
   \bgroup
     \def\par{\string\n\string\n}%
     \xdef\@@DriverFieldValues {#8}%
     \xdef\@@DriverFieldDefault{#9}%
   \egroup}

\def\changefield#1%
  {\setfield{#1}\@@DriverFieldType\@@DriverFieldRoot\@@DriverFieldParent\@@DriverFieldKids\@@DriverFieldGroup
     {\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}\@@DriverFieldValues\@@DriverFieldDefault}

\def\getfield#1% name
  {\doifundefinedelse{fielddata#1}
     {\dosetfield{#1}\empty\empty\empty\empty\empty{\empty00}\empty\empty}
     {\getvalue{fielddata#1}}}

\newif\iftracefields \tracefieldsfalse

\let\tracefields\tracefieldstrue

\def\doshowfields[#1]% todo: tabulate van maken en runtime
  {\bgroup
   \switchtobodyfont[8pt,tt]%
   \doifsomething{#1}{\def\collectedfields{#1}}%
   \ifx\collectedfields\empty
     \par specify [fieldlist] or say \type{\tracefieldstrue} first\par
   \else
     \def\normalizedfieldmode##1##2##3%
       {\ifcase0##2 \else\sl\fi
        \ifcase0##1 loner\or parent\or clone\or copy\fi}%
     \def\dosetfield##1##2##3##4##5##6##7##8##9%
       {##1&##2&##3&##4&##5&##6&\normalizedfieldmode##7&##8&##9\cr}%
     \halign
       {&##\strut\hss\quad\cr
        \noalign{\hrule}%
        NAME  &TYPE  &ROOT   &
        PARENT&KIDS  &GROUP  &
        MODE  &VALUES&DEFAULT\cr
        \noalign{\hrule}%
        \@EA\globalprocesscommalist\@EA[\collectedfields]\getfield
        \noalign{\hrule}}%
   \fi
   \egroup}

\def\showfields
  {\dosingleempty\doshowfields}

\def\dologfields[#1]%
  {\bgroup
   \immediate\openout\scratchwrite=fields.log
   \doifsomething{#1}{\def\collectedfields{#1}}%
   \ifx\colledtedfields\empty
     \immediate\write\scratchwrite{use \tracefieldstrue}%
   \else
     \def\normalizedfieldmode##1##2##3%
       {\edef\@@DriverFieldMode
          {\ifcase##1 loner \or parent \or clone \or copy \fi
           \ifcase##2 \else(done)\fi}}%
     \def\dosetfield##1##2##3##4##5##6##7##8##9%
       {\normalizedfieldmode##7%
        \immediate\write\scratchwrite
          {N=##1 / T=##2 / R=##3 / P=##4 / K=##5 / G=##6 /
           M=\@@DriverFieldMode\space/ V=##8 / D=##9}}%
     \processcommacommand[\collectedfields]\getfield
   \fi
   \immediate\closeout\scratchwrite
   \egroup}

\def\logfields
  {\dosingleempty\doLogFields}

%D \starttyping
%D \definefield [name] [type] [group] [values] [default]
%D
%D \definefield [WWWW] [text]  [textsetup]            [default text]
%D \definefield [XXXX] [push]  [pushsetup]  [yes,no]  [yes]
%D \definefield [XXXX] [check] [checksetup] [yes,no]  [yes]
%D \definefield [YYYY] [combo] [combosetup] [a,b,c,d] [b]
%D \definefield [ZZZZ] [radio] [radiosetup] [W,X,Y,Z] [Y]
%D
%D \definesubfield [W]   [subsetup] [p,q]
%D \definesubfield [X,Y] [subsetup] [p,r]
%D \definesubfield [Z]   [subsetup] [y,z]
%D
%D evt \definemainfield ... wanneer geplaatst voor subs gegeven
%D
%D \clonefield [XXXX] [XX,YY] [mysetup]      [on,off]
%D \clonefield [Z]    [AA,BB] [somesetup]    [true,false]
%D \clonefield [Z]    [CC,DD] [anothersetup]
%D
%D \copyfield [XXXX] [PP,QQ,RR]
%D
%D \field[XXXX]
%D \fitfield[XXXX]
%D \stoptyping

\newif\ifdefinemainfield \definemainfieldfalse

%D We need to keep track of cloned (related) fields and so by
%D maintaining lists of field clones.
%D
%D The first alternative used a two pass data list and was
%D implemented as follows:
%D
%D \starttyping
%D \def\getmainfieldkids#1%
%D   {\let\@@DriverFieldKids\empty
%D    \ifdefinemainfield
%D      \definetwopasslist{fld:#1}% defined by system
%D      \doloop
%D        {\gettwopassdata{fld:#1}%
%D         \iftwopassdatafound
%D          %\addtocommalist\twopassdata\@@DriverFieldKids
%D           \appendtocommalist\twopassdata\@@DriverFieldKids
%D         \else
%D           \exitloop
%D         \fi}%
%D    \fi}
%D \stoptyping
%D
%D However, the next alternative is much faster when we have
%D a field with thousands of clones, something not that
%D imaginary.
%D
%D \starttyping
%D \def\getmainfieldkids#1%
%D   {\let\@@DriverFieldKids\empty
%D    \ifdefinemainfield
%D      \definerawpasslist{fld:#1}% runtime defined by system
%D      \moverawpasslist{fld:#1}\@@DriverFieldKids
%D    \fi}
%D \stoptyping
%D
%D The data is written by file using:
%D
%D \starttyping
%D \newcounter\nofmainfieldkids
%D
%D \def\setmainfieldkid#1#2%
%D   {\doglobal\increment\nofmainfieldkids
%D    \immediatewriteutilitycommand
%D      {\twopassentry%
%D         {fld:#1}%
%D         {\nofmainfieldkids}%
%D         {#2}}}
%D \stoptyping
%D
%D The trade of of this mechanism is that for each cloned or
%D copied field, the uitlity file is to be read in order to
%D fetch the data.
%D
%D The next, much faster alternative uses a dedicated %
%D reference mechanism.

\def\setmainfieldkid#1#2%
  {\immediatewriteutilitycommand{\fieldreference{#1}{#2}}}

\def\checkfieldreferences
  {\startnointerference
   \protectlabels
   \doutilities{fieldreferences}\jobname\empty\relax\relax
   \global\let\checkfieldreferences\relax
   \stopnointerference}

\def\setfieldreferences
  {\def\fieldreference##1##2%
     {\ifundefined{\r!widget##1}%
        \setxvalue{\r!widget##1}{##2}%
      \else
        \edef\!!stringa{\getvalue{\r!widget##1}}%
        \setxvalue{\r!widget##1}{\!!stringa,##2}%
      \fi}}

\def\resetfieldreferences
  {\let\fieldreference\gobbletwoarguments}

\def\getmainfieldkids#1%
  {\checkfieldreferences
   \ifdefinemainfield
     \doifundefinedelse{\r!widget#1}%
       {\let\@@DriverFieldKids\empty}
       {\@EA\let\@EA\@@DriverFieldKids\csname\r!widget#1\endcsname}%
   \else
     \let\@@DriverFieldKids\empty
   \fi}

\resetfieldreferences

%D Of course it costs a few more tokens to implement, but it's
%D worth the memory: running for instance the 2000 page
%D english examns publishing on demand document went down from
%D 1350 seconds to less than 950 on a 650 Mhz pentium.

\def\definefield
  {\definemainfieldfalse\doquintupleempty\dodefinefield}

\def\definemainfield
  {\definemainfieldtrue \doquintupleempty\dodefinefield}

\let\collectedfields\empty
\newcounter\numberoffields
\newcounter\totalnumberoffields

\def\savenumberoffields
  {\ifcase\numberoffields\relax\else
     \savecurrentvalue\totalnumberoffields\numberoffields
   \fi}

\appendtoks \savenumberoffields \to \everybye % \everylastshipout

% \def\presetfieldreferences
%   {\ifnum\totalnumberoffields>0
%      \definereference[AtOpenInitializeForm][\v!ResetForm]%
%    \fi}
%
% \definereference[AtOpenInitializeForm][\v!geen]
%
% \appendtoks \presetfieldreferences \to \everycheckreferences

\def\dodefinefield[#1][#2][#3][#4][#5]%
  {\ifsecondargument
     \edef\currentfieldname{#1}% just in case we're inside a loop
     \doifundefinedelse{define#2field}
       {\writestatus\m!fields{unknown field type #2}}
       {\doifundefined{fielddata\currentfieldname}
          {\getmainfieldkids\currentfieldname
           \ifdefinemainfield
             \ifx\@@DriverFieldKids\empty
               \let\@@DriverFieldMode\fieldlonermode
             \else
               \let\@@DriverFieldMode\fieldparentmode
             \fi
             \def\@@DriverFieldAuto{1}%
           \else
             \let\@@DriverFieldMode\fieldlonermode
             \def\@@DriverFieldAuto{0}%
           \fi
           \def\@@DriverFieldFree{0}%
           \getvalue{define#2field}{\currentfieldname}{#2}{#3}{#4}{#5}}}%
   \else
     \writestatus\m!fields{pass fieldname and fieldtype}%
   \fi}

\def\definelinefield#1#2#3#4#5%
  {\setfield{#1}{#2}{}{}{\@@DriverFieldKids}{#3}{\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}{}{#4}}

\let\definetextfield=\definelinefield

\def\definechoicefield#1#2#3#4#5%
  {\doifelsenothing{#4}
     {\def\@@DriverFieldValues{yes,no}}
     {\def\@@DriverFieldValues{#4}}%
   \doifelsenothing{#5}
     {\dogetcommacommandelement2\from\@@DriverFieldValues \to\@@DriverFieldDefault
      \dogetcommacommandelement1\from\@@DriverFieldDefault\to\@@DriverFieldDefault}
     {\def\@@DriverFieldDefault{#5}}%
   \setfield{#1}{#2}{}{}{\@@DriverFieldKids}{#3}{\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}{\@@DriverFieldValues}{\@@DriverFieldDefault}}

\let\definepopupfield=\definechoicefield
\let\definecombofield=\definechoicefield

%\def\definecheckfield#1#2#3#4#5%
%  {\doifelsenothing{#4}
%     {\definedefaultsymbols
%      \def\@@DriverFieldValues{defaultyes}}
%     {\def\@@DriverFieldValues{#4}}%
%   \doifelsenothing{#5}
%     {\dogetcommacommandelement2\from\@@DriverFieldValues\to\@@DriverFieldDefault
%      \dogetcommacommandelement1\from\@@DriverFieldDefault\to\@@DriverFieldDefault}
%     {\def\@@DriverFieldDefault{#5}}%
%   \setfield{#1}{#2}{}{}{\@@DriverFieldKids}{#3}{\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}{\@@DriverFieldValues}{\@@DriverFieldDefault}}

%D Since these fields have an on/off state only, we pass 1/0
%D to the driver as default values.

\def\definecheckfield#1#2#3#4#5%
  {\doifelsenothing{#4}
     {\definedefaultsymbols
      \def\@@DriverFieldValues{defaultyes}}
     {\def\@@DriverFieldValues{#4}}%
   \doifelsenothing{#5}
     {\def\@@DriverFieldDefault{2}}
     {\dogetcommacommandelement1\from\@@DriverFieldValues\to\@@DriverFieldDefault
      \doifinstringelse{#5}{\@@DriverFieldDefault}
        {\def\@@DriverFieldDefault{1}}
        {\def\@@DriverFieldDefault{0}}}%
   \setfield
     {#1}{#2}{}{}{\@@DriverFieldKids}{#3}%
     {\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}%
     {\@@DriverFieldValues}{\@@DriverFieldDefault}}

\let\definepushfield=\definecheckfield

\def\defineradiofield#1#2#3#4#5%
  {\iffourthargument
     \doifelsenothing{#5}
       {\dogetcommacommandelement1\from#4\to\SavedFieldDefault
        \dogetcommacommandelement1\from\SavedFieldDefault\to\SavedFieldDefault}
       {\def\SavedFieldDefault{#5}}%
% when opt works
% \@EA\beforesplitstring\SavedFieldDefault\at=>\to\SavedFieldDefault
     \ifx\@@DriverFieldKids\empty
       \setfield{#1}{#2}{}{}{#4}{#3}{\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}{}{\SavedFieldDefault}%
     \else
       \setfield{#1}{#2}{}{}{#4,\@@DriverFieldKids}{#3}{\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}{}{\SavedFieldDefault}%
     \fi
%
     \def\docommando##1%
       {\doifelse{##1}\SavedFieldDefault
          {\def\@@DriverFieldDefault{##1}}%
          {\let\@@DriverFieldDefault\empty}%
        \setfield{##1}{#2}{#1}{}{}{#3}{\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}{}{\@@DriverFieldDefault}}%
% when opt works
%     \def\docommando##1%
%       {\@EA\beforesplitstring##1\at=>\to\FieldValue
%        \doifelse\FieldValue\SavedFieldDefault
%          {\let\@@DriverFieldDefault\FieldValue}%
%          {\let\@@DriverFieldDefault\empty}%
%        \setfield\FieldValue{#2}{#1}{}{}{#3}{\@@DriverFieldMode\@@DriverFieldFree\@@DriverFieldAuto}{}{\@@DriverFieldDefault}}%
     \processcommalist[#4]\docommando
  \else
     \writestatus\m!fields{pass values too}%
  \fi}

\def\definesubfield
  {\dotripleempty\dodefinesubfield}

\def\dodefinesubfield[#1][#2][#3]% for the moment only radio ones
  {\ifsecondargument
     \def\docommando##1%
       {\getfield{##1}%
        \ifx\@@DriverFieldType\empty
          \writestatus\m!fields{unknown field ##1}% to do
        \else
          \doifsomething{#2}
            {\edef\@@DriverFieldGroup{#2}}%
          \doifelsenothing{#3}
            {\definedefaultsymbols
             \def\@@DriverFieldValues{defaultyes}}
            {\def\@@DriverFieldValues{#3}}%
          \changefield{##1}%
        \fi}%
      \processcommalist[#1]\docommando
   \else
     \writestatus\m!fields{pass fieldname, setupgroup, values and default}%
   \fi}

\def\doclonefield[#1][#2][#3][#4]% parent children setupgroup values
  {\ifsecondargument
     \getfield{#1}%
\iftrialtypesetting\else
     \ifx\@@DriverFieldType\empty
       \writestatus\m!fields{unknown field #1}%
     \else
       \let\@@DriverFieldMode\fieldparentmode
      %\def\docommando##1{\addtocommalist{##1}\@@DriverFieldKids}%
       \def\docommando##1{\appendtocommalist{##1}\@@DriverFieldKids}%
       \processcommalist[#2]\docommando
       \changefield{#1}%
       \let\@@DriverFieldAutoParent\@@DriverFieldAuto
       \def\@@DriverFieldParent{#1}%
       \let\@@DriverFieldKids\empty
       \let\@@DriverFieldRoot\empty
       \let\@@DriverFieldMode\fieldchildmode
       \def\@@DriverFieldFree{0}%
       \def\@@DriverFieldAuto{0}%
       \doifsomething{#3}{\edef\@@DriverFieldGroup{#3}}%
       \doifsomething{#4}{\edef\@@DriverFieldValues{#4}}%
       \def\docommando##1%
         {\ifcase\@@DriverFieldAutoParent\else
            \setmainfieldkid{\@@DriverFieldParent}{##1}%
          \fi
          \changefield{##1}}%
       \processcommalist[#2]\docommando
     \fi
\fi
   \else
     \writestatus\m!fields{pass parent field and clones}%
   \fi}

\def\clonefield
  {\doquadrupleempty\doclonefield}

\def\docopyfield[#1][#2]% parent children
  {\ifsecondargument
     \getfield{#1}%
\iftrialtypesetting\else
     \ifx\@@DriverFieldType\empty
       \writestatus\m!fields{unknown field #1}%
     \else
       \let\@@DriverFieldMode\fieldparentmode
      %\def\docommando##1{\addtocommalist{##1}\@@DriverFieldKids}%
       \def\docommando##1{\appendtocommalist{##1}\@@DriverFieldKids}%
       \processcommalist[#2]\docommando
       \changefield{#1}%
       \let\@@DriverFieldAutoParent\@@DriverFieldAuto
       \def\@@DriverFieldParent{#1}%
       \let\@@DriverFieldKids\empty
       \let\@@DriverFieldRoot\empty
       \let\@@DriverFieldMode\fieldcopymode
       \def\@@DriverFieldFree{0}%
       \def\@@DriverFieldAuto{0}%
       \def\docommando##1%
         {\ifcase\@@DriverFieldAutoParent\else
            \setmainfieldkid{\@@DriverFieldParent}{##1}%
          \fi
          \changefield{##1}}%
       \processcommalist[#2]\docommando
     \fi
\fi
   \else
     \writestatus\m!fields{pass parent field and copies}%
   \fi}

\def\copyfield{\dodoubleempty\docopyfield}

\unexpanded\def\field   {\dotripleempty\dofield[\dohandlefield]}
\unexpanded\def\fitfield{\dotripleempty\dofield[\dohandlefitfield]}

\def\dofield[#1][#2][#3]%
  {\iffirstargument
     \bgroup
     \getfield{#2}%
     \ifsecondargument
       \def\@@DriverFieldLabel{#3}%
     \else
       \let\@@DriverFieldLabel\@@DriverFieldName
     \fi
     \ifx\@@DriverFieldType\empty
       \writestatus\m!fields{unknown field #2}%
     \else\ifcase\@@DriverFieldFree\relax
       \doifdefinedelse{\strippedcsname\setupfield\@@DriverFieldGroup}
         {\let\dosetupfield=#1\getvalue{\strippedcsname\setupfield\@@DriverFieldGroup}}
         {#1[\@@DriverFieldName][\v!label,\v!frame,\v!horizontal][][][]}%
\iftrialtypesetting\else
       \def\@@DriverFieldFree{1}%
       \changefield{#2}%
\fi
     \else\ifcase\@@DriverFieldAuto\relax
     % \writestatus\m!fields{field #2 already typeset}%
     \else
     % \writestatus\m!fields{field #2 automatically copied}%
       \nextsystemfield
       \copyfield[\@@DriverFieldName][\currentsystemfield]%
       \dotripleempty\dofield[#1][\currentsystemfield][#3]% get the if's right
     \fi\fi\fi
     \egroup
   \fi}

\def\typesetfield
  {\useJSscripts[fld]%
   \ifx\@@DriverFieldRoot\empty \else
     \let\@@SavedFieldName\@@DriverFieldName
     \getfield\@@DriverFieldRoot
     \ifcase\@@DriverFieldFree\relax
       \dosetfieldstatus\@@DriverFieldMode\@@DriverFieldParent\@@DriverFieldKids\@@DriverFieldRoot
       \dopresetrecord
\iftrialtypesetting\else
       \def\@@DriverFieldFree{1}%
       \changefield\@@DriverFieldName
\fi
     \fi
     \getfield\@@SavedFieldName
   \fi
   \ifx\@@DriverFieldKids\empty
     \donefalse
   \else
     \donetrue
   \fi
   \ifdone
     \let\@@DriverFieldParent\@@DriverFieldName
    %\addtocommalist\@@DriverFieldParent\@@DriverFieldKids
     \appendtocommalist\@@DriverFieldParent\@@DriverFieldKids
     \dosetfieldstatus\@@DriverFieldMode\@@DriverFieldParent\@@DriverFieldKids\@@DriverFieldRoot
     \dopresetfield
     \let\@@DriverFieldMode\fieldchildmode
   \fi
   \dosetfieldstatus\@@DriverFieldMode\@@DriverFieldParent\@@DriverFieldKids\@@DriverFieldRoot
   \dopresetfield}

\def\dopresetfield
  {\iftrialtypesetting\else\iflocation\getvalue{preset\@@DriverFieldType  field}\fi\fi}

\def\dopresetrecord
  {\iftrialtypesetting\else\iflocation\getvalue{preset\@@DriverFieldType record}\fi\fi}

\def\dodefinethefieldset[#1][#2]%
  {\dodefinefieldset{#1}{#2}}

\def\definefieldset%
  {\dodoubleargument\dodefinethefieldset}

\def\normaldodosetupfield[#1][#2][#3][#4][#5]%
  {\doifdefinedelse{\strippedcsname\setupfield#1}
     {\pushmacro\dosetupfield
      \def\dosetupfield[##1][##2][##3][##4][##5]%
        {\setvalue{\strippedcsname\setupfield#1}{\dosetupfield[#1][##2,#2][##3,#3][##4,#4][##5,#5]}}%
      \getvalue{\strippedcsname\setupfield#1}%
      \popmacro\dosetupfield}
     {\setvalue{\strippedcsname\setupfield#1}{\dosetupfield[#1][#2][#3][#4][#5]}}}

\let\dodosetupfield\normaldodosetupfield

\def\donosetupfield[#1][#2][#3][#4][#5]%
  {\setvalue{\strippedcsname\setupfield#1}{\dosetupfield[#1][#2][#3][#4][#5]}}

\def\dosetupfield[#1][#2][#3][#4][#5]%
  {\iffifthargument
     \def\docommando##1{\dodosetupfield[##1][#2][#3][#4][#5]}%
     \processcommalist[#1]\docommando
   \else\ifthirdargument
     \def\docommando##1{\dodosetupfield[##1][#2][][][#3]}%
     \processcommalist[#1]\docommando
   \else\ifsecondargument
     \doifelse{#2}\v!reset
       {\def\docommando##1{\donosetupfield[#1][][][][]}}
       {\def\docommando##1{\dodosetupfield[##1][][][][#2]}}%
     \processcommalist[#1]\docommando
   \else\iffirstargument
     \def\docommando##1{\dodosetupfield[##1][][][][]}%
     \processcommalist[#1]\docommando
   \else
     \writestatus\m!fields{provide either 1, 2, 3 or 5 arguments}%
   \fi\fi\fi\fi}

\def\setupfield
  {\doquintupleempty\dosetupfield}

\def\dosetupfields[#1][#2][#3][#4]%
  {\ifsecondargument
     \def\dodosetupfield[##1][##2][##3][##4][##5]%
       {\doifdefinedelse{\strippedcsname\setupfield##1}
          {\def\dosetupfield[####1][####2][####3][####4][####5]%
             {\setvalue{\strippedcsname\setupfield##1}{\dosetupfield[##1][#1,####2,##2][#2,####3,##3][#3,####4,##4][#4,####5,##5]}}%
           \getvalue{\strippedcsname\setupfield##1}}
          {\setvalue{\strippedcsname\setupfield##1}{\dosetupfield[##1][#1,##2][#2,##3][#3,##4][#4,##5]}}}%
   \else\iffirstargument
     \doifelse{#1}\v!reset
       {\resetfields}
       {\setupfields[][][][#1]}% checken
   \else
     \writestatus\m!fields{provide either 1 or 4 arguments}%
   \fi\fi}

\def\setupfields
  {\doquadrupleempty\dosetupfields}

\def\resetfields
  {\let\dodosetupfield\normaldodosetupfield}

% \setupfields[\v!reset]

% opties: veld, label, kader, vertikaal/horizontaal

\newif\ifShowFieldLabel
\newif\ifShowFieldFrame
\newif\ifVerticalField
\newif\ifHorizontalField

% way to slow/complicated, we need some simple alternative
% as well

\def\dohandlefield[#1][#2][#3][#4][#5]%
  {\presetlocalframed[\??fd]%
   \processallactionsinset
     [#2]
     [      \v!reset=>\ShowFieldLabelfalse\ShowFieldFramefalse
                      \HorizontalFieldfalse\VerticalFieldfalse,
            \v!label=>\ShowFieldLabeltrue,
            \v!frame=>\ShowFieldFrametrue,
       \v!horizontal=>\HorizontalFieldtrue,
         \v!vertical=>\VerticalFieldtrue]%
   \ifVerticalField
     \getparameters[\??fd]
       [\c!distance=\!!zeropoint,\c!inbetween=\vskip\@@localoffset,
        \c!align=\v!right,\c!width=20em]%
   \else\ifHorizontalField
     \getparameters[\??fd]
       [\c!distance=\@@localoffset,\c!inbetween=,\c!align=\c!left,
        \c!height=10ex]%
   \else
     \getparameters[\??fd]
       [\c!distance=\!!zeropoint,\c!inbetween=,\c!align=\c!left]%
   \fi\fi
   \getparameters[\??fd]
     [\c!n=,\c!before=,\c!after=\vss,\c!style=,\c!color=,#3]%
   \reshapeframeboxfalse % else ugly spacing
   \ifShowFieldFrame
     \localframed[\??fd][\c!strut=\v!no,\c!align=]\bgroup
   \else
     \vbox\bgroup
   \fi
     \dontcomplain
     \ifShowFieldLabel
       \setbox0\hbox
         {\reshapeframeboxtrue % else wrong dimensions
          \framed
            [\c!style=,\c!color=,\c!align=\c!right,#4]
            {\@@DriverFieldLabel}}%
     \fi
     \setbox2\hbox
       {\reshapeframeboxtrue % else wrong dimensions
        \ifVerticalField
          \setupframed[\c!height=6ex,\c!width=\hsize]%
        \else\ifHorizontalField
          \setupframed[\c!height=\vsize,\c!width=20em]%
        \else
          \setupframed[\c!height=2cm,\c!width=2cm]%
        \fi\fi
        \framed
          [\c!align=\v!right,\c!strut=\v!no,#5]
          {\getparameters
             [\??fd]
             [\c!color=,\c!style=,\c!align=\v!right,\c!option=,
              \c!clickin=,\c!clickout=,\c!regionin=,\c!regionout=,
              \c!afterkey=,\c!format=,\c!validate=,\c!calculate=,
              \c!focusin=,\c!focusout=,
              \c!fieldoffset=\!!zeropoint,\c!fieldbackgroundcolor=,
              \c!fieldframecolor=,\c!fieldlayer=\@@iafieldlayer,#5]%
            \scratchdimen\framedwidth \edef\@@DriverFieldWidth {\the\scratchdimen}%
            \scratchdimen\framedheight\edef\@@DriverFieldHeight{\the\scratchdimen}%
            \vfill
            \hbox{\lower\@@fdfieldoffset\hbox{\typesetfield}}
            \vss}}%
     \ifShowFieldLabel
       \ifVerticalField
         \vbox
           {\copy0
            \@@fdinbetween
            \copy2}%
       \else
         \hbox
           {\vbox \ifdim\ht2>\ht0 to \ht2 \fi
              {\@@fdbefore
               \copy0
               \@@fdafter}%
            \hskip\@@fddistance
            \vbox \ifdim\ht0>\ht2 to \ht0 \fi
              {\@@fdbefore
               \box2
               \@@fdafter}}%
       \fi
     \else
       \box2
     \fi
   \egroup}

\chardef\fitfieldmode\plusone % 3 = best

\def\dohandlefitfield[#1][#2][#3][#4][#5]% alleen check
  {\presetlocalframed[\??fd]%
   \localframed
     [\??fd]
     [\c!n=1024, % beware: weblink plug in truncates
      \c!strut=\v!no,\c!color=,\c!style=,\c!option=,
      \c!clickin=,\c!clickout=,\c!regionin=,\c!regionout=,
      \c!focusin=,\c!focusout=,
      \c!afterkey=,\c!format=,\c!validate=,\c!calculate=,
      \c!fieldoffset=\!!zeropoint,\c!fieldbackgroundcolor=,
      \c!fieldframecolor=,\c!fieldlayer=\@@iafieldlayer,#5,\c!align=]
     {\dogetcommacommandelement1\from\@@DriverFieldValues\to\@@DriverFieldValue
      \ifx\@@DriverFieldValue\empty
        \let\@@DriverFieldValue\@@DriverFieldDefault
      \fi
      \dopresetfieldsymbol\@@DriverFieldValue
      \setbox\scratchbox\hbox{\dogetfieldsymbol\@@DriverFieldValue}%
      \scratchdimen\wd\scratchbox \edef\@@DriverFieldWidth {\the\scratchdimen}%
      \scratchdimen\ht\scratchbox \edef\@@DriverFieldHeight{\the\scratchdimen}%
      \ifcase\fitfieldmode
        \typesetfield
      \or % 1 = ignore depth (original, assumed no depth, actually a bug)
        \vbox to \ht\scratchbox{\vfill\hbox to \wd\scratchbox{\typesetfield\hfill}\vss}%
      \or % 2 = add depth to height, but no depth in result
        \advance\scratchdimen\dp\scratchbox \edef\@@DriverFieldHeight{\the\scratchdimen}%
        \vbox to \ht\scratchbox{\vfill\hbox to \wd\scratchbox{\typesetfield\hfill}\vss}%
      \or % 3 = add depth to height, and apply depth to result
        \advance\scratchdimen\dp\scratchbox \edef\@@DriverFieldHeight{\the\scratchdimen}%
        \hbox to \wd\scratchbox{\lower\dp\scratchbox\hbox{\typesetfield}\hfill}%
      \fi}}

%D Common stuff

\newcounter\nofsystemfields

\def\nextsystemfield
  {\doglobal\increment\nofsystemfields
   \def\currentsystemfield{sys::\nofsystemfields}}

%D An example:

\def\fillinfield
  {\dosingleempty\dofillinfield}

\def\dofillinfield[#1]#2%
  {\dontleavehmode
   \hbox
     {\forgetall
      \setupfields[\v!reset]%
      \nextsystemfield
      \useJSscripts[ans]%
      \doifelsenothing{#1}
        {\def\therightanswer{#2}}
        {\def\therightanswer{#1}}%
      \setbox0\hbox{#2}%
      \setbox2\hbox{\therightanswer}%
      \dimen0=\ifdim\wd0>\wd2 \wd0 \else \wd2 \fi
      \advance\dimen0 .2em
      \definefield
        [\currentsystemfield][line][systemfield]%
      \setupfield
        [systemfield]
        [\c!n=1024, % beware: weblink plugin truncates
         \c!location=\v!low,\c!strut=\v!yes,\c!fieldoffset=0pt,
         \c!height=1.2\openlineheight,\c!width=\dimen0,\c!offset=\v!overlay,
         \c!style=,\c!align=\v!middle,\c!frame=\v!off,
         \c!color=red,\c!fieldbackgroundcolor=\s!white,\c!fieldframecolor=blue,
         \c!validate=JS(Check_Answer{\currentsystemfield,\therightanswer})]%
      \switchtobodyfont
        [\c!small]%
      \hbox to \wd0
        {\copy0\hskip-\wd0\hss\field[\currentsystemfield]\hss}}}

%D and another one:

\def\tooltip
  {\dosingleempty\dotooltip}

\def\dotooltip[#1]#2#3%
  {\bgroup
   \setupfields[\v!reset]%
   \useJSscripts[fld]%
   \setbox0\hbox
     {\dontcomplain
      \nextsystemfield
      \setbox0\hbox{#2}%
      \definesymbol
        [\currentsystemfield:txt]
        [{\inframed[\c!frame=\v!off,\c!background=\v!screen]{#3}}]%
      \setbox2\hbox{\symbol[\currentsystemfield:txt]}%
      \definefield
        [\currentsystemfield:txt][check]
        [dummy][\currentsystemfield:txt][\currentsystemfield:txt]%
      \setupfield
        [dummy]
        [\c!frame=\v!off,
         \c!regionout=JS(Hide_Field{\currentsystemfield:txt}),
         \c!option=\v!hidden]%
      \hbox to \zeropoint
         {\dimen0\wd2\advance\dimen0 -\wd0
          \doifelse{#1}\v!left
            {\hskip-\dimen0}
            {\doif{#1}\v!middle
               {\hskip-.5\dimen0}}%
          \lower\openlineheight\hbox to \zeropoint
            {\fitfield[\currentsystemfield:txt]}}%
      \dimen0=\ifdim\wd0=\zeropoint 3em\else\wd0\fi
      \definesymbol
        [\currentsystemfield:but]
        [{\framed[\c!height=2ex,\c!width=\dimen0,\c!frame=\v!off]{}}]%
      \definefield
        [\currentsystemfield:but][push]
        [dummy][\currentsystemfield:but][\currentsystemfield:but]%
      \setupfield
        [dummy]
        [\c!frame=\v!off,
         \c!option=,
         \c!regionin=JS(Vide_Field{\currentsystemfield:txt}),
         \c!regionout=JS(Hide_Field{\currentsystemfield:txt}),
         \c!fieldlayer=\@@iafieldlayer]%
       \lower2ex\hbox to \zeropoint
         {\fitfield[\currentsystemfield:but]}%
      #2}%
  \ht0\strutht\dp0\strutdp\box0
  \egroup}

%D And one more:

\def\definefieldstack
  {\dotripleargument\dodefinefieldstack}

\def\dodefinefieldstack[#1][#2][#3]% name, symbols, settings
  {\doifundefined{fieldstack:#1}
     {\setgvalue{fieldstack:#1}{\dodofieldstack[#1][#2][#3]}}}

\def\dodofieldstack[#1][#2][#3]% start=n, 0 == leeg
  {\bgroup
   \getparameters[\??fd][\c!start=1,#3]%
   \setupfields[\v!reset]%
   \definesymbol[\v!empty][]%
   \useJSscripts[fld][FieldStack]%
   \newcounter\stackedfieldnumber
   \def\dododofieldstack##1%
     {\increment\stackedfieldnumber
      \ifnum\stackedfieldnumber=\@@fdstart\relax
        \definefield[#1:\stackedfieldnumber][check][#1][##1,\v!empty][##1]%
      \else
        \definefield[#1:\stackedfieldnumber][check][#1][##1,\v!empty][\v!empty]%
      \fi}%
   \processcommalist[#2]\dododofieldstack
   \setupfield[#1][\v!reset]% added
   \setupfield[#1][\c!option=\v!readonly,#3]% #3 swapped
   \newcounter\stackedfieldnumber
   \def\dododofieldstack##1%
     {\doglobal\increment\stackedfieldnumber
      \fitfield[#1:\stackedfieldnumber]\egroup\bgroup}%
   \startoverlay
     \bgroup
     \globalprocesscommalist[#2]\dododofieldstack
     \egroup
   \stopoverlay
   \egroup}

\def\dofieldstack[#1][#2][#3]%
  {\ifsecondargument
     \dodefinefieldstack[#1][#2][#3]\fieldstack[#1]%
   \else
     \getvalue{fieldstack:#1}\setgvalue{fieldstack:#1}{[#1]}%
   \fi}

\def\fieldstack
  {\dotripleempty\dofieldstack}

%D When submitting a form, we need to tell the driver module
%D that we want \FDF\ or \HTML.

\def\setupforms
  {\dodoubleargument\getparameters[\??fr]}

\def\checksubmitform#1%
  {\setsubmitoutputformat\@@frmethod}

\setexecutecommandcheck {submitform} \checksubmitform

\setupforms
  [\c!method=HTML]

\protect \endinput
