Map window bindings to alt.
[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 $title = "Ardour Shortcuts";
8 $in_group_def = 0;
9 $group_name;
10 $group_text;
11 $group_key;
12 $group_number = 0;
13 %group_names;
14 %group_text;
15 %group_bindings;
16 %modifier_map;
17 %group_numbering;
18 %merge_bindings;
19
20 $platform = linux;
21 $winkey = 'Win';
22 $make_cheatsheet = 1;
23 $make_accelmap = 0;
24 $merge_from = "";
25 $html = 0;
26
27 GetOptions ("platform=s" => \$platform,
28             "winkey=s" => \$winkey,
29             "cheatsheet" => \$make_cheatsheet,
30             "accelmap" => \$make_accelmap,
31             "merge=s" => \$merge_from,
32             "html" => \$html);
33
34 if ($platform eq "darwin") {
35
36     $gtk_modifier_map{'PRIMARY'} = 'Primary';
37     $gtk_modifier_map{'SECONDARY'} = 'Control';
38     $gtk_modifier_map{'TERTIARY'} = 'Shift';
39     $gtk_modifier_map{'LEVEL4'} = 'Mod1';
40     $gtk_modifier_map{'WINDOW'} = 'Alt';
41
42     $cs_modifier_map{'PRIMARY'} = 'Cmd';
43     $cs_modifier_map{'SECONDARY'} = 'Control';
44     $cs_modifier_map{'TERTIARY'} = 'Shift';
45     $cs_modifier_map{'LEVEL4'} = 'Mod1';
46     $cs_modifier_map{'WINDOW'} = 'Alt';
47
48     $mouse_modifier_map{'PRIMARY'} = 'Cmd';
49     $mouse_modifier_map{'SECONDARY'} = 'Ctrl';
50     $mouse_modifier_map{'TERTIARY'} = 'Shift';
51     $mouse_modifier_map{'LEVEL4'} = 'Opt';
52     $mouse_modifier_map{'WINDOW'} = 'Ctrl';
53
54 } else {
55
56     $gtk_modifier_map{'PRIMARY'} = 'Control';
57     $gtk_modifier_map{'SECONDARY'} = 'Alt';
58     $gtk_modifier_map{'TERTIARY'} = 'Shift';
59     $gtk_modifier_map{'LEVEL4'} = 'Meta';
60     $gtk_modifier_map{'WINDOW'} = 'Alt';
61     $gtk_modifier_map{$winkey} => 'Meta';
62
63     $cs_modifier_map{'PRIMARY'} = 'Control';
64     $cs_modifier_map{'SECONDARY'} = 'Alt';
65     $cs_modifier_map{'TERTIARY'} = 'Shift';
66     $cs_modifier_map{'LEVEL4'} = 'Meta';
67     $cs_modifier_map{'WINDOW'} = 'Alt';
68     $cs_modifier_map{$winkey} => 'Meta';
69
70     $mouse_modifier_map{'PRIMARY'} = 'Ctl';
71     $mouse_modifier_map{'SECONDARY'} = 'Alt';
72     $mouse_modifier_map{'TERTIARY'} = 'Shift';
73     $mouse_modifier_map{'LEVEL4'} = 'Meta';
74     $mouse_modifier_map{'WINDOW'} = 'Alt';
75     $mouse_modifier_map{$winkey} => 'Meta';
76 }
77
78 %keycodes = ();
79
80 if ($html) {
81     %keycodes = (
82         'asciicircum' => '^',
83         'apostrophe' => '\'',
84         'bracketleft' => '[',
85         'bracketright' => ']',
86         'braceleft' => '{',
87         'braceright' => '}',
88         'backslash' => '\\',
89         'slash' => '/',
90         'rightanglebracket' => '>',
91         'leftanglebracket' => '<',
92         'ampersand' => '&',
93         'comma' => ',',
94         'period' => '.',
95         'semicolon' => ';',
96         'colon' => ':',
97         'equal' => '=',
98         'minus' => '-',
99         'plus' => '+',
100         'grave' => '`',
101         'rightarrow' => '→',
102         'leftarrow' => '←',
103         'uparrow' => '↑',
104         'downarrow' => '↓',
105         'Page_Down' => 'PageDown',
106         'Page_Up' => 'PageUp',
107         'space' => 'space',
108         'KP_Right' => 'KP-→',
109         'KP_Left' => 'KP-←',
110         'KP_Up' => 'KP-↑',
111         'KP_Down' => 'KP-↓',
112         'KP_0' => 'KP-0;',
113         'greater' => '>',
114         'less' => '<',
115         );
116 } else {
117
118     %keycodes = (
119         'asciicircum' => '\\verb=^=',
120         'apostrophe' => '\'',
121         'bracketleft' => '[',
122         'bracketright' => ']',
123         'braceleft' => '\\{',
124         'braceright' => '\\}',
125         'backslash' => '$\\backslash$',
126         'slash' => '/',
127         'rightanglebracket' => '>',
128         'leftanglebracket' => '<',
129         'ampersand' => '\\&',
130         'comma' => ',',
131         'period' => '.',
132         'semicolon' => ';',
133         'colon' => ':',
134         'equal' => '=',
135         'minus' => '-',
136         'plus' => '+',
137         'grave' => '`',
138         'rightarrow' => '$\rightarrow$',
139         'leftarrow' => '$\\leftarrow$',
140         'uparrow' => '$\\uparrow$',
141         'downarrow' => '$\\downarrow$',
142         'Page_Down' => 'Page Down',
143         'Page_Up' => 'Page Up',
144         'space' => 'space',
145         'KP_' => 'KP$\_$',
146         'greater' => '>',
147         'less' => '<',
148     );
149 }
150
151 if ($merge_from) {
152     open (BINDINGS, $merge_from) || die ("merge from bindings: file not readable");
153     while (<BINDINGS>) {
154         next if (/^$semicolon/);
155         if (/^\(gtk_accel/) {
156             chop; # newline
157             chop; # closing parenthesis
158             s/"//g;
159             ($junk, $action, $binding) = split;
160             $merge_bindings{$action} = $binding;
161         }
162     }
163     close (BINDINGS);
164 }
165
166 if ($make_accelmap && !$merge_from) {
167     print ";; this accelmap was produced by tools/fmt-bindings\n";
168 }
169
170 while (<>) {
171     next if /^$semicolon/;
172
173     if (/^\$/) {
174         s/^\$//;
175         $title = $_;
176         next;
177     }
178
179     if (/^%/) {
180         
181         if ($in_group_def) {
182             chop $group_text;
183             $group_names{$group_key} = $group_name;
184             $group_text{$group_key} = $group_text;
185             $group_numbering{$group_key} = $group_number;
186             # each binding entry is 2 element array. bindings
187             # are all collected into a container array. create
188             # the first dummy entry so that perl knows what we
189             # are doing.
190             $group_bindings{$group_key} = [ [] ];
191         }
192
193         s/^%//;
194         chop;
195         ($group_key,$group_name) = split (/\s+/, $_, 2);
196         $group_number++;
197         $group_text = "";
198         $in_group_def = 1;
199         next;
200     }
201
202     if ($in_group_def) {
203         if (/^@/) {
204             chop $group_text;
205             $group_names{$group_key} = $group_name;
206             $group_text{$group_key} = $group_text;
207             $in_group_def = 0;
208         } else {
209             next if (/^[ \t]+$/);
210             $group_text .= $_;
211             $group_text;
212             next;
213         }
214     }
215
216     if (/^@/) {
217         s/^@//;
218         chop;
219         ($key,$action,$binding,$text) = split (/\|/, $_, 4);
220
221         # substitute bindings
222
223         $gtk_binding = $binding;
224
225         if ($merge_from) {
226             $lookup = "<Actions>/" . $action;
227             if ($merge_bindings{$lookup}) {
228                 $binding = $merge_bindings{$lookup};
229             } else {
230                 if ($key =~ /^\+/) {
231                     # forced inclusion of bindings from template
232                 } else {
233                     # this action is not defined in the merge from set, so forget it 
234                     next;
235                 }
236             }
237         } 
238
239         # print the accelmap output
240
241         if ($key =~ /^\+/) {
242             # remove + and don't print it in the accelmap
243             $key =~ s/^\+//;
244         } else {
245             # include this in the accelmap
246             if (!$merge_from && $make_accelmap) {
247                 foreach $k (keys %gtk_modifier_map) {
248                     $gtk_binding =~ s/\@$k\@/$gtk_modifier_map{$k}/;
249                 }
250                 print "(gtk_accel_path \"<Actions>/$action\" \"$gtk_binding\")\n";
251             }
252         }
253
254         if ($key =~ /^-/) {
255             # do not include this binding in the cheat sheet
256             next;
257         }
258
259         $bref = $group_bindings{$key};
260         push (@$bref, [$binding, $text]);
261
262         next;
263     }
264
265     next;
266 }
267
268 if ($make_accelmap || !$make_cheatsheet) {
269     exit 0;
270 }
271
272 if ($html) {
273
274     @groups_sorted_by_number = sort { $group_numbering{$a} <=> $group_numbering{$b} } keys %group_numbering; 
275     
276     foreach $gk (@groups_sorted_by_number) {
277
278         if ($gk =~ /^m/) {
279             # mouse stuff - ignore
280             next;
281         }
282
283         # $bref is a reference to the array of arrays for this group
284         $bref = $group_bindings{$gk};
285         
286         if (scalar @$bref > 1) {
287             
288             $name = $group_names{$gk};
289             $name =~ s/\\linebreak.*//;
290             $name =~ s/\\&/&/;
291             $name =~ s/\$\\_\$/-/g;
292             $name =~ s/\\[a-z]+ //g;
293             $name =~ s/[{}]//g;
294             $name =~ s/\\par//g;
295
296             print "<h3>$name</h3>\n";
297
298             $gtext = $group_text{$gk};
299             $gtext =~ s/\\linebreak.*//;
300             $gtext =~ s/\\&/&/;
301             $gtext =~ s/\$\\_\$/-/g;
302             $gtext =~ s/\\[a-z]+ //g;
303             $gtext =~ s/[{}]//g;
304             $gtext =~ s/\\par//g;
305             
306             if (!($gtext eq  "")) {
307                 print "$gtext\n\n";
308             }
309             
310             # ignore the first entry, which was empty
311             
312             shift (@$bref);
313             
314             # set up the list
315             
316             print "<dl class=\"bindings\">\n";
317             
318             # sort the array of arrays by the descriptive text for nicer appearance,
319             # and print them
320             
321             for $bbref (sort { @$a[1] cmp @$b[1] } @$bref) {
322                 # $bbref is a reference to an array
323                 
324                 $binding = @$bbref[0];
325                 $text = @$bbref[1];
326
327                 if ($binding =~ /:/) { # mouse binding with "where" clause
328                     ($binding,$where) = split (/:/, $binding, 2);
329                 }
330                 
331                 foreach $k (keys %cs_modifier_map) {
332                     $binding =~ s/\@$k\@/$cs_modifier_map{$k}/;
333                 }
334
335                 # remove braces for HTML
336
337                 $binding =~ s/></\+/g;
338                 $binding =~ s/^<//;
339                 $binding =~ s/>/\+/;
340                 
341                 # substitute keycode names for something printable
342                 
343                 $re = qr/${ \(join'|', map quotemeta, keys %keycodes)}/;
344                 $binding =~ s/($re)/$keycodes{$1}/g;
345
346                 # tidy up description
347
348                 $descr = @$bbref[1];
349                 $descr =~ s/\\linebreak.*//;
350                 $descr =~ s/\\&/&/;
351                 $descr =~ s/\$\\_\$/-/g;
352                 $descr =~ s/\\[a-z]+ //g;
353                 $descr =~ s/[{}]//g;
354                 $descr =~ s/\\par//g;
355
356                 print "<dt>$descr</dt><dd>$binding</dd>\n";
357             }
358             
359             print "</dl>\n";
360         
361         }
362     }
363     print "&nbsp; <!-- remove this if more text is added below -->\n";
364     exit 0;
365 }
366
367
368 # Now print the cheatsheet
369
370 $boilerplate_header = <<END_HEADER;
371 \\documentclass[10pt,landscape]{article}
372 %\\documentclass[10pt,landscape,a4paper]{article}
373 %\\documentclass[10pt,landscape,letterpaper]{article}
374 \\usepackage{multicol}
375 \\usepackage{calc}
376 \\usepackage{ifthen}
377 \\usepackage{palatino}
378 \\usepackage{geometry}
379
380 \\setlength{\\parskip}{0pt}
381 \\setlength{\\parsep}{0pt}
382 \\setlength{\\headsep}{0pt}
383 \\setlength{\\topskip}{0pt}
384 \\setlength{\\topmargin}{0pt}
385 \\setlength{\\topsep}{0pt}
386 \\setlength{\\partopsep}{0pt}
387
388 % This sets page margins to .5 inch if using letter paper, and to 1cm
389 % if using A4 paper. (This probably isnott strictly necessary.)
390 % If using another size paper, use default 1cm margins.
391 \\ifthenelse{\\lengthtest { \\paperwidth = 11in}}
392         { \\geometry{top=.5in,left=.5in,right=.5in,bottom=.5in} }
393         {\\ifthenelse{ \\lengthtest{ \\paperwidth = 297mm}}
394                 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
395                 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
396         }
397
398 % Turn off header and footer
399 \\pagestyle{empty}
400  
401 % Redefine section commands to use less space
402 \\makeatletter
403 \\renewcommand{\\section}{\\\@startsection{section}{1}{0mm}%
404                                 {-1ex plus -.5ex minus -.2ex}%
405                                 {0.5ex plus .2ex}%
406                                 {\\normalfont\\large\\bfseries}}
407 \\renewcommand{\\subsection}{\\\@startsection{subsection}{2}{0mm}%
408                                 {-1explus -.5ex minus -.2ex}%
409                                 {0.5ex plus .2ex}%
410                                 {\\normalfont\\normalsize\\bfseries}}
411 \\renewcommand{\\subsubsection}{\\\@startsection{subsubsection}{3}{0mm}%
412                                 {-1ex plus -.5ex minus -.2ex}%
413                                 {1ex plus .2ex}%
414                                 {\\normalfont\\small\\bfseries}}
415 \\makeatother
416
417 % Do not print section numbers% Do not print section numbers
418 \\setcounter{secnumdepth}{0}
419
420 \\setlength{\\parindent}{0pt}
421 \\setlength{\\parskip}{0pt plus 0.5ex}
422
423 %-------------------------------------------
424
425 \\begin{document}
426 \\newlength{\\MyLen}
427 \\raggedright
428 \\footnotesize
429 \\begin{multicols}{3}
430 END_HEADER
431
432 $boilerplate_footer = <<END_FOOTER;
433 \\rule{0.3\\linewidth}{0.25pt}
434 \\scriptsize
435
436 Copyright \\copyright\\ 2013 ardour.org
437
438 % Should change this to be date of file, not current date.
439
440 http://manual.ardour.org
441
442 \\end{multicols}
443 \\end{document}
444 END_FOOTER
445
446 if ($make_cheatsheet) {
447     print $boilerplate_header;
448     print "\\begin{center}\\Large\\bf $title \\end{center}\n";
449 }
450
451 @groups_sorted_by_number = sort { $group_numbering{$a} <=> $group_numbering{$b} } keys %group_numbering; 
452
453 foreach $gk (@groups_sorted_by_number) {
454     # $bref is a reference to the array of arrays for this group
455     $bref = $group_bindings{$gk};
456
457     if (scalar @$bref > 1) {
458         print "\\section{$group_names{$gk}}\n";
459
460         if (!($group_text{$gk} eq  "")) {
461             print "$group_text{$gk}\n\\par\n";
462         }
463         
464         # ignore the first entry, which was empty
465
466         shift (@$bref);
467
468         # find the longest descriptive text (this is not 100% accuracy due to typography)
469
470         $maxtextlen = 0;
471         $maxtext = "";
472
473         for $bbref (@$bref) {
474             # $bbref is a reference to an array
475             $text = @$bbref[1];
476             
477             #
478             # if there is a linebreak, just use everything up the linebreak
479             # to determine the width
480             #
481
482             if ($text =~ /\\linebreak/) {
483                 $matchtext = s/\\linebreak.*//;
484             } else {
485                 $matchtext = $text;
486             }
487             if (length ($matchtext) > $maxtextlen) {
488                 $maxtextlen = length ($matchtext);
489                 $maxtext = $matchtext;
490             }
491         }
492
493         if ($gk =~ /^m/) {
494             # mouse mode: don't extend max text at all - space it tight
495             $maxtext .= ".";
496         } else {
497             $maxtext .= "....";
498         }
499
500         # set up the table
501
502         print "\\settowidth{\\MyLen}{\\texttt{$maxtext}}\n";
503         print "\\begin{tabular}{\@{}p{\\the\\MyLen}% 
504                                 \@{}p{\\linewidth-\\the\\MyLen}%
505                                 \@{}}\n";
506
507         # sort the array of arrays by the descriptive text for nicer appearance,
508         # and print them
509
510         for $bbref (sort { @$a[1] cmp @$b[1] } @$bref) {
511             # $bbref is a reference to an array
512
513             $binding = @$bbref[0];
514             $text = @$bbref[1];
515
516             if ($binding =~ /:/) { # mouse binding with "where" clause
517                 ($binding,$where) = split (/:/, $binding, 2);
518             }
519
520             if ($gk =~ /^m/) {
521                 # mouse mode - use shorter abbrevs
522                 foreach $k (keys %mouse_modifier_map) {
523                     $binding =~ s/\@$k\@/$mouse_modifier_map{$k}/;
524                 }
525             } else {
526                 foreach $k (keys %cs_modifier_map) {
527                     $binding =~ s/\@$k\@/$cs_modifier_map{$k}/;
528                 }
529             }
530
531             $binding =~ s/></\+/g;
532             $binding =~ s/^<//;
533             $binding =~ s/>/\+/;
534
535             # substitute keycode names for something printable
536
537             $re = qr/${ \(join'|', map quotemeta, keys %keycodes)}/;
538             $binding =~ s/($re)/$keycodes{$1}/g;
539
540             # split up mouse bindings to "click" and "where" parts
541
542             if ($gk eq "mobject") {
543                 print "{\\tt @$bbref[1] } & {\\tt $binding} {\\it $where}\\\\\n";
544             } else {
545                 print "{\\tt @$bbref[1] } & {\\tt $binding} \\\\\n";
546             }
547         }
548
549         print "\\end{tabular}\n";
550
551     }
552 }
553
554 print $boilerplate_footer;
555
556 exit 0;