summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/widows-and-orphans/widows-and-orphans.dtx
blob: fc92f914836a39533e6c66e81a0b913b13ddac50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
% \iffalse meta-comment
%%
%% File: widows-and-orphans.dtx (C) Copyright 2017-2022 Frank Mittelbach
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file
%
%    https://www.latex-project.org/lppl.txt
%
%
% The development version of the bundle can be found at
%
%    https://github.com/FrankMittelbach/fmitex/widows-and-orphans
%
% for those people who are interested.
% \fi
% \iffalse
%<*driver>
\RequirePackage[nohyphen]{underscore}    % no hyphen after undercore
                                         %   in csname
\documentclass
    [final]
    {l3doc-TUB}

\setcounter{page}{1}

% fix for _TF undefined,
% https://github.com/latex3/latex3/issues/477#issuecomment-419458783
\ExplSyntaxOn
\cs_set_protected:Npn \__codedoc_typeset_TF:
  {
    \group_begin:
      \exp_args:No \__codedoc_if_macro_internal:nT \l__codedoc_tmpa_tl
        { \color[gray]{0.5} }
      \itshape TF
\iffalse      \makebox[0pt][r]
        {
          \color{red}
          \underline{\phantom{\itshape TF} \kern-0.1em}
        } \fi
    \group_end:
  }

\NewDocumentCommand \TypesetImplementationTF { m m }
  {
    \bool_if:NTF \g__codedoc_typeset_implementation_bool
      { #1 }
      { #2 }
  }

\ExplSyntaxOff

\makeatletter  % fix for ltugboat issue with doc (if old doc is used)
\def\dotfill{\leaders\hbox to.6em{\hss .\hss}\hskip\z@ plus  1fill\kern\z@}%
\def\dotfil{\leaders\hbox to.6em{\hss .\hss}\hfil\kern\z@}%
\def\pfill{\unskip~\dotfill\penalty500\strut\nobreak
               \dotfil~\ignorespaces}%
\makeatother

\begin{document}

\DocInput{widows-and-orphans.dtx}
\advance\signaturewidth by 42pt
\makesignature
\end{document}
%</driver>
% \fi
%
% \DoNotIndex{\cs_new:Npn,\cs_new_nopar:Npn,\csname,\endcsname,\fi:,\else:}
% \DoNotIndex{\tl_new:N,\bool_new:N}
% \DoNotIndex{\if_meaning:w,\iftrue,\int_new:N,\int_set:Nn}
% \DoNotIndex{\prop_new:N,\tl_put_left:Nn}
% \DoNotIndex{\bool_do_until:Nn,\bool_gset_false:N,\bool_gset_true:N}
% \DoNotIndex{\bool_if:NTF,\bool_set_false:N,\bool_set_true:N}
% \DoNotIndex{\clist_map_break:n,\clist_map_inline:nn}
% \DoNotIndex{\cs_if_eq:NNTF,\exp_args:Nc}
% \DoNotIndex{\int_case:nnTF,\int_case:nnF,\int_case:nnF,\int_incr:N}
% \DoNotIndex{\int_to_arabic:n,\int_to_roman:n}
% \DoNotIndex{\keys_define:nn,\keys_set:nn}
% \DoNotIndex{\msg_err:nn,\msg_new:nnnn,\msg_redirect_module:nnn}
% \DoNotIndex{\msg_warning:nn,\msg_warning:nnn,\msg_error:nn}
% \DoNotIndex{\prop_clear:N,\prop_get:NnNTF,\prop_put:Nnn,\prop_show:N}
% \DoNotIndex{\prg_new_conditional:Npnn}
% \DoNotIndex{\prg_return_false:,\prg_return_true:}
% \DoNotIndex{\prop_get:NVNT,\prop_put:NVn,\bool_if:NF,\bool_if:NT}
% \DoNotIndex{\marginpar,\space,\ignorespaces,\thepage}
% ^^A\DoNotIndex{\RequirePackage,\NeedsTeXFormat,\NewDocumentCommand}
%
% \title{The \pkg{widows-and-orphans} package}
% \author{Frank Mittelbach}
% \date{Printed \today\thanks{For the package version
%   see the \texttt{\string\ProvidesExplPackage} line in the code.}}
% \address{Mainz, Germany}
% \netaddress{https://www.latex-project.org}
% \personalURL{https://ctan.org/pkg/widows-and-orphans}
%
% \maketitle
%
% \begin{abstract}
%   The \pkg{widows-and-orphans} package checks page or column breaks
%   for issues with widow or orphan lines and issues warnings if such
%   problems are detected. In addition, it checks and complains
%   about breaks involving hyphenated words and warns about display
%   formulas directly after a page break\Dash if they are allowed by the
%   document parameter settings, which by default isn't the case.
%
%   A general discussion of the problem of widows and orphans and
%   suggestions for resolution is given in~\cite{tub-wao}.
% \end{abstract}
%
% \tableofcontents
%
% \newcommand\option[1]{\texttt{#1}}
% \newcommand\ovalue[1]{\texttt{#1}}
%
% \section{Overview}
%
% To determine if a widow or orphan has occurred at a column or page
% break, the package analyzes the \cs{outputpenalty} that triggered the
% break. As \TeX{} adds special penalties to widow and orphan lines
% (\cs{widowpenalty} and \cs{clubpenalty}), we can hope to identify
% them, provided the penalties have unique values so that we don't end
% up with false positives.
%
% \TypesetImplementationTF {\enlargethispage*{2\baselineskip}}{}
%
% The package therefore analyzes the different parameter values and if
% necessary adjusts the settings slightly so that all possible
% combinations that can appear in documents have unique values and can
% thus be identified.
%
% All that remains is to hook into the output routine check;
% if \cs{outputpenalty} has a value that matches one of the
% problematic cases, issue a warning.
%
% Besides widows and orphans it is possible to detect other cases
% controlled through penalty parameters, e.g., \cs{brokenpenalty} that is
% added if a line ends in a hyphen. So by including this parameter
% into the checks, we can identify when that happens at the end of a
% column and issue a warning there too.
%
% We also do this for \cs{predisplaypenalty}, which controls a break just
% in front of a math display. This is normally set to \texttt{10000}
% so such breaks don't happen in standard \LaTeX{}, but if the value
% is lowered it becomes possible, and thus a possible issue.
%
% \subsection{Options}
%
% The package has a number of key/value options to adjust its
% behavior.
% The option \option{check} defines what happens
% when an issue is found: default is \ovalue{warning}, other
% possibilities are \ovalue{error}, \ovalue{info} and \ovalue{none}.
%
% The options \option{orphans} and \option{widows} set
% reasonable parameter values; the default is to use whatever the class
% defines. Possible values are \ovalue{prevent}, \ovalue{avoid} or
% \ovalue{default}, the latter meaning use standard \LaTeX{} defaults.
%
% To set all parameters in one go you can use \option{prevent-all},
% \option{avoid-all} or \option{default-all}. These options also assign
% values to \cs{brokenpenalty} and \cs{predisplaypenalty}.
%
% \TypesetImplementationTF {\pagebreak}{}
%
%
% \subsection{User commands}
%
% The package provides three user-level commands.
%
% \vspace*{-4pt}
% \begin{function}{\WaOsetup}
%   \begin{syntax}
%     \cs{WaOsetup} \meta{comma list}
%   \end{syntax}
%   This command accepts any of the package options and allows
%   adjusting the package behavior in mid-document if necessary.
% \end{function}

% \vspace*{-4pt}
% \begin{function}{\WaOparameters}
%   \begin{syntax}
%     \cs{WaOparameters}
%   \end{syntax}
%    This command produces a listing of the parameter combinations and
%    their values.
% \end{function}

% \vspace*{-4pt}
% \begin{function}{\WaOignorenext}
%   \begin{syntax}
%     \cs{WaOignorenext}
%   \end{syntax}
%   This command directs the package to not generate warnings for
%    the current page or columns (if we know that they can't be corrected).
% \end{function}
%
%
%
% \subsection{Related packages}
%
% Package \pkg{nowidow}: This package offers some commands to help
% pushing lines from one page to another by locally
% requesting no widows or orphans\Dash possibly for several lines. In that
% respect it implements one of the possibilities discussed in the
% \TUB\ article~\cite{tub-wao}. This is, however, in many cases not the
% best solution to get rid of a widow or orphan when the interest is
% to achieve high typographical quality.
%
%
%
% \StopEventually{
% \begin{thebibliography}{1}
% \bibitem{tub-wao}
% Frank Mittelbach.
% \newblock Managing forlorn paragraph lines (a.k.a.\ widows and orphans)
%  in \LaTeX{}.
% \newblock \textsl{TUGboat} 39:3,
%   \ifx\thisissuepageref\undefined \else \thisissuepageref{mitt-widows} ,\fi
%   2018.
% \bibitem{expl3}
% \LaTeX3 Project Team.
% \newblock A collection of articles on \pkg{expl3}.\\
%    \url{https://latex-project.org/publications/indexbytopic/l3-expl3/}
% \end{thebibliography}
% \ifx\thisissuepageref\undefined    ^^A is this TUB production ??? if not gen index
%     \setlength\IndexMin{200pt}  \PrintIndex
% \fi
% }
%
%
% \section{The implementation}
%
% The package is implemented in \pkg{expl3}, so the first thing we do
% is to define a prefix for our internal commands, so that we can
% write \texttt{@@} in the source when we mean the prefix \texttt{__fmwao},
% indicating that something is internal.\footnote{\texttt{l3docstrip}
% expands that for us, so in the \texttt{.sty} file we get the
% longer names with double underscores even though the real source
% just contains \texttt{@@} all over the place. The same is done by
% the \texttt{l3doc} class for the printed documentation you are
% currently reading.\raggedright}
%
%    \begin{macrocode}
%<@@=fmwao>
%    \end{macrocode}
%
% Then we check that we are running on top of \LaTeXe{} and load the
% two packages we want to use: \pkg{xparse} for the user interface and
% \pkg{l3keys2e} for key/value option syntax. They load the needed
% \pkg{expl3} code so we are ready to roll afterwards.
% Nowadays \pkg{xparse} is in the kernel so doesn't need loading any longer.
%
%    \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}    \RequirePackage{l3keys2e}
%    \end{macrocode}
%
% Then we announce the package to the world at large. This declaration
% will also tell \LaTeX{} that this is an \pkg{expl3} package and that
% from now on the \pkg{expl3} conventions are to be obeyed, e.g.,
% \texttt{_} and \texttt{:} can appear in command names and whitespace
% is automatically ignored (so no need for \texttt{\%} all over
% the place).\footnote{Figure~\ref{fig:expl3} gives a short
%   introduction to the naming conventions of \pkg{expl3} for those
%   readers who haven't seen any code written in \pkg{expl3}.
%   For more details refer to~\cite{expl3}.}
%
% \begin{figure}
%   \centering
%   \setlength\fboxsep{7pt}
%   \noindent\hspace{-\marginparwidth}\fbox{\begin{minipage}{1.21\textwidth}
%    Commands in \pkg{expl3} use the following naming convention:
%   \begin{tabbing}
%     \qquad \texttt{\space\space\bslash\meta{module}\_\meta{action}:\meta{arg-spec}}
%     \quad
%               \= \% externally available command \\
%     \qquad \texttt{\bslash\_\_\meta{module}\_\meta{action}:\meta{arg-spec}}
%               \> \% internal command local to the package
%   \end{tabbing}
%
%   \vspace*{2pt}
%
%   \begin{description} \raggedright
%   \item[\ttfamily\meta{module}] describes the (main) area to which
%     the command belongs,
%   \item[\ttfamily\meta{action}] describes the (main) action of the
%     command, and
%   \item[\ttfamily\meta{arg-spec}] shows the expected command
%     arguments and their preprocessing:\\
%    \texttt{N} means expect a
%     single token; \\
%     \texttt{n} means expect a (normal) braced
%     argument;\\
%    \texttt{T} and \texttt{F} also represent braced
%     arguments, indicating ``true'' and ``false'' branches in a
%     conditional; \\
%    \texttt{V} means expect a single token, interpret it as a
%     variable and pass its value on to the command;\\
%    Finally, \texttt{p} stands for a (possibly empty) parameter spec, e.g.,
%     \texttt{\#1\#2...}\ in a definition.
%     \\There are a number of other
%     argument types, but they aren't used in the code described here.
%   \end{description}
%
%   \vspace*{2pt}
%
% Examples:
%   \begin{description} \raggedright
%   \item[\cs{cs_if_eq:NNTF}] is a conditional from the module
%     \texttt{cs} (command names) implementing the action \\
%    \texttt{if\_eq} (if equal) and expecting two single tokens
%     (commands to compare) and a true and a false branch (one of
%     which is executed).
%
%   \item[\cs{tl_put_left:Nn}] is a function from the module
%     \texttt{tl} (token lists) implementing \texttt{put\_left} and \\
%    expecting a single token (a token list variable) and a braced
%     argument (the data to insert at \\
%    the front/left in the  variable).
%   \end{description}
%
% \medskip
%
%   Variables start with \cs{l_} (for local) or \cs{g_} (for global)
%   and have the data type as the last part of the name. Variables
%   internal to the package use two underscores, e.g.,
%   \cs{l__@@_gen_warn_bool}.
%
%   \end{minipage}}
%
%   \caption{Crash course in \pkg{expl3} command name conventions}
%     \label{fig:expl3}
% \end{figure}
%
%    \begin{macrocode}
\ProvidesExplPackage{widows-and-orphans}{2022/08/27}{v1.0e}
                    {Detecting widows and orphans (FMi)}
%    \end{macrocode}
%
%
%  \begin{macro}[int]{\@makecol}
%    
%    As mentioned in the introduction we want to check the value of
%    \cs{outputpenalty} inside the output routine, and so we need
%    to add some code to the output routine.
%
%    We add it to the front of \cs{@makecol}, the macro that
%    assembles the actual column. This way it only gets executed if we
%    make a column or a page but not when the output routine is
%    triggered because of a float or \cs[no-index]{marginpar}.
%    \begin{macrocode}
\tl_put_left:Nn \@makecol { \@@_test_for_widows_etc: }
%    \end{macrocode}
%  \end{macro}
%
%
% \subsection{Checking \cs{outputpenalty}}
%
%
%  \begin{macro}{\g_@@_gen_warn_bool}
%    To be able to suppress checking we define a global boolean
%    variable which by default is set to \texttt{true} (warnings enabled).
%    \begin{macrocode}
\bool_new:N \g_@@_gen_warn_bool
\bool_gset_true:N \g_@@_gen_warn_bool
%    \end{macrocode}
%  \end{macro}
%
%
%
%  \begin{macro}{\@@_test_for_widows_etc:}
%
%    What are the different values related to orphans and widows and
%    the like that can appear in \cs{outputpenalty}? Here is the
%    basic list:
%    \begin{description}
%
%    \item[$\cs{widowpenalty} + \cs{interlinepenalty} \to$] if the
%    break happens on the second-last line of a paragraph and the
%    paragraph is not followed by a math display.
%
%    \item[$\cs{displaywidowpenalty} + \cs{interlinepenalty} \to$] if
%    the break happens on the second-last line of a paragraph and the
%    paragraph is followed by a math display.
%
%    \item[$\cs{clubpenalty} + \cs{interlinepenalty} \to$] if the
%    break happens after the first line of a paragraph and the
%    paragraph has more than two lines.
%
%    \item[$\cs{clubpenalty}+\cs{widowpenalty} + \cs{interlinepenalty}
%    \to$] if the break happens after the first line of a paragraph in
%    a two-line paragraph (thus this line is also the second-last
%    line).
%
%    \item[$\cs{clubpenalty}+\cs{displaywidowpenalty}  +
%    \cs{interlinepenalty} \to $] if the
%    break happens after the first line of a paragraph in a two-line
%    paragraph and a math display follows.
%
%    \end{description}
%    That's it for widows and orphans. If we also consider hyphenated
%    breaks then we get a further set of cases, namely all of the
%    above with \cs{brokenpenalty} added in and the case of
%    \cs{brokenpenalty} on its own (with just \cs{interlinepenalty} added).
%    
%    \begin{macrocode}
\cs_new:Npn \@@_test_for_widows_etc: {
%    \end{macrocode}
%    So here is the main test. We compare \cs{outputpenalty} with each
%    case until we have a hit or run out of cases. Instead of adding
%    \cs{interlinepenalty} to each case we subtract it once from
%    \cs{outputpenalty} to make the comparison a little more
%    efficient.
%
%    If we get a hit, we execute the corresponding code: either
%    \cs{@@_problem_identified:n} or \cs{@@_problem_identified:nn}
%    (i.e., one or two arguments) to issue the warning. The arguments
%    are to select the correct warning text and customize it further
%    if necessary. For example, in the first case it is a ``widow''
%    problem and the text in the message should start with
%    ``\texttt{Widow}'' while in the second case it is also a
%    ``widow'' problem but the text will say ``\texttt{Display\string~
%    widow}''.\footnote{If you haven't seen much \pkg{expl3} code you
%    may wonder about the \texttt{\string~}. As the code ignores
%    spaces we have to mark up real spaces and for this the tilde is
%    used. In \pkg{expl3} code this does not act as a tie, but simply
%    as a catcode 10 space character (while normal spaces are
%    ignored).}  This just saves a bit of space when different cases
%    share more or less the same message text.
%    \begin{macrocode}
  \int_case:nnF { \outputpenalty - \interlinepenalty }
    {
      { \widowpenalty }
        { \@@_problem_identified:nn{widow}{Widow} }
      { \displaywidowpenalty }
        { \@@_problem_identified:nn{widow}{Display~ widow} }
%    \end{macrocode}
%    Above we have always talked about testing for \cs{clubpenalty}
%    because that is the register \TeX{} is using. However, \LaTeX{}
%    changes its value back and forth between \texttt{10000} (after a
%    heading) and \cs{@clubpenalty} (the saved value) and sometimes
%    even \texttt{0}. Thus when the output routine is triggered it
%    could be any of these values and in case it is zero we would get
%    spurious matches. So for comparison we always test against
%    \cs{@clubpenalty}.
%    \begin{macrocode}
      { \@clubpenalty }
        { \@@_problem_identified:n{orphan} }
      { \@clubpenalty + \widowpenalty }
        { \@@_problem_identified:nn{orphan-widow}{} }
      { \@clubpenalty + \displaywidowpenalty }
        { \@@_problem_identified:nn{orphan-widow}{display} }
%    \end{macrocode}
%
%    A similar issue comes from a hyphen at the end of a column or
%    page in which case \TeX{} adds \cs{brokenpenalty} so we can test
%    against that:
%    \begin{macrocode}
      { \brokenpenalty }
        { \@@_problem_identified:n{hyphen} }
%    \end{macrocode}
%    However, I said ``\TeX{} adds'', which means if a widow line also
%    ends in a hyphen then the penalty will be the sum of both
%    individual penalties. So all the cases above need to be repeated
%    with \cs{brokenpenalty} added to the value.
%    We generate the same warnings, though\Dash e.g., we will say ``Widow
%    detected'' and not ``Hyphenated widow line detected'' as the
%    latter seems to be overkill.
%    \begin{macrocode}
      { \brokenpenalty + \widowpenalty }
        { \@@_problem_identified:nn{widow}{Widow} }
      { \brokenpenalty + \displaywidowpenalty }
        { \@@_problem_identified:nn{widow}{Display~ widow} }
      { \brokenpenalty + \@clubpenalty }
        { \@@_problem_identified:n{orphan} }
      { \brokenpenalty + \@clubpenalty + \widowpenalty }
        { \@@_problem_identified:nn{orphan-widow}{} }
      { \brokenpenalty + \@clubpenalty + \displaywidowpenalty }
        { \@@_problem_identified:nn{orphan-widow}{display} }
%    \end{macrocode}
%    Finally there is \cs{predisplaypenalty} that we may as well check also
%    (in case it was set to a value lower than \texttt{10000}). If it
%    appears it means we have a display at the very top of the page.
%    We reuse the ``widow'' warning but this time say
%    ``\texttt{Lonely\string~ display}'' in the second argument.
%    This case does not have \cs{interlinepenalty} added by
%    \TeX{}, so we have to undo the optimization above.
%    \begin{macrocode}
      { \predisplaypenalty - \interlinepenalty }
        { \@@_problem_identified:nn{widow}{Lonely~ display} }
    }
%    \end{macrocode}
%    The last argument of \cs[no-index]{int_case:nnF} is executed in
%    the ``false'' case, i.e., when no match has been found.  In that
%    case we check the status of \cs{g_@@_gen_warn_bool} and
%    if that is also ``false'', i.e., we have been asked not to
%    generate warnings, we issue an error message. Why?  Because 
%    the user asked us explicitly to ignore problems on the
%    current page, but we found nothing wrong. This either means a
%    problem got corrected or the request was intended for a
%    different page. Either way it is probably worth checking.
%
%    \begin{macrocode}
    { \bool_if:NF \g_@@_gen_warn_bool
        { \msg_error:nn{widows-and-orphans}{no-problem} } }
%    \end{macrocode}
%    Finally, we make sure that the next page or column is again
%    checked.
%    \begin{macrocode}
  \bool_gset_true:N \g_@@_gen_warn_bool
}
%    \end{macrocode}
%  \end{macro}



%  \begin{macro}{\@@_problem_identified:n}
%  \begin{macro}{\@@_problem_identified:nn}
%    These commands prepare for generating a warning, but only if we
%    are supposed to, i.e., if \cs{g_@@_gen_warn_bool} is true.
%    \begin{macrocode}
\cs_new:Npn \@@_problem_identified:n #1 {
   \bool_if:NT \g_@@_gen_warn_bool
               { \msg_warning:nn{widows-and-orphans}{#1} }
}
\cs_new:Npn \@@_problem_identified:nn #1 #2 {
   \bool_if:NT \g_@@_gen_warn_bool
               { \msg_warning:nnn{widows-and-orphans}{#1}{#2} }
}
%    \end{macrocode}
%  \end{macro}
%  \end{macro}

      
% \subsection{Messages to the user}
%
%
%  \begin{macro}{\@@_this_page:}
%  \begin{macro}{\@@_next_page:}
%    For displaying nice messages to the user we need a few helper
%    commands. The two here show the page number of the current or
%    next page. They are semi-smart, that is they will recognize
%    if the document uses roman numerals and if so display the number
%    as a roman numeral (but in all other cases it uses arabic numerals).
%    \begin{macrocode}
\cs_new:Npn \@@_this_page: { \@@_some_page:n   \c@page       }
\cs_new:Npn \@@_next_page: { \@@_some_page:n { \c@page + 1 } }  
%    \end{macrocode}
%  \end{macro}
%  \end{macro}


%  \begin{macro}{\@@_some_page:n}
%  \begin{macro}{\@@_roman_thepage:}
%    This macro first compares \cs[no-index]{thepage} against the code that
%    would be used in the case of a roman numeral representation, and then
%    displays its argument using either arabic numbers or roman numerals.
%    \begin{macrocode}
\cs_new:Npn \@@_some_page:n #1 {
  \cs_if_eq:NNTF \thepage \@@_roman_thepage:
    { \int_to_roman:n } { \int_to_arabic:n }
  { #1 }
}                               
%    \end{macrocode}
%    \cs{@@_roman_thepage:} just stores the default definition of
%    \cs[no-index]{thepage} if page numbers are represented by roman numerals
%    for use in the comparison above.
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_roman_thepage: {\csname @roman\endcsname \c@page}
%    \end{macrocode}
%  \end{macro}
%  \end{macro}




%  \begin{macro}[TF, int]{\legacy_switch_if:n}
%    To evaluate \LaTeXe{} boolean switches in a nice way, we need a
%    conditional. Eventually this will probably make it into the \pkg{expl3}
%    code in this or a similar form, but right now it is missing.
%    \begin{macrocode}
\prg_new_conditional:Npnn \legacy_switch_if:n #1 {p, T , F , TF }
  { \exp_args:Nc\if_meaning:w { if#1 } \iftrue \prg_return_true:
                                        \else: \prg_return_false: \fi: }
%    \end{macrocode}
%  \end{macro}
%
%
%
%
%    The first message is issued if we have been directed to ignore a
%    problem and there wasn't one:{\hfuzz=14.1pt
%    \begin{macrocode}
\msg_new:nnnn {widows-and-orphans} {no-problem}
  { No~ problem~ to~ suppress~ on~ this~ page! }
  { Suppression~ of~ a~ widow~ or~ orphan~ problem~ was~ requested~
    but~ on~ the~ current~ page~ there~ doesn't~ seem~ to~ be~ any.~
    Maybe~ the~ text~ was~ changed~ and~ the~ request~ should~ get~
    (re)moved?}
%    \end{macrocode}
%   }
%
%   \bigskip
%
%
%
%
%    The next message is about orphans. They can appear at the bottom of
%    the first or the second column of the current page, if we are in
%    two-column mode.  So we check for this and adjust the message accordingly.
%    \begin{macrocode}
\msg_new:nnnn {widows-and-orphans} {orphan}
  { Orphan~ on~ page~ \@@_this_page:
    \legacy_switch_if:nT {@twocolumn}
      { \space ( \legacy_switch_if:nTF {@firstcolumn}
                                       { first~ } { second~ } column) }
  }
  { Check~ out~ the~ page~ and~ see~ if~ you~ can~ avoid~ the~ orphan.}
%    \end{macrocode}
%
%
%   \bigskip
%
%
%    A hyphen at the end of a page or column requires more or less the
%    same message, so this could have been combined with the previous one.
%    \begin{macrocode}
\msg_new:nnnn {widows-and-orphans} {hyphen}
  { Hyphen~ in~ last~ line~ of~ page~ \@@_this_page:
    \legacy_switch_if:nT {@twocolumn}
      { \space ( \legacy_switch_if:nTF {@firstcolumn}
                                       { first~ } { second~ } column) }
  }
  { Check~ out~ the~ page~ and~ see~ if~ you~ can~ get~
    a~ better~ line~ break. }
%    \end{macrocode}
%
%
%   \bigskip
%
%
%    Widows need a different logic since we detect them when cutting a
%    previous page or column but the widow is on the following one.
%    This message works for ``widows'', ``display widows'' as well as
%    math displays by just changing the first word (or words), so here
%    we use an additional argument:
%    \begin{macrocode}
\msg_new:nnnn {widows-and-orphans} {widow}
  { #1~ on~ page~ 
    \legacy_switch_if:nTF {@twocolumn}
      { \legacy_switch_if:nTF {@firstcolumn}
          { \@@_this_page: \space (second~ }
          { \@@_next_page: \space (first~  }
        column)
      }
      { \@@_next_page: }
  }
  { Check~ out~ the~ page~ and~ see~ if~ you~ can~ avoid~ the~ widow.}
%    \end{macrocode}
%
%
%   \bigskip
%
%
%    The case of both widow and orphan is similar, but we obviously
%    need different text so we made it its own message.
%    \begin{macrocode}
\msg_new:nnnn {widows-and-orphans} {orphan-widow}
  { Orphan~
    \legacy_switch_if:nTF {@twocolumn}
       { \legacy_switch_if:nTF {@firstcolumn}
           { and~ #1 widow~ on~ page~ \@@_this_page: \space
             (first~ and~ second~ }
           { on~ page~ \@@_this_page: \space (second~ column)~
             and~ #1 widow~ on~ page~ \@@_next_page: \space (first~ }
       }
       { on~ page~ \@@_this_page: \space (second~ column)~
         and~ #1 widow~ on~ page~ \@@_next_page: \space (first~ }
     column)
  }
  { Check~ out~ the~ page~ and~ see~ if~ you~ can~ avoid~ both~
    orphan~ and~ widow.}
%    \end{macrocode}
%
%
%
%
%
%
% \subsection{Adjusting parameter values}
%
% To avoid (a lot of) false positives during checking it is important
% that the parameter values are chosen in a way that all possible
% combinations lead to unique \cs{outputpenalty} values. At the same
% time, we want them to be as close as possible to the values that
% have been initially requested by the user (or in the document class)
% and if we deviate too much then this will likely alter the 
% page breaks \TeX{} finds.
%  So here is an outline of how we handle the parameters:
% \begin{itemize}
% \item We set up a property list to hold penalty values that
%    can appear in \cs{outputpenalty} inside the output routine. The
%    penalties are the ``keys'' and the corresponding property list value is
%    the source of how they got produced. For example, the key might be
%    \texttt{150} and the value \cs{widowpenalty}.
%
% \item Initially the property list is empty. So adding the first item
%    simply means taking the value of one parameter, say \texttt{150}
%    from $\cs{widowpenalty}+\cs{interlinepenalty}$, as the key and
%    this formula as the property list value.
%
% \item For the next parameter, say \cs{@clubpenalty}, we check if its
%    value (or more precisely its value plus \cs{interlinepenalty}) is
%    already a key in the property list. If that is the case, then we
%    have failed and must modify the parameter value somehow.
%
% \item If not, we also have to check any combination of the current
%    parameter with any parameter processed earlier. If that
%    combination is possible, e.g., \cs{@clubpenalty} (new) and
%    \cs{widowpenalty} (already processed) then we also have to check
%    the sum. If that sum is already a key in the property list then
%    we have failed as well.
%
% \item If we have failed, we iterate by incrementing the current parameter
%   value and try again. Eventually we will get to a value where all
%   combinations we test work, that is, are not yet in the property list.
%
% \item We then change the parameter to this value and add all the
%    combinations we tried before to the property list (that is
%    $\cs{@clubpenalty}+\cs{interlinepenalty}$ both alone and together with
%    \cs{widowpenalty} in our example). Thus from now on those are
%    also forbidden values.
%
% \item We do all this with a helper command that takes the new
%    parameter as the first argument and the list of different cases
%    to try as a comma-separated list as a second argument, e.g.,
%\begin{verbatim}
%    \__fmwao_decide_penalty:Nn \@clubpenalty 
%             { \@clubpenalty + \interlinepenalty ,
%               \@clubpenalty + \widowpenalty + \interlinepenalty }
%\end{verbatim}
%
% \item This way we are adding all relevant parameters to the property
%   list and at the same time adjusting their values if needed.
% \item Once all parameters are handled the property list is no longer
%   needed as the parameters got changed along the way, but we keep it
%   around as it allows for a simple display of all settings in one go.
% \end{itemize}
%
%

%  \begin{macro}{\l_@@_penalties_prop}
%    Here is the property list for our process.
%    \begin{macrocode}
\prop_new:N \l_@@_penalties_prop
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\@@_initialize:}
%    Now we are ready to go. The first action is to clear the property
%   list as the initialization may happen several times.
%    \begin{macrocode}
\cs_new:Npn \@@_initialize: {
  \prop_clear:N  \l_@@_penalties_prop
%    \end{macrocode}
%    When \TeX{} breaks a page at a glue item with no explicit penalty involved
%    it sets \cs{outputpenalty} to \texttt{10000} in the output
%    routine to distinguish it from a case where an explicit penalty of
%    \texttt{0} was in the document. That means none of our parameters
%    or parameter combinations can be allowed to have that particular
%    value, because otherwise we would get a false match for each break
%    at glue and report an issue. So we enter that value first (by
%    hand) so that it will not be used by a parameter or parameter
%    combination.
%    \begin{macrocode}
  \prop_put:Nnn \l_@@_penalties_prop {10000} {break~ at~ glue}
%    \end{macrocode}
%    The next thing is to add the values for \cs{@lowpenalty},
%    \cs{@medpenalty}, \cs{@highpenalty} to the property list as
%    they also may show up in \cs{outputpenalty} if a user says, for
%    example, \verb=\nopagebreak[2]=.
%
%    Such a penalty from an explicit page break request does not get
%    \cs{interlinepenalty} added in.
%    \begin{macrocode}
  \@@_decide_penalty:Nn \@lowpenalty  { \@lowpenalty}
  \@@_decide_penalty:Nn \@medpenalty  { \@medpenalty}
  \@@_decide_penalty:Nn \@highpenalty { \@highpenalty}
%    \end{macrocode}
%    Then comes the first real parameter for the orphans:
%    \begin{macrocode}
  \@@_decide_penalty:Nn \@clubpenalty 
    { \@clubpenalty + \interlinepenalty }
%    \end{macrocode}
%    followed by the one for the widows and the one for the display widows:
%    \begin{macrocode}
  \@@_decide_penalty:Nn \widowpenalty 
    { \widowpenalty + \interlinepenalty ,
      \widowpenalty + \@clubpenalty  + \interlinepenalty }

  \@@_decide_penalty:Nn \displaywidowpenalty 
    { \displaywidowpenalty + \interlinepenalty ,
      \displaywidowpenalty + \@clubpenalty + \interlinepenalty }
%    \end{macrocode}
%    \cs{brokenpenalty} can appear on its own, and also with each and
%    every combination we have seen so far:
%    \begin{macrocode}
  \@@_decide_penalty:Nn \brokenpenalty
    { \brokenpenalty + \interlinepenalty ,
      \brokenpenalty + \@clubpenalty + \interlinepenalty ,
      \brokenpenalty + \widowpenalty + \interlinepenalty , 
      \brokenpenalty + \widowpenalty + \@clubpenalty + \interlinepenalty ,
      \brokenpenalty + \displaywidowpenalty + \@clubpenalty
                                            + \interlinepenalty }
%    \end{macrocode}
%    Finally we have the parameter for lonely displays (again without
%    \cs{interlinepenalty} being added):
%    \begin{macrocode}
  \@@_decide_penalty:Nn \predisplaypenalty { \predisplaypenalty }
}
%    \end{macrocode}
%    If we run the above code with \LaTeX's default parameter settings
%    in force it will make a few adjustments and the property list
%    will afterwards contain
%    the following entries:
%\begin{small}
%\begin{verbatim}
% The property list \l__fmwao_penalties_prop contains the pairs (without
% outer braces):
% >  {10000}  =>  {break at glue}
% >  {51}  =>  {\@lowpenalty }
% >  {151}  =>  {\@medpenalty }
% >  {301}  =>  {\@highpenalty }
% >  {150}  =>  {\@clubpenalty +\interlinepenalty }
% >  {152}  =>  {\widowpenalty +\interlinepenalty }
% >  {302}  =>  {\widowpenalty +\@clubpenalty +\interlinepenalty }
% >  {50}  =>  {\displaywidowpenalty +\interlinepenalty }
% >  {200}  =>  {\displaywidowpenalty +\@clubpenalty +\interlinepenalty }
% >  {100}  =>  {\brokenpenalty +\interlinepenalty }
% >  {250}  =>  {\brokenpenalty +\@clubpenalty +\interlinepenalty }
% >  {252}  =>  {\brokenpenalty +\widowpenalty +\interlinepenalty }
% >  {402}  =>  {\brokenpenalty +\widowpenalty +\@clubpenalty +\interlinepenalty }
% >  {300}  =>  {\brokenpenalty +\displaywidowpenalty +\@clubpenalty
%                                                            +\interlinepenalty }
% >  {10001}  =>  {\predisplaypenalty }.
%\end{verbatim}
%\end{small}
%  \end{macro}
%
%
%  \begin{macro}{\l_@@_tmp_int}
%  \begin{macro}{\l_@@_tmp_tl}
%  \begin{macro}{\l_@@_success_bool}
%    For doing the calculations and insertions into the
%    property list, we will also need an integer register, a token list
%    variable and another boolean variable.
%    \begin{macrocode}
\int_new:N  \l_@@_tmp_int
\tl_new:N   \l_@@_tmp_tl
\bool_new:N \l_@@_success_bool
%    \end{macrocode}
%  \end{macro}
%  \end{macro}
%  \end{macro}
%
%
%  \begin{macro}{\@@_decide_penalty:Nn}
%    This is the core command that does the real work of choosing values.
%    Let's recall that its first
%    argument is the parameter we are currently handling and the
%    second argument is a comma-separated list of cases for which we
%    need to ensure that their results are not yet in the property list.
%    \begin{macrocode}
\cs_new:Npn \@@_decide_penalty:Nn #1 #2 {
%    \end{macrocode}
%    We start by setting the boolean to \texttt{false} and then run a
%    loop until we have found a suitable parameter value that meets
%    our criteria.
%    \begin{macrocode}
  \bool_set_false:N \l_@@_success_bool
  \bool_do_until:Nn \l_@@_success_bool
%    \end{macrocode}
%    Inside the loop we start with the assumption that the current
%    value of the parameter is fine and then check if that assumption
%    is true. If yes, we can exit the loop, otherwise we will have to
%    try with a different value.
%    \begin{macrocode}
   { \bool_set_true:N \l_@@_success_bool
%    \end{macrocode}
%    For the verification we try each item in the second parameter to
%    see if that is already in the property list.
%    This means evaluating the expression to get the penalty value
%    and then looking it up in the property list. If it is there, we
%    have failed. In this case we set the boolean back to false and
%    break out of the loop over the second argument since there is
%    no point in testing further.
%    \begin{macrocode}
     \clist_map_inline:nn { #2 }
       { \int_set:Nn \l_@@_tmp_int {##1}
         \prop_get:NVNT
            \l_@@_penalties_prop \l_@@_tmp_int \l_@@_tmp_tl
            { \clist_map_break:n {\bool_set_false:N\l_@@_success_bool} }
       }
%    \end{macrocode}
%    Once we have finished, the boolean will tell us if we are
%    successful so far. If yes, it means there was no conflict.  We
%    therefore add all combinations with this parameter to the
%    property list, as from now on they are forbidden as well.
%
%
%    So we
%    map once more over the second argument and enter them:
%    \begin{macrocode}
     \bool_if:NTF \l_@@_success_bool
       { \clist_map_inline:nn { #2 }
           { \int_set:Nn \l_@@_tmp_int {##1}
             \prop_put:NVn \l_@@_penalties_prop \l_@@_tmp_int {##1}
           } }
%    \end{macrocode}
%    If we failed we increment the parameter value and retry:
%    \begin{macrocode}
       { \int_incr:N #1 }
     }
}
%    \end{macrocode}
%
%
%    One place where we will run this code is at the beginning of the
%    document (so that changes to the parameters in the document class or
%    the preamble are picked up). The other place is when the
%    user changes any of the parameters in the middle of the document
%    via \cs{WaOsetup}.
%    \begin{macrocode}
\AtBeginDocument { \@@_initialize: }
%    \end{macrocode}
%
%  \end{macro}
%
% \subsection{The option setup}
%
% The options are fairly straightforward:
%
%    \begin{macrocode}
\keys_define:nn {fmwao} {
%    \end{macrocode}
%    By default messages are given as warnings above. If anything else
%    is wanted the option \option{check} can be used which simply
%    changes the message class used internally:
%    \begin{macrocode}
  ,check .choice:
  ,check / error
     .code:n = \msg_redirect_module:nnn {widows-and-orphans}{warning}{error}
  ,check / info
     .code:n = \msg_redirect_module:nnn {widows-and-orphans}{warning}{info}
  ,check / none
     .code:n = \msg_redirect_module:nnn {widows-and-orphans}{warning}{none}
  ,check / warning
     .code:n = \msg_redirect_module:nnn {widows-and-orphans}{warning}{ }
%    \end{macrocode}
%    The other options set parameters to some hopefully ``reasonable''
%   values\Dash no real surprises here. \LaTeX{} internally uses
%   \cs{@clubpenalty} so we need to set this too, if we change
%   \cs{clubpenalty}.
%    \begin{macrocode}
  ,orphans .choice:
  ,orphans / prevent .code:n = \int_set:Nn \clubpenalty  { 10000        }
                               \int_set:Nn \@clubpenalty { \clubpenalty }
  ,orphans / avoid   .code:n = \int_set:Nn \clubpenalty  {  5000        }
                               \int_set:Nn \@clubpenalty { \clubpenalty }
  ,orphans / default .code:n = \int_set:Nn \clubpenalty  {   150        }
                               \int_set:Nn \@clubpenalty { \clubpenalty }
%    \end{macrocode}
%    
%    \begin{macrocode}
  ,widows .choice:
  ,widows / prevent  .code:n = \int_set:Nn \widowpenalty  { 10000   }
  ,widows / avoid    .code:n = \int_set:Nn \widowpenalty  {  5000   }
  ,widows / default  .code:n = \int_set:Nn \widowpenalty  {   150   }
%    \end{macrocode}
%    
%    \begin{macrocode}
  ,hyphens .choice:
  ,hyphens / prevent .code:n = \int_set:Nn \brokenpenalty { 10000   }
  ,hyphens / avoid   .code:n = \int_set:Nn \brokenpenalty {  2000   }
  ,hyphens / default .code:n = \int_set:Nn \brokenpenalty {    50   }
  ,prevent-all  .code:n = \int_set:Nn \clubpenalty        { 10000   }
                          \int_set:Nn \widowpenalty       { 10000   }
                          \int_set:Nn \displaywidowpenalty{ 10000   }
                          \int_set:Nn \brokenpenalty      { 10000   }
                          \int_set:Nn \predisplaypenalty  { 10000   }
                          \int_set:Nn \@clubpenalty  { \clubpenalty }
%    \end{macrocode}
%    As an exception, \option{avoid-all} doesn't set
%    \cs{predisplaypenalty}; maybe it should.
%    \begin{macrocode}
  ,avoid-all    .code:n = \int_set:Nn \clubpenalty         { 5000   }
                          \int_set:Nn \widowpenalty        { 5000   }
                          \int_set:Nn \displaywidowpenalty { 2000   }
                          \int_set:Nn \brokenpenalty       { 2000   }
%                         \int_set:Nn \predisplaypenalty   { 9999   }
                          \int_set:Nn \@clubpenalty  { \clubpenalty }
%    \end{macrocode}
%    \option{default-all} reverts back to the standard \LaTeX{}
%    default values:
%    \begin{macrocode}
  ,default-all  .code:n = \int_set:Nn \clubpenalty          { 150   }
                          \int_set:Nn \widowpenalty         { 150   }
                          \int_set:Nn \displaywidowpenalty  {  50   }
                          \int_set:Nn \brokenpenalty        { 100   }
                          \int_set:Nn \predisplaypenalty  { 10000   }
                          \int_set:Nn \@clubpenalty  { \clubpenalty }
}
%    \end{macrocode}
%    Once declared we evaluate the options given to the package:
%    \begin{macrocode}
\ProcessKeysPackageOptions{fmwao}
%    \end{macrocode}
%
%
% \subsection{Document-level commands}
%
% Finally we declare the user-level commands:
%

%  \begin{macro}{\WaOsetup}
%    This runs the key setup on the first argument and then
%    reinitializes the parameter setup:
%    \begin{macrocode}
\NewDocumentCommand\WaOsetup{m}
  { \keys_set:nn{fmwao}{#1}  \@@_initialize: \ignorespaces }
%    \end{macrocode}
%  \end{macro}
%
%

%  \begin{macro}{\WaOparameters}
%    This parameterless command outputs a display of the
%    current parameter settings.
%    \begin{macrocode}
\NewDocumentCommand\WaOparameters{}{\prop_show:N \l_@@_penalties_prop}
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}{\WaOignorenext}
%    And here is the command that suppresses any warning on the
%    current page or column:
%    \begin{macrocode}
\NewDocumentCommand\WaOignorenext{}
  { \bool_gset_false:N \g_@@_gen_warn_bool }
%    \end{macrocode}
%  \end{macro}
%
% \small\Finale
%

\endinput