Exercise 30: Simple Form Class with Check Boxes (Solution)
This is a solution to Exercise 30.
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{simple-form}[2014/10/13]
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions
\LoadClass[a4paper,12pt]{article}
\RequirePackage{etoolbox}
\RequirePackage{wasysym}
\newcommand*{\form@fillin}[2]{%
\makebox[#1][l]{\rlap{#2}\hrulefill}%
}
\newcommand*{\form@checked}{\XBox}
\newcommand*{\form@unchecked}{\Square}
\newcommand*{\form@layout@checkbox}[2]{#1 #2}
\newcommand*{\form@layout@fillin}[3]{#3: \form@fillin{#1}{#2}}
\newcommand*{\reset@element}[2]{%
\csdef{#2@#1}{\form@unchecked}%
}
\newcommand*{\set@element}[2]{%
\ifcsdef{#2@#1}%
{\csdef{#2@#1}{\form@checked}}%
{%
\ClassError{simple-form}{Unknown #2 `#1'}{}%
}%
}
\newcommand*{\use@element}[2]{%
\ifcsdef{#2@#1}{\csuse{#2@#1}}{\form@unchecked}%
}
\newcommand*{\for@block}[3]{%
\ifcsdef{@#2list}%
{%
\expandafter\@for\expandafter
#1\expandafter:\expandafter=\csname @#2list\endcsname\do{#3}%
}%
{%
\ClassError{simple-form}{Unknown block `#2'}{}%
}%
}
\newcommand*{\reset@block}[1]{%
\for@block{\this@element}{#1}{%
\reset@element{\expandafter\@firstoftwo\this@element}{#1}%
}%
}
\newcount\form@columncount
\newcommand*{\form@block}[2]{%
\def\form@columnargs{}%
\form@columncount=0\relax
\loop
\appto\form@columnargs{l}%
\advance\form@columncount by 1\relax
\ifnum\form@columncount<#2
\repeat
\def\form@precolumn{}%
\form@columncount=1\relax
\begin{tabular}{\form@columnargs}
\for@block\this@element{#1}%
{%
\global\let\this@element\this@element
\form@precolumn
\form@layout@checkbox
{\use@element{\expandafter\@firstoftwo\this@element}{#1}}%
{\expandafter\@secondoftwo\this@element}%
\global\advance\form@columncount by 1\relax
\ifnum\form@columncount>#2\relax
\global\form@columncount=1\relax
\gdef\form@precolumn{\\}%
\else
\gdef\form@precolumn{&}%
\fi
}%
\end{tabular}%
}
\newcommand*{\@name}{}
\newcommand*{\name}[1]{%
\renewcommand*{\@name}{#1}%
}
\newcommand*{\@genderlist}{{male}{Male},{female}{Female}}
\newcommand*{\gender}[1]{%
\reset@block{gender}%
\set@element{#1}{gender}%
}
\newcommand*{\@projectlist}{%
{cookies}{Mind-Controlling Cookies},%
{cakes}{Telepathic Cakes},%
{chocolates}{Exploding Chocolates},%
{raygun}{Ray Gun}%
}
\newcommand*{\project}[1]{%
\reset@block{project}%
\set@element{#1}{project}%
}
\newcommand*{\@icecreamlist}{%
{vanilla}{Vanilla},%
{mint}{Mint},%
{toffee}{Toffee},%
{fudge}{Fudge},%
{guarana}{Guaran\'a},%
{strawberry}{Strawberry},%
{raspberry}{Raspberry Ripple},%
{chilli}{Chilli},%
{other}{Other}%
}
\reset@block{icecream}%
\newcommand*{\icecream}[1]{%
\set@element{#1}{icecream}%
}
\newcommand{\makeform}{%
\form@layout@fillin{8em}{\@name}{Name}\qquad
\form@layout@fillin{12em}{\@date}{Date}%
\par
\bigskip
\par
\form@layout@checkbox{\gender@male}{Male}\qquad
\form@layout@checkbox{\gender@female}{Female}%
\par
\bigskip
\par
Which project would you like to enrol on? (Tick one box.)
\par
\begin{center}
\form@block{project}{2}
\end{center}
\par
Which ice-cream flavours do you like? (Tick all that apply.)
\par
\begin{center}
\form@block{icecream}{3}
\end{center}
}
\endinput
The more adventurous part requires only a minor modification. First
new commands in the same style as \@name and
\name:
\newcommand*{\@othericecream}{}
\newcommand*{\othericecream}[1]{%
\icecream{other}%
\renewcommand*{\@othericecream}{#1}%
}
Note that in addition to setting \@othericecream,
\othericecream also performs \icecream{other}.
The fill-in area now just needs to be added to the text assigned to
the other element:
\newcommand*{\@icecreamlist}{%
{vanilla}{Vanilla},%
{mint}{Mint},%
{toffee}{Toffee},%
{fudge}{Fudge},%
{guarana}{Guaran\'a},%
{strawberry}{Strawberry},%
{raspberry}{Raspberry Ripple},%
{chilli}{Chilli},%
{other}{\form@layout@fillin{6em}{\@othericecream}{Other}}%
}
Download simple-form.cls and example document simple-form-exercise.tex or simple-form-exercise.pdf.
