largely complete automated generation of cheat sheets & bindings from templates,...
[ardour.git] / tools / fmt-bindings
1 #!/usr/bin/perl
2
3 # import module
4 use Getopt::Long; 
5
6 $semicolon = ";"; # help out stupid emacs
7 $in_group_def = 0;
8 $group_name;
9 $group_text;
10 $group_key;
11 $group_number = 0;
12 %group_names;
13 %group_text;
14 %group_bindings;
15 %modifier_map;
16 %group_numbering;
17
18 $platform = linux;
19
20 GetOptions ("platform=s" => \$platform);
21
22 if ($platform eq "osx") {
23     $gtk_modifier_map{'PRIMARY'} = 'meta';
24     $gtk_modifier_map{'SECONDARY'} = 'Mod1';
25     $gtk_modifier_map{'TERTIARY'} = 'Shift';
26     $gtk_modifier_map{'LEVEL4'} = 'Control';
27     $gtk_modifier_map{'WINDOW'} = 'Mod1';
28
29     $cs_modifier_map{'PRIMARY'} = 'Command';
30     $cs_modifier_map{'SECONDARY'} = 'Alt';
31     $cs_modifier_map{'TERTIARY'} = 'Shift';
32     $cs_modifier_map{'LEVEL4'} = 'Control';
33     $cs_modifier_map{'WINDOW'} = 'Alt';
34
35 } else {
36
37     $gtk_modifier_map{'PRIMARY'} = 'Control';
38     $gtk_modifier_map{'SECONDARY'} = 'Alt';
39     $gtk_modifier_map{'TERTIARY'} = 'Shift';
40     $gtk_modifier_map{'LEVEL4'} = 'Mod4';
41     $gtk_modifier_map{'WINDOW'} = 'Alt';
42
43     $cs_modifier_map{'PRIMARY'} = 'Control';
44     $cs_modifier_map{'SECONDARY'} = 'Alt';
45     $cs_modifier_map{'TERTIARY'} = 'Shift';
46     $cs_modifier_map{'LEVEL4'} = 'Win';
47     $cs_modifier_map{'WINDOW'} = 'Alt';
48 }
49
50 %keycodes = (
51     'asciicircum' => '\\verb=^=',
52     'apostrophe' => '\'',
53     'bracketleft' => '[',
54     'bracketright' => ']',
55     'braceleft' => '\\{',
56     'braceright' => '\\}',
57     'backslash' => '$\\backslash$',
58     'rightanglebracket' => '>',
59     'leftanglebracket' => '<',
60     'ampersand' => '\\&',
61     'comma' => ',',
62     'period' => '.',
63     'semicolon' => ';',
64     'colon' => ':',
65     'equal' => '=',
66     'minus' => '-',
67     'plus' => '+',
68     'grave' => '`',
69     'rightarrow' => '$\rightarrow$',
70     'leftarrow' => '$\\leftarrow$',
71     'uparrow' => '$\\uparrow$',
72     'downarrow' => '$\\downarrow$',
73     'Page_Down' => 'Page Down',
74     'Page_Up' => 'Page Up',
75     'space' => 'space',
76     'KP_' => 'KP$\_$'
77     );
78
79 while (<>) {
80     next if /^$semicolon/;
81
82     if (/^%/) {
83         
84         if ($in_group_def) {
85             chop $group_text;
86             $group_names{$group_key} = $group_name;
87             $group_text{$group_key} = $group_text;
88             $group_numbering{$group_key} = $group_number;
89             # each binding entry is 2 element array. bindings
90             # are all collected into a container array. create
91             # the first dummy entry so that perl knows what we
92             # are doing.
93             $group_bindings{$group_key} = [ [] ];
94         }
95
96         s/^%//;
97         chop;
98         ($group_key,$group_name) = split (/\s+/, $_, 2);
99         $group_number++;
100         $group_text = "";
101         $in_group_def = 1;
102         next;
103     }
104
105     if ($in_group_def) {
106         if (/^@/) {
107             chop $group_text;
108             $group_names{$group_key} = $group_name;
109             $group_text{$group_key} = $group_text;
110             $in_group_def = 0;
111         } else {
112             next if (/^[ \t]+$/);
113             $group_text .= $_;
114             $group_text;
115             next;
116         }
117     }
118
119     if (/^@/) {
120         s/^@//;
121         chop;
122         ($key,$action,$binding,$text) = split (/\|/, $_, 4);
123
124         # substitute bindings
125
126         $gtk_binding = $binding;
127
128         foreach $k (keys %gtk_modifier_map) {
129             $gtk_binding =~ s/\@$k\@/$gtk_modifier_map{$k}/;
130         }
131
132         # print the accelmap output
133
134         if ($key =~ /^\+/) {
135             # remove + and don't print it in the accelmap
136             $key =~ s/^\+//;
137         } else {
138             # include this in the accelmap
139             # print "(gtk_accel_map \"<Actions>/$action\" \"$gtk_binding\")\n";
140         }
141
142         if ($key =~ /^-/) {
143             # do not include this binding in the cheat sheet
144             next;
145         }
146
147         $bref = $group_bindings{$key};
148         push (@$bref, [$binding, $text]);
149
150         next;
151     }
152
153     next;
154 }
155
156 # Now print the cheatsheet
157
158 $boilerplate_header = <<END_HEADER;
159 \\documentclass[10pt,landscape]{article}
160 \\usepackage{multicol}
161 \\usepackage{calc}
162 \\usepackage{ifthen}
163 \\usepackage{palatino}
164 \\usepackage[landscape]{geometry}
165
166
167 % This sets page margins to .5 inch if using letter paper, and to 1cm
168 % if using A4 paper. (This probably isnott strictly necessary.)
169 % If using another size paper, use default 1cm margins.
170 \\ifthenelse{\\lengthtest { \\paperwidth = 11in}}
171         { \\geometry{top=.5in,left=.5in,right=.5in,bottom=.5in} }
172         {\\ifthenelse{ \\lengthtest{ \\paperwidth = 297mm}}
173                 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
174                 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
175         }
176
177 % Turn off header and footer
178 \\pagestyle{empty}
179  
180 % Redefine section commands to use less space
181 \\makeatletter
182 \\renewcommand{\\section}{\\\@startsection{section}{1}{0mm}%
183                                 {-1ex plus -.5ex minus -.2ex}%
184                                 {0.5ex plus .2ex}%x
185                                 {\\normalfont\\large\\bfseries}}
186 \\renewcommand{\\subsection}{\\\@startsection{subsection}{2}{0mm}%
187                                 {-1explus -.5ex minus -.2ex}%
188                                 {0.5ex plus .2ex}%
189                                 {\\normalfont\\normalsize\\bfseries}}
190 \\renewcommand{\\subsubsection}{\\\@startsection{subsubsection}{3}{0mm}%
191                                 {-1ex plus -.5ex minus -.2ex}%
192                                 {1ex plus .2ex}%
193                                 {\\normalfont\\small\\bfseries}}
194 \\makeatother
195
196 % Define BibTeX command
197 \\def\\BibTeX{{\\rm B\\kern-.05em{\\sc i\\kern-.025em b}\\kern-.08em
198     T\\kern-.1667em\\lower.7ex\\hbox{E}\\kern-.125emX}}
199
200 % Do not print section numbers
201 \\setcounter{secnumdepth}{0}
202
203 \\setlength{\\parindent}{0pt}
204 \\setlength{\\parskip}{0pt plus 0.5ex}
205
206 %-------------------------------------------
207
208 \\begin{document}
209 \\newlength{\\MyLen}
210 \\raggedright
211 \\footnotesize
212 \\begin{multicols}{3}
213 END_HEADER
214
215 $boilerplate_footer = <<END_FOOTER;
216 \\end{multicols}
217 \\end{document}
218 END_FOOTER
219
220 print $boilerplate_header;
221
222 @groups_sorted_by_number = sort { $group_numbering{$a} <=> $group_numbering{$b} } keys %group_numbering; 
223
224 foreach $gk (@groups_sorted_by_number) {
225     # $bref is a reference to the array of arrays for this group
226     $bref = $group_bindings{$gk};
227
228     if (scalar @$bref > 1) {
229         print "\\section*{$group_names{$gk}}\n";
230
231         if (!($group_text{$gk} eq  "")) {
232             print "$group_text{$gk}\n\\par\n";
233         }
234         
235         # ignore the first entry, which was empty
236
237         shift (@$bref);
238
239         # find the longest descriptive text (this is not 100% accuracy due to typography)
240
241         $maxtextlen = 0;
242         $maxtext = "";
243
244         for $bbref (@$bref) {
245             # $bbref is a reference to an array
246             $text = @$bbref[1];
247             if ($text =~ /\\linebreak/) {
248                 $matchtext = s/\\linebreak.*//;
249             } else {
250                 $matchtext = $text;
251             }
252             if (length ($matchtext) > $maxtextlen) {
253                 $maxtextlen = length ($matchtext);
254                 $maxtext = $matchtext;
255             }
256         }
257
258         $maxtext .= "....";
259
260         # set up the table
261
262         print "\\settowidth{\\MyLen}{\\texttt{$maxtext}}\n";
263         print "\\begin{tabular}{\@{}p{\\the\\MyLen}% 
264                                 \@{}p{\\linewidth-\\the\\MyLen}%
265                                 \@{}}\n";
266
267         # now print the bindings
268
269         for $bbref (@$bref) {
270             # $bbref is a reference to an array
271
272             $binding = @$bbref[0];
273             $text = @$bbref[1];
274
275             if ($binding =~ /:/) { # mouse binding with "where" clause
276                 ($binding,$where) = split (/:/, $binding, 2);
277             }
278
279             $binding =~ s/></\+/g;
280             $binding =~ s/^<//;
281             $binding =~ s/>/\+/;
282
283             foreach $k (keys %cs_modifier_map) {
284                 $binding =~ s/\@$k\@/$cs_modifier_map{$k}/;
285             }
286
287             # substitute keycode names for something printable
288
289             $re = qr/${ \(join'|', map quotemeta, keys %keycodes)}/;
290             $binding =~ s/($re)/$keycodes{$1}/g;
291
292             # split up mouse bindings to "click" and "where" parts
293
294             if ($gk eq "mobject") {
295                 print "{\\tt @$bbref[1] } & {\\tt $binding} {\\it $where}\\\\\n";
296             } else {
297                 print "{\\tt @$bbref[1] } & {\\tt $binding} \\\\\n";
298             }
299         }
300
301         print "\\end{tabular}\n";
302
303     }
304 }
305
306 print $boilerplate_footer;
307
308 exit 0;