diff options
Diffstat (limited to 'macros/latex/contrib/sdaps/sdapsclassic.dtx')
-rw-r--r-- | macros/latex/contrib/sdaps/sdapsclassic.dtx | 1134 |
1 files changed, 1134 insertions, 0 deletions
diff --git a/macros/latex/contrib/sdaps/sdapsclassic.dtx b/macros/latex/contrib/sdaps/sdapsclassic.dtx new file mode 100644 index 0000000000..d2f4002800 --- /dev/null +++ b/macros/latex/contrib/sdaps/sdapsclassic.dtx @@ -0,0 +1,1134 @@ +% \iffalse meta-comment +% +% Copyright (C) 2015 by Benjamin Berg <benjamin@sipsolutions.net> +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Benjamin Berg. +% +% \fi +% +% \iffalse +%<*driver> +\ProvidesFile{sdapsclassic.dtx} +%</driver> +%<class>\NeedsTeXFormat{LaTeX2e}[1999/12/01] +%<class>\ProvidesClass{sdapsclassic} +%<*class> + [2015/08/02 v0.1 Initial version of SDAPS classic class] +%</class> +% +%<*driver> +\documentclass{ltxdoc} +%\EnableCrossrefs +\CodelineIndex +\RecordChanges +\begin{document} + \DocInput{sdapsclassic.dtx} +\end{document} +%</driver> +% \fi +% +% \CheckSum{0} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% +% \changes{v0.1}{2015/01/14}{Initial version} +% +% \GetFileInfo{sdapsclassic.dtx} +% +% \DoNotIndex{\newcommand,\newenvironment} +% +% +% \title{The \textsf{sdapsclassic} package\thanks{This document +% corresponds to \textsf{sdapsclassic}~\fileversion, dated \filedate.}} +% \author{Benjamin Berg \\ \texttt{benjamin@sipsolutions.net}} +% +% \maketitle +% +% \section{Documentation} +% +% Please refer to \url{https://sdaps.org/class-doc} for documentation. +% +% \StopEventually{\PrintChanges\PrintIndex} +% +% \section{Implementation} +% +% This package uses the \LaTeX3 language internally, so we need to enable it. +% \begin{macrocode} +% We need at least 2011-08-23 for \keys_set_known:nnN +\RequirePackage{expl3}[2011/08/23] +%\RequirePackage{xparse} +\ExplSyntaxOn + +\RequirePackage{sdapsbase} +\RequirePackage{sdapslayout} + +% We use verbatim to grab the content of the questionnaire envorinment into a +% temporary file. This way we can input it multiple times for repeating the +% the content. + +\RequirePackage{verbatim} + + +% \end{macrocode} +% Now a lot of LaTeX 2e stuff that is basically just a copy of the old class. +% Would be nicer to redo this with xparse, but what the heck. +% \begin{macrocode} + +\RequirePackage{scrkbase} + + +%------------------------------------------------------------------------------- +% option processing +%------------------------------------------------------------------------------- + +% Whether the main SDAPS program will be used +\DeclareOption{disable_recognition}{\bool_gset_false:N\g_sdaps_recognition_bool} + +% Whether sdaps should print the questionnaire id +\DeclareOption{no_print_questionnaire_id}{\bool_gset_false:N\g_sdaps_print_questionnaire_id_bool\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{}}} +\DeclareOption{print_questionnaire_id}{\bool_gset_true:N\g_sdaps_print_questionnaire_id_bool\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{NONE}}} + +% pass unknown options to scrartcl +\DeclareOption*{\PassOptionsToClass{\CurrentOption}{scrartcl}} + +% We need this if right now as it is set by sdaps.opt. +\newif\if@sdaps@draft\@sdaps@drafttrue +\DeclareOption{final}{\@sdaps@draftfalse} + +\tl_gset:Nn\g_sdaps_style_tl{qr}% + + +\DefineFamily{SDAPS} +\DefineFamilyMember{SDAPS} +\DefineFamilyKey{SDAPS}{sdaps_style}[qr]% +{% + \KOMA@set@ncmdkey{sdaps_style}{@tempa}{% + {code128}{0},% + {custom}{1},% + {qr}{2}% + }{#1}% + \ifcase \@tempa\relax + \tl_gset:Nn\g_sdaps_style_tl{code128}% + \or + \tl_gset:Nn\g_sdaps_style_tl{custom}% + \or + \tl_gset:Nn\g_sdaps_style_tl{qr}% + \fi +} + +\DefineFamilyKey{SDAPS}{twoside_barcode}[both]% +{% + \KOMA@set@ncmdkey{twoside_barcode}{@tempa}{% + {both}{0},% + {front}{1},% + {back}{2}% + }{#1}% + \ifcase \@tempa\relax + \tl_gset:Nn\g_sdaps_twoside_barcode_tl{both}% + \or + \tl_gset:Nn\g_sdaps_twoside_barcode_tl{front}% + \or + \tl_gset:Nn\g_sdaps_twoside_barcode_tl{back}% + \fi +} + +\DefineFamilyKey{SDAPS}{checkmode}[checkcorrect]% +{% + \KOMA@set@ncmdkey{checkmode}{@tempa}{% + {checkcorrect}{0},% + {check}{1},% + {fill}{2}% + }{#1}% + \ifcase \@tempa\relax + \tl_gset:Nn\g_sdaps_checkmode_tl{checkcorrect}% + \or + \tl_gset:Nn\g_sdaps_checkmode_tl{check}% + \or + \tl_gset:Nn\g_sdaps_checkmode_tl{fill}% + \fi +} + + +\DefineFamilyKey{SDAPS}{globalid}[] +{ + \tl_gset:Nn \g_sdaps_global_id_tl { #1 } +} + +\DefineFamilyKey{SDAPS}{globalidlabel}[] +{ + \tl_gset:Nn \g_sdaps_global_id_label_tl { #1 } +} + +% Set default options for scrartcl. Is this done correctly? +\PassOptionsToClass{headings=small}{scrartcl} +\PassOptionsToClass{twoside}{scrartcl} +% Well, I don't think that this worked in the past ... +%\PassOptionsToClass{10pt}{scrartcl} + +\seq_new:N \g_sdaps_questionnaire_ids_seq +\seq_gset_from_clist:Nn \g_sdaps_questionnaire_ids_seq {{}} + +% process given options +\FamilyProcessOptions{SDAPS}\relax + +%------------------------------------------------------------------------------- +% load base-class +%------------------------------------------------------------------------------- +\LoadClass{scrartcl} + + +%------------------------------------------------------------------------------- +% required packages +%------------------------------------------------------------------------------- +% geometry package +\RequirePackage{geometry} +% Corner mark postions plus 2mm, except for the header +\geometry{top=12mm} +\geometry{bottom=14mm} +\geometry{hmargin=12mm} +\geometry{includeheadfoot} +\geometry{headheight=\baselineskip} +\geometry{headsep=\baselineskip} +% QR code size plus 2mm +\geometry{footskip=12mm} + +% ifthen package +\RequirePackage{ifthen} + +% fontenc package +\RequirePackage[T1]{fontenc} + +% color +\RequirePackage{color} + +% Symbols (boxes) +\RequirePackage{amssymb} + +% For writing out the page number of boxes +\RequirePackage{refcount} + +% Defines the LastPage label +\RequirePackage{lastpage} + +% Environment creation that gets the body as a macro +\RequirePackage{environ} + +% headers and footers +\usepackage{scrlayer-scrpage} +\clearpairofpagestyles +\chead*{\@title} +\cfoot*{\sdapspagemark} + +% hyperrefs +\RequirePackage{url} +\RequirePackage{hyperref} +\hypersetup{% + breaklinks,% + baseurl = http://,% + pdfborder = 0 0 0,% + pdfpagemode = UseNone,% + pdfcreator = \LaTeX{} with `sdaps' class,% + pdfproducer = \LaTeX{} +} + +% graphics +\RequirePackage{graphicx} + +% Section formatting +\RequirePackage{sectsty} + +% table of fixed width +\RequirePackage{tabularx} + +% Babel (needed for the translator) +\RequirePackage{babel} + +% Translation +\RequirePackage{translator} +\usedictionary{translator-sdaps-dictionary} + + +% Execute sdaps.opt file to allow SDAPS to override any options +\InputIfFileExists{sdaps.opt}{}{} + + +%------------------------------------------------------------------------------- +% class definition +%------------------------------------------------------------------------------- +% minimal base settings + +\setlength\lineskip{1\p@} +\setlength\normallineskip{1\p@} +\renewcommand\baselinestretch{} +\setlength{\parindent}{0pt} +\setlength{\parskip}{1.0ex \@plus 1.5ex \@minus -0.25ex} +%\setlength{\parskip}{0pt} +\setlength\columnsep{10\p@} +\setlength\columnseprule{0\p@} +% Redefine the spacings for \subsection ie. \questions. +\renewcommand\section{\@startsection {section}{1}{\z@}% + {-\parskip}% + {\parskip}% + {\normalfont\Large\bfseries\SS@sectfont}} +\renewcommand\subsection{ + \@startsection{subsection}{2}{\z@}% + {0.5\parskip}% + {0.25\parskip}% These are deleted again for questions + {}% +} +\pagestyle{scrheadings} +\pagenumbering{arabic} +\raggedbottom +%\flushbottom +\onecolumn + +% +% Fonts for the "classic" class +% + +%common font settings +\newkomafont{questionfont}{} +\newkomafont{choicefont}{} +%singlemark +\newkomafont{singlemarkquestionfont}{\usekomafont{questionfont}} +\newkomafont{singlemarkchoicefont}{\usekomafont{choicefont}} +%markgroup +\newkomafont{marklinequestionfont}{\usekomafont{questionfont}} +\newkomafont{marklinechoicefont}{\usekomafont{choicefont}} +%choicequestion +\newkomafont{choiceitemfont}{\usekomafont{choicefont}} +%choicegroup +\newkomafont{choicegroupchoicefont}{\usekomafont{questionfont}} +\newkomafont{choicegrouplinefont}{\usekomafont{choicefont}} + + +% \end{macrocode} +% +% \subsection{A bunch of old commands} +% +% +% \begin{macrocode} + + +\providecommand{\addinfo}[2]{ + \sdaps_info_write:x{\unexpanded{#1}=\unexpanded{#2}} +} + +\newcommand\qid{\tl_use:N \g__sdaps_questionnaire_id_tl} + +\def\question#1{% + \tl_if_empty:nTF{#1}{ + \refstepcounter{subsection}% + \par% + } { + % #1 is nonempty + \subsection{\usekomafont{questionfont}\strut\ignorespaces#1}% + % This is extra spacing after the subsection is removed. By doing this we + % get exactly one \parskip + \nobreak% + \vspace{-0.25\parskip}% + \nobreak% + } +} + +\newenvironment{info}{% + \group_begin: + { + \par + % Prevent top skip glue from being inserted at the start of the page + \topskip=0pt + \noindent\hrule height 1pt% + \nobreak + \vspace{-\parskip} + \nobreak + \vspace{0.5ex} + \nobreak + } +}{% + \par + \nobreak + \vspace{-\parskip} + \nobreak + \vspace{0.5ex} + \nobreak + \noindent\hrule height 1pt + \group_end: +} + + +\definecolor{sectionbgcolor}{gray}{0.8} +\definecolor{sectionfgcolor}{gray}{0.0} + +\newcommand{\sectbox}[1]{% + \noindent\protect\colorbox{sectionbgcolor}{% + \@tempdima=\hsize + \advance\@tempdima by-2\fboxsep + \protect\parbox{\@tempdima}{% + \smallskip + \raggedright % extra commands here + \color{sectionfgcolor}\usekomafont{section}{#1} \smallskip + }% + }% +} + +\sectionfont{\sectbox} + +\setkomafont{disposition}{\normalfont} +\addtokomafont{section}{\bfseries\sffamily} + + + + + +% Not sure why we even had this ... +\def\smallskip{\vspace\smallskipamount} +\def\medskip{\vspace\medskipamount} +\def\bigskip{\vspace\bigskipamount} +\newskip\smallskipamount \smallskipamount=3pt plus 1pt minus 1pt +\newskip\medskipamount \medskipamount =6pt plus 2pt minus 2pt +\newskip\bigskipamount \bigskipamount =12pt plus 4pt minus 4pt + + + +\cs_generate_variant:Nn \sdaps_textbox_hstretch:nnnnn { nVVnn } +\cs_generate_variant:Nn \sdaps_textbox_hstretch:nnnnn { VVVnn } +\cs_generate_variant:Nn \int_set:Nn { NV } + + +% \end{macrocode} +% +% \subsection{The different question environments} +% +% +% \begin{macrocode} + + +\bool_new:N \g__sdaps_classic_have_section +\bool_gset_false:N \g__sdaps_classic_have_section +\cs_new_eq:NN\_sdapsclassic_origsection\section +\renewcommand{\section}[1]{ + \bool_if:NT \g__sdaps_classic_have_section { + \sdaps_qobject_end:n { section } + } + + \_sdapsclassic_origsection{#1} + \bool_gset_true:N \g__sdaps_classic_have_section + \sdaps_qobject_begin:nnn { section }{ Head }{ #1 } +} + +\cs_new_protected_nopar:Nn \sdaps_classic_ensure_section: { + \bool_if:NF \g__sdaps_classic_have_section { + % This is a bad hack to make the numbering start with zero + \int_gdecr:N \g__sdaps_object_id_int + \bool_gset_true:N \g__sdaps_classic_have_section + \sdaps_qobject_begin:nnn { section }{ Head }{ } + \sdaps_context_hook_end:n { \bool_gset_false:N \g__sdaps_classic_have_section } + } +} + + +\tl_new:N \l_sdaps_classic_textbox_var_tl +\tl_new:N \l_sdaps_classic_textbox_text_tl +\keys_define:nn { sdapsclassic / textbox } +{ + var .tl_set:N = \l_sdaps_classic_textbox_var_tl, + text .tl_set:N = \l_sdaps_classic_textbox_text_tl, +} + +% With star non-expanding, without start expanding +\providecommand{\textbox}{\@ifstar + {\bool_set_false:N \l_tmpa_bool \_sdaps_classic_textbox } + {\bool_set_true:N \l_tmpa_bool \_sdaps_classic_textbox } +} + +\providecommand{\_sdaps_classic_textbox}[3][] +{ + \keys_set:nn { sdapsclassic / textbox } { #1 } + + \sdaps_classic_ensure_section: + \question{#3} + + \tl_if_empty:NTF \l_sdaps_classic_textbox_text_tl { + \sdaps_qobject_begin:nnn { textbox } { Text } { #3 } + } { + \sdaps_qobject_begin:nnV { textbox } { Text } \l_sdaps_classic_textbox_text_tl + } + + \bool_if:NTF \l_tmpa_bool { + \sdaps_textbox_vhstretch:Vnn \l_sdaps_classic_textbox_var_tl { #2 } { 1 } + }{ + \sdaps_textbox_vhstretch:Vnn \l_sdaps_classic_textbox_var_tl { #2 } { 0 } + } + + \sdaps_qobject_end:n { textbox } +} + +\newcounter{markcheckboxcount} +\setcounter{markcheckboxcount}{5} + + +\tl_new:N \l_sdaps_singlemark_var_tl +\int_new:N \l_sdaps_singlemark_count_int + +\keys_define:nn { sdaps / singlemark } +{ + var .tl_set:N = \l_sdaps_singlemark_var_tl, + + % 0 is equivalent to using the markcheckboxcount counter + count .int_set:N = \l_sdaps_singlemark_count_int, + count .initial:n = 0, +} + +\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_prepare:NN +{ + \group_begin: + \sdaps_context_begin_local: + \sdaps_context_disable_writing: + \bool_if:NT #1 { + \sdaps_context_append:nn { singlechoice } { draw_check=true } + \sdaps_context_append:nn { multichoice } { draw_check=true } + } + \bool_if:NT #2 { + \sdaps_context_append:nn { singlechoice } { fill=black } + \sdaps_context_append:nn { multichoice } { fill=black } + } +} + +\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_single: +{ + \sdaps_checkbox_set_type:n { singlechoice } + \sdaps_checkbox:nn { } { } + \group_end: + \ignorespaces +} + +\cs_new_protected_nopar:Nn \_sdaps_classic_dummy_checkbox_multi: +{ + \sdaps_checkbox_set_type:n { multichoice } + \sdaps_checkbox:nn { } { } + \group_end: + \ignorespaces +} + +\providecommand{\checkbox}{ + \_sdaps_classic_dummy_checkbox_prepare:NN \c_false_bool \c_false_bool + \@ifstar + \_sdaps_classic_dummy_checkbox_single: + \_sdaps_classic_dummy_checkbox_multi: +} +\providecommand{\checkedbox}{% + \_sdaps_classic_dummy_checkbox_prepare:NN \c_true_bool \c_false_bool + \@ifstar + \_sdaps_classic_dummy_checkbox_single: + \_sdaps_classic_dummy_checkbox_multi: +} +\providecommand{\filledbox}{% + \_sdaps_classic_dummy_checkbox_prepare:NN \c_false_bool \c_true_bool + \@ifstar + \_sdaps_classic_dummy_checkbox_single: + \_sdaps_classic_dummy_checkbox_multi: +} +\providecommand{\correctedbox}{% + \_sdaps_classic_dummy_checkbox_prepare:NN \c_true_bool \c_true_bool + \@ifstar + \_sdaps_classic_dummy_checkbox_single: + \_sdaps_classic_dummy_checkbox_multi: +} + +\providecommand*{\singlemark}[4][]{% + \sdaps_classic_ensure_section: + + \group_begin: + + \keys_set:nn { sdaps / singlemark } { #1 } + + \int_compare:nNnT { \l_sdaps_singlemark_count_int } = { 0 } { + \int_set:Nn \l_sdaps_singlemark_count_int { \themarkcheckboxcount } + } + + \question{#2}% + + \sdaps_qobject_begin:nnn { singlemark } { range } { #2 } + + \sdaps_checkbox_set_type:n { singlechoice } + + \tl_if_empty:NF \l_sdaps_singlemark_var_tl { + \sdaps_qobject_append_var:V \l_sdaps_singlemark_var_tl + } + + \sdaps_range:nnn{lower}{0}{#3} + \sdaps_range:nnn{upper}{\int_use:N\l_sdaps_singlemark_count_int-1}{#4} + + \begin{tabularx}{\linewidth}{X*{\int_use:N\l_sdaps_singlemark_count_int}{c}X} + {\hfill\usekomafont{singlemarkchoicefont}\strut\ignorespaces#3} & + \int_step_inline:nnnn { 1 } { 1 } { \int_use:N\l_sdaps_singlemark_count_int } { \sdaps_checkbox:nn { _ ##1 } { ##1 } & } + {\usekomafont{singlemarkchoicefont}#4\hfill}\\% + \end{tabularx}% + + \sdaps_qobject_end:n { singlemark } + \group_end: +} + + +\providecommand*{\singlemarkother}[5][]{% + \sdaps_classic_ensure_section: + + \group_begin: + + \keys_set:nn { sdaps / singlemark } { #1 } + + \int_compare:nNnT { \l_sdaps_singlemark_count_int } = { 0 } { + \int_set:Nn \l_sdaps_singlemark_count_int { \themarkcheckboxcount } + } + + \question{#2}% + + \sdaps_qobject_begin:nnn { singlemark } { range } { #2 } + + \sdaps_checkbox_set_type:n { singlechoice } + + \tl_if_empty:NF \l_sdaps_singlemark_var_tl { + \sdaps_qobject_append_var:V \l_sdaps_singlemark_var_tl + } + + \sdaps_range:nnn{lower}{0}{#3} + \sdaps_range:nnn{upper}{\int_use:N\l_sdaps_singlemark_count_int-1}{#4} + \sdaps_answer:n{#5} + + \begin{tabularx}{\linewidth}{X*{\int_use:N\l_sdaps_singlemark_count_int}{c}XX} + {\hfill\usekomafont{singlemarkchoicefont}\strut\ignorespaces#3} & + \int_step_inline:nnnn { 1 } { 1 } { \int_use:N\l_sdaps_singlemark_count_int } { \sdaps_checkbox:nn { _ ##1 } { ##1 } & } + {\usekomafont{singlemarkchoicefont}#4} & + {\usekomafont{singlemarkchoicefont}\sdaps_checkbox:nn { _0 } { 0 } {} ~ #5\hfill}\\% + \end{tabularx}% + + \sdaps_qobject_end:n { singlemark } + \group_end: +} + +\dim_new:N \l__sdaps_classic_choiceitem_pad_dim +\int_new:N \l__sdaps_classic_choiceitem_cols_int +\int_new:N \l__sdaps_classic_choiceitem_col_int +\coffin_new:N \l__sdaps_classic_choicequestion_coffin + +\msg_new:nnn { sdapsclassic } { choicequestion_wrong_mode } { Mode~should~always~be~vertical~inside~a~choicequestion.\\ This~likely~means~that~the~choicequestion~contains~content~other~than~one~of~the~permissable~macros. } + +\msg_new:nnn { sdapsclassic } { choicequestion_no_text } { Textboxes~cannot~be~used~in~singlechoice~questions.\\ +It~is~currently~not~supported~to~use~text~answers~in~single~choice~questions.~Until~support~is~added~ +you~can~get~similar~results~by~temporarily~changing~the~checkbox~style. } + +\msg_new:nnn { sdapsclassic } { choicequestion_unknown_key } { The~key~'#1'~is~unknown.\\If~you~are~migrating~from~the~old~class~then~you~need~to~add~'cols='~to~specify~the~number~of~columns. } + +\tl_new:N \l_sdaps_choicquestion_type_tl +\tl_new:N \l_sdaps_choicequestion_var_tl +\tl_new:N \l_sdaps_choicequestion_text_tl +\int_new:N \l_sdaps_choicequestion_cols_int + +\keys_define:nn { sdaps / choicequestion } +{ + var .tl_set:N = \l_sdaps_choicequestion_var_tl, + text .tl_set:N = \l_sdaps_choicequestion_text_tl, + colsep .dim_set:N = \l_sdaps_choicequestion_colsep_dim, + colsep .initial:n = 6pt, + rowsep .dim_set:N = \l_sdaps_choicequestion_rowsep_dim, + rowsep .initial:n = 0pt, + cols .int_set:N = \l_sdaps_choicequestion_cols_int, + cols .initial:n = 3, + + type .choices:nn = { multichoice, singlechoice } { \tl_set:Nx \l_sdaps_choicquestion_type_tl { \l_keys_choice_tl } }, + type .initial:n = { multichoice }, + + singlechoice .meta:n = { type=singlechoice }, + multichoice .meta:n = { type=multichoice }, + + unknown .code:n = \msg_error:nnV { sdapsclassic } { choicequestion_unknown_key } \l_keys_key_tl +} + + +\tl_new:N \l_sdaps_choicequestion_choice_var_tl +\tl_new:N \l_sdaps_choicequestion_choice_val_tl +\tl_new:N \l_sdaps_choicequestion_choice_text_tl + +\keys_define:nn { sdaps / choicequestion / choice } +{ + var .tl_set:N = \l_sdaps_choicequestion_choice_var_tl, + val .tl_set:N = \l_sdaps_choicequestion_choice_val_tl, + text .tl_set:N = \l_sdaps_choicequestion_choice_text_tl, +} + + +\newenvironment{choicequestion}[2][]{ + \group_begin: + + \sdaps_classic_ensure_section: + + \tl_clear:N \l_sdaps_choicequestion_var_tl + \tl_clear:N \l_sdaps_choicequestion_text_tl + + \keys_set:nn { sdaps / choicequestion } { #1 } + + \question{#2}% + + % Setup the context + \tl_if_eq:VnTF \l_sdaps_choicquestion_type_tl { multichoice } { + \tl_set:Nn \l_tmpa_tl { Choice } + } { + \tl_set:Nn \l_tmpa_tl { Option } + } + \tl_if_empty:NTF \l_sdaps_choicequestion_text_tl { + \sdaps_qobject_begin:nVn { choicequestion } \l_tmpa_tl { #2 } + } { + \sdaps_qobject_begin:nVV { choicequestion } \l_tmpa_tl \l_sdaps_choicequestion_text_tl + } + + \sdaps_checkbox_set_type:V \l_sdaps_choicquestion_type_tl + + \tl_if_empty:NF \l_sdaps_choicequestion_var_tl { + \sdaps_qobject_append_var:V \l_sdaps_choicequestion_var_tl + } + + \dim_set:Nn \l__sdaps_classic_choiceitem_pad_dim { 1ex } + + \int_set:NV \l__sdaps_classic_choiceitem_cols_int \l_sdaps_choicequestion_cols_int + \int_set:Nn \l__sdaps_classic_choiceitem_col_int { 0 } + \coffin_clear:N \l__sdaps_classic_choicequestion_coffin + + % We have to be in vertical mode at this point. + \if_mode_vertical: + % Nothing + \else: + \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode } + \fi: + + % It is important to look like a paragraph, otherwise latex thinks there is + % nothing between the two question sections and doesn't insert a voluntary + % page break. + \the\everypar + + % Paragraph like spacing + \vspace{\parskip} + + \def\choicequestion_clubpenalty{\penalty\clubpenalty\def\choicequestion_clubpenalty{\relax}\def\choicequestion_clubpenalty{\penalty\widowpenalty}} + \def\choicequestion_widowpenalty{} +}{ + \if_mode_vertical: + % Nothing + \else: + \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode } + \fi: + + \choicequestion_widowpenalty + \vbox:n { \skip_horizontal:N \@totalleftmargin \box_use:N \l__sdaps_classic_choicequestion_coffin } + \coffin_clear:N \l__sdaps_classic_choicequestion_coffin + \sdaps_qobject_end:n { choicequestion } + + \group_end: + \vspace{\parskip} +} + +\newenvironment{optionquestion}[2][]{ + \choicequestion[singlechoice,#1]{#2} +} { + \endchoicequestion +} + +\cs_new_protected_nopar:Nn \_sdaps_classic_line_shipout_add:Nn +{ + % We have to be in vertical mode at this point. + \if_mode_vertical: + % Nothing + \else: + \msg_error:nn { sdapsclassic } { choicequestion_wrong_mode } + \fi: + + % Is linewidth the right thing here? + \int_compare:nT { \l__sdaps_classic_choiceitem_col_int + #2 > \l__sdaps_classic_choiceitem_cols_int } { + % We can only typeset a coffin in vertical mode if we use the box function + \vbox:n { \skip_horizontal:N \@totalleftmargin \box_use:N \l__sdaps_classic_choicequestion_coffin } + \skip_vertical:N \l_sdaps_choicequestion_rowsep_dim + \coffin_clear:N \l__sdaps_classic_choicequestion_coffin + \choicequestion_clubpenalty + \int_set:Nn \l__sdaps_classic_choiceitem_col_int { 0 } + } + + \dim_set:Nn \l_tmpa_dim { \linewidth / \l__sdaps_classic_choiceitem_cols_int + \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int } + \dim_set:Nn \l_tmpa_dim { \l__sdaps_classic_choiceitem_col_int \l_tmpa_dim } + + \coffin_join:NnnNnnnn \l__sdaps_classic_choicequestion_coffin { l } { H } #1 { l } { H } { \l_tmpa_dim } { 0pt } + \int_add:Nn \l__sdaps_classic_choiceitem_col_int { #2 } +} + +\providecommand*{\choiceitem}[2][]{% + \tl_clear:N \l_sdaps_choicequestion_choice_var_tl + \tl_clear:N \l_sdaps_choicequestion_choice_val_tl + \tl_clear:N \l_sdaps_choicequestion_choice_text_tl + + \keys_set:nn { sdaps / choicequestion / choice } { #1 } + + \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl { + \sdaps_answer:n { #2 } + } { + \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl + } + + \dim_set:Nn \l_tmpa_dim { \linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int } + + \hcoffin_set:Nn \l_tmpa_coffin { + \hbox_set:Nn \l_tmpa_box { \strut \sdaps_checkbox:VV \l_sdaps_choicequestion_choice_var_tl \l_sdaps_choicequestion_choice_val_tl {}\ } + \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box } + \hskip \l_sdaps_choicequestion_colsep_dim + \hbox_unpack_clear:N \l_tmpa_box + \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - \l_tmpb_dim - 2\l_sdaps_choicequestion_colsep_dim}} + \noindent \usekomafont{choiceitemfont} \tl_trim_spaces:n { #2 } + \end{minipage} + } + + \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { 1 } + \ignorespaces +} + +\providecommand*{\choicemulticolitem}[3][]{ % + \tl_clear:N \l_sdaps_choicequestion_choice_var_tl + \tl_clear:N \l_sdaps_choicequestion_choice_val_tl + \tl_clear:N \l_sdaps_choicequestion_choice_text_tl + + \keys_set:nn { sdaps / choicequestion / choice } { #1 } + + \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl { + \sdaps_answer:n { #3 } + } { + \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl + } + + \dim_set:Nn \l_tmpa_dim { #2\linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + #2 \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int } + + \hcoffin_set:Nn \l_tmpa_coffin { + \hbox_set:Nn \l_tmpa_box { \strut \sdaps_checkbox:VV \l_sdaps_choicequestion_choice_var_tl \l_sdaps_choicequestion_choice_val_tl {}\ } + \dim_set:Nn \l_tmpb_dim { \box_wd:N \l_tmpa_box } + \hskip \l_sdaps_choicequestion_colsep_dim + \hbox_unpack_clear:N \l_tmpa_box + \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - \l_tmpb_dim - 2\l_sdaps_choicequestion_colsep_dim}} + \noindent \usekomafont{choiceitemfont} \tl_trim_spaces:n { #3 } + \end{minipage} + } + + \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { #2 } + \ignorespaces +} + +\providecommand{\choiceitemtext}[4][]{% + \tl_clear:N \l_sdaps_choicequestion_choice_var_tl + \tl_clear:N \l_sdaps_choicequestion_choice_val_tl + \tl_clear:N \l_sdaps_choicequestion_choice_text_tl + + \keys_set:nn { sdaps / choicequestion / choice } { #1 } + % TODO: Warn if val has been set here + + % If not multichoice then warn + \tl_if_eq:VnF \l_sdaps_choicquestion_type_tl { multichoice } { + \msg_error:nn { sdapsclassic } { choicequestion_no_text } + } + + \tl_if_empty:NTF \l_sdaps_choicequestion_choice_text_tl { + \sdaps_answer:n { #4 } + } { + \sdaps_answer:V \l_sdaps_choicequestion_choice_text_tl + } + + \dim_set:Nn \l_tmpa_dim { #3\linewidth / \l__sdaps_classic_choiceitem_cols_int - \l__sdaps_classic_choiceitem_pad_dim + #3 \l__sdaps_classic_choiceitem_pad_dim / \l__sdaps_classic_choiceitem_cols_int } + + \hcoffin_set:Nn \l_tmpa_coffin { + \hskip \l_sdaps_choicequestion_colsep_dim + \begin{minipage}[t]{\dim_eval:n { \l_tmpa_dim - 2\l_sdaps_choicequestion_colsep_dim}} + \usekomafont{choiceitemfont} + \dim_set:Nn \l_tmpa_dim { #2 } + \dim_set:Nn \l_tmpb_dim { #2 } + \dim_set:Nn \l_tmpa_dim { 0.5 \l_tmpa_dim - 0.8ex } + \dim_set:Nn \l_tmpb_dim { 0.5 \l_tmpb_dim + 0.8ex } + \strut\ignorespaces \tl_trim_spaces:n { #4 } ~ + \sdaps_textbox_hstretch:VVVnn \l_sdaps_choicequestion_choice_var_tl \l_tmpa_dim \l_tmpb_dim { 0pt } { 1 } + \end{minipage} + } + + \_sdaps_classic_line_shipout_add:Nn \l_tmpa_coffin { #3 } + \ignorespaces +} + + + +% \end{macrocode} +% +% \subsection{The questionnaire environment} +% +% There is a bit of magic here. If (and only if) we need to render multiple +% pages of the questionnaire, then we write the whole questionnaire into a +% temporary file. This way we can input the file multiple times, which means +% that environments changing the parser (e.g. verbatim) work properly. +% +% \begin{macrocode} +% + +\iow_new:N \l__sdaps_questionnaire_iow +\bool_new:N \l__sdaps_questionnaire_parse_direct_bool + +\bool_new:N \l__sdaps_questionnaire_info_bool + +\keys_define:nn { sdaps / questionnaire } +{ + info .bool_set:N = \l__sdaps_questionnaire_info_bool, + info .default:n = true, + info .initial:n = true, + + noinfo .meta:n = { info=false }, +} + +\cs_new_protected_nopar:Nn \_sdaps_classic_show_info: +{ + \bool_if:NT \l__sdaps_questionnaire_info_bool { + \begin{info} + \translate{infotext} \\[1ex] + \tl_if_eq:VnTF \g_sdaps_checkmode_tl { checkcorrect } { + \begin{tabularx}{\textwidth}{lXllll} + \checkbox & \strut \translate{info-multi} & \hspace{2em} \checkedbox {} & \translate{info-cross} & \hspace{1em} \correctedbox {} & \translate{info-corrected} \\ + \checkbox* & \strut \translate{info-single} & \hspace{2em} \checkedbox* {} & \translate{info-cross} & \hspace{1em} \correctedbox* {} & \translate{info-corrected} \\ + \end{tabularx} + } { + \tl_if_eq:VnTF \g_sdaps_checkmode_tl { check } { + \begin{tabularx}{\textwidth}{lXll} + \checkbox & \strut \translate{info-multi} & \hspace{2em} \checkedbox {} & \translate{info-cross} \\ + \checkbox* & \strut \translate{info-single} & \hspace{2em} \checkedbox* {} & \translate{info-cross} \\ + \end{tabularx} + } { + \tl_if_eq:VnTF \g_sdaps_checkmode_tl { fill } { + \begin{tabularx}{\textwidth}{lXll} + \checkbox & \strut \translate{info-multi} & \hspace{2em} \filledbox {} & \translate{info-fill} \\ + \checkbox* & \strut \translate{info-single} & \hspace{2em} \filledbox* {} & \translate{info-fill} \\ + \end{tabularx} + } { + \PackageError{sdaps}{Sorry, there is no help text for the checkmode you have choosen right now! Please pass the noinfo optional argument to the questionnaire environment!}\@ehb % + } + } + } + \end{info} + } + \ignorespaces +} + +\newenvironment { questionnaire } [ 1 ] [] +{ + \keys_set:nn { sdaps / questionnaire } { #1 } + + \hypersetup{ + pdfauthor = \@author, + pdftitle = \@title, + pdfsubject = sdaps questionnaire \@title, + pdfkeywords = sdaps questionnaire \@title + } + + % If we only have one questionnaire ID, then parse the environment directly, + % otherwise write it into a temporary file and input it multiple times. + \group_begin: + + \if@twoside + \bool_gset_true:N \g_sdaps_twoside_bool + \else + \bool_gset_false:N \g_sdaps_twoside_bool + \fi + + % Enable all metadata writing by default + \sdaps_context_enable_writing: + + \sdaps_info_write:x{Author=\exp_not:o{\@author}} + \sdaps_info_write:x{Title=\exp_not:o{\@title}} + + \int_compare:nTF { \seq_count:N \g_sdaps_questionnaire_ids_seq <= 1 } { + \bool_set_true:N \l__sdaps_questionnaire_parse_direct_bool + + % Set the questionnaire ID + \seq_gpop_left:NN \g_sdaps_questionnaire_ids_seq \l_tmpa_tl + \sdaps_set_questionnaire_id:V \l_tmpa_tl + + % And, begin the questionnaire + \sdaps_begin: + \_sdaps_classic_show_info: + } { + \bool_set_false:N \l__sdaps_questionnaire_parse_direct_bool + + % Write content into a file, see "verbatim" documentation for more information. + % TODO: Allow multiple temporary files by postfixing with integer? + \iow_open:Nn \l__sdaps_questionnaire_iow { \c_sys_jobname_str . questionnaire } + \cs_set_eq:NN\do\@makeother\dospecials + \catcode`\^^M\active + \def\verbatim@processline{ + \iow_now:Nx \l__sdaps_questionnaire_iow {\the\verbatim@line} + } + \verbatim@ + } +} { + \bool_if:NTF \l__sdaps_questionnaire_parse_direct_bool { + % Just end everything, nothing else to do. + % Make sure we always end the current paragraph. + \par + \sdaps_end: + + \group_end: + } { + % We are done inputting the questionnaire + \iow_close:N \l__sdaps_questionnaire_iow + \group_end: + + % Now cycle through all IDS and output it again. + \group_begin: + \seq_map_inline:Nn \g_sdaps_questionnaire_ids_seq { + \sdaps_set_questionnaire_id:n { ##1 } + + % Reset a lot of global LaTeX counters + \@ifundefined{c@page}{}{\setcounter{page}{1}} + \@ifundefined{c@part}{}{\setcounter{part}{0}} + \@ifundefined{c@chapter}{}{\setcounter{chapter}{0}} + \@ifundefined{c@paragraph}{}{\setcounter{paragraph}{0}} + \@ifundefined{c@subparagraph}{}{\setcounter{subparagraph}{0}} + \@ifundefined{c@section}{}{\setcounter{section}{0}} + \@ifundefined{c@subsection}{}{\setcounter{subsection}{0}} + \@ifundefined{c@subsubsection}{}{\setcounter{subsubsection}{0}} + \@ifundefined{c@equation}{}{\setcounter{equation}{0}} + \@ifundefined{c@figure}{}{\setcounter{figure}{0}} + \@ifundefined{c@table}{}{\setcounter{table}{0}} + + \sdaps_begin: + \_sdaps_classic_show_info: + \input{ \c_sys_jobname_str . questionnaire } + % Make sure we always end the current paragraph. + \par + \sdaps_end: + \newpage + + % Close the output file now (after the page has been shipped out) + % XXX: This is a bit of a hack, as disabling metadata writing has side effects + \iow_close:N \g_sdaps_infofile_iow + + } + \group_end: + } +} + +\def\sdapspagemark{ + \sdaps_page_end: +} + +% \end{macrocode} +% +% \subsection{The group environments} +% +% Alias for the group environments. +% +% \begin{macrocode} +% + + +\newenvironment { choicegroup } [ 2 ] [] +{ + \group_begin: + + \question{#2} + + % Undefine the question (and choice) commands in local scope so that they + % can be redefined by choicearray without any issues. + % Note that \cs_undefine:N works in global scope, so we cannot use it here. + \cs_set_eq:NN\question\tex_undefined:D + \cs_set_eq:NN\choice\tex_undefined:D + + \begin{choicearray}[#1]{#2} + % XXX: This is a bit of a hack, set in global scope because the choicearray + % environment does evil things with scopes. + \cs_gset_eq:NN\groupaddchoice\choice + \cs_gset_eq:NN\choiceline\question +} +{ + \cs_gset_eq:NN\groupaddchoice\undefined + \cs_gset_eq:NN\choiceline\undefined + + \end{choicearray} + \group_end: +} + +\newenvironment { optiongroup } [ 2 ] [] +{ + \group_begin: + + \question{#2} + + % Undefine the question (and choice) commands in local scope so that they + % can be redefined by optionarray without any issues. + % Note that \cs_undefine:N works in global scope, so we cannot use it here. + \cs_set_eq:NN\question\tex_undefined:D + \cs_set_eq:NN\choice\tex_undefined:D + + \begin{optionarray}[#1]{#2} + % XXX: This is a bit of a hack, set in global scope because the optionarray + % environment does evil things with scopes. + \cs_gset_eq:NN\groupaddchoice\choice + \cs_gset_eq:NN\optionline\question +} +{ + \cs_gset_eq:NN\groupaddoption\undefined + \cs_gset_eq:NN\optionline\undefined + + \end{optionarray} + \group_end: +} + +\newenvironment { markgroup } [ 2 ] [] +{ + \group_begin: + + \question{#2} + + % Undefine the question (and choice) commands in local scope so that they + % can be redefined by choicearray without any issues. + % Note that \cs_undefine:N works in global scope, so we cannot use it here. + \cs_set_eq:NN\range\tex_undefined:D + + \begin{rangearray}[#1]{#2} + % XXX: This is a bit of a hack, set in global scope because the choicearray + % environment does evil things with scopes. + \cs_gset_eq:NN\markline\range +} +{ + \cs_gset_eq:NN\markline\undefined + + \end{rangearray} + \group_end: +} + +\ExplSyntaxOff + +% \end{macrocode} + +% \Finale +\endinput |