% \iffalse meta-comment % % Copyright (C) 2019 by Benjamin Berg % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3c % 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 % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Benjamin Berg. % % \fi % % \iffalse %<*driver> \ProvidesFile{sdapsclassic.dtx} % %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesClass{sdapsclassic} %<*class> [2015/08/02 v0.1 Initial version of SDAPS classic class] % % %<*driver> \documentclass{ltxdoc} %\EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{sdapsclassic.dtx} \end{document} % % \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. \_sdaps_classic_question. \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{Info-\unexpanded{#1}=\unexpanded{#2}} } \newcommand\qid{\tl_use:N \g__sdaps_questionnaire_id_tl} \def\_sdaps_classic_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: \_sdaps_classic_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 } } \_sdaps_classic_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 } } \_sdaps_classic_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 \dim_new:N \l__sdaps_classic_choicequestion_prevdepth_dim \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 } \_sdaps_classic_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 } \dim_set:Nn \l__sdaps_classic_choicequestion_prevdepth_dim { 1000pt } \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 } \prevdepth=\l__sdaps_classic_choicequestion_prevdepth_dim \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:NNn { % 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 + #3 > \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 } \prevdepth=\l__sdaps_classic_choicequestion_prevdepth_dim \skip_vertical:N \l_sdaps_choicequestion_rowsep_dim \coffin_clear:N \l__sdaps_classic_choicequestion_coffin \dim_set:Nn \l__sdaps_classic_choicequestion_prevdepth_dim { 1000pt } \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 } \dim_compare:nNnTF { \coffin_dp:N \l__sdaps_classic_choicequestion_coffin } > { \coffin_dp:N #1 } { \dim_set:Nn \l__sdaps_classic_choicequestion_prevdepth_dim {\dim_min:nn { \l__sdaps_classic_choicequestion_prevdepth_dim } { #2 + \coffin_dp:N \l__sdaps_classic_choicequestion_coffin - \coffin_dp:N #1 } } } { \dim_set:Nn \l__sdaps_classic_choicequestion_prevdepth_dim {\dim_min:nn { \l__sdaps_classic_choicequestion_prevdepth_dim + \coffin_dp:N #1 - \coffin_dp:N \l__sdaps_classic_choicequestion_coffin } { #2 } } } \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 { #3 } } \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: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 }\strut \par \dim_gset:Nn \g_tmpa_dim { \dim_max:nn { \box_dp:N \l_tmpa_box } { \prevdepth } } \unskip \end{minipage} } \_sdaps_classic_line_shipout_add:NNn \l_tmpa_coffin \g_tmpa_dim { 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: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 }\strut \par \dim_gset:Nn \g_tmpa_dim { \dim_max:nn { \box_dp:N \l_tmpa_box } { \prevdepth } } \unskip \end{minipage} } \_sdaps_classic_line_shipout_add:NNn \l_tmpa_coffin \g_tmpa_dim { #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 } \par \dim_gset:Nn \g_tmpa_dim { \prevdepth } \unskip \end{minipage} } \_sdaps_classic_line_shipout_add:NNn \l_tmpa_coffin \g_tmpa_dim { #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_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 % } } } } \cs_new_protected_nopar:Nn \_sdaps_classic_show_info: { \bool_if:NT \l__sdaps_questionnaire_info_bool { \begin{info} \_sdaps_classic_info: \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: } \def\sdapsinfo{ \_sdaps_classic_info: } % \end{macrocode} % % \subsection{The group environments} % % Alias for the group environments. % % \begin{macrocode} % \tl_new:N \l__sdaps_classic_group_var_tl \tl_new:N \l__sdaps_classic_group_text_tl \tl_new:N \l__sdaps_classic_group_extra_tl \keys_define:nn { sdapsclassic / group } { var .tl_set:N = \l__sdaps_classic_group_var_tl, text .tl_set:N = \l__sdaps_classic_group_text_tl, } \newenvironment { choicegroup } [ 2 ] [] { \group_begin: \keys_set_known:nnN { sdapsclassic / group } { #1 } \l__sdaps_classic_group_extra_tl \_sdaps_classic_question{#2} \tl_if_empty:NTF \l__sdaps_classic_group_text_tl { \sdaps_qobject_begin:nnn { choicegroup } { Head } { #2 } } { \sdaps_qobject_begin:nnV { choicegroup } { Head } \l__sdaps_classic_group_text_tl } \tl_if_empty:NF \l__sdaps_classic_group_var_tl { \sdaps_qobject_append_var:V \l__sdaps_classic_group_var_tl } % 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 \expandafter\choicearray\expandafter[\l__sdaps_classic_group_extra_tl] % 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 \endchoicearray \sdaps_qobject_end:n { choicegroup } \group_end: } \newenvironment { optiongroup } [ 2 ] [] { \group_begin: \keys_set_known:nnN { sdapsclassic / group } { #1 } \l__sdaps_classic_group_extra_tl \_sdaps_classic_question{#2} \tl_if_empty:NTF \l__sdaps_classic_group_text_tl { \sdaps_qobject_begin:nnn { optiongroup } { Head } { #2 } } { \sdaps_qobject_begin:nnV { optiongroup } { Head } \l__sdaps_classic_group_text_tl } \tl_if_empty:NF \l__sdaps_classic_group_var_tl { \sdaps_qobject_append_var:V \l__sdaps_classic_group_var_tl } % 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 \expandafter\optionarray\expandafter[\l__sdaps_classic_group_extra_tl] % 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 \endoptionarray \sdaps_qobject_end:n { optiongroup } \group_end: } \newenvironment { markgroup } [ 2 ] [] { \group_begin: \keys_set_known:nnN { sdapsclassic / group } { #1 } \l__sdaps_classic_group_extra_tl \_sdaps_classic_question{#2} \tl_if_empty:NTF \l__sdaps_classic_group_text_tl { \sdaps_qobject_begin:nnn { markgroup } { Head } { #2 } } { \sdaps_qobject_begin:nnV { markgroup } { Head } \l__sdaps_classic_group_text_tl } \tl_if_empty:NF \l__sdaps_classic_group_var_tl { \sdaps_qobject_append_var:V \l__sdaps_classic_group_var_tl } % Undefine the range command in local scope so that it can be redefined % by rangearray 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 \expandafter\rangearray\expandafter[\l__sdaps_classic_group_extra_tl] % 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 \endrangearray \sdaps_qobject_end:n { markgroup } \group_end: } \ExplSyntaxOff % \end{macrocode} % \Finale \endinput