Merge branch 'master' of https://github.com/nmains/ardour
[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 = 'Mod4\>\<Super';
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'} = 'Control';
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'} = 'Control';
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'} = $winkey;
60     $gtk_modifier_map{'WINDOW'} = 'Alt';
61     $gtk_modifier_map{$winkey} => 'Win';
62
63     $cs_modifier_map{'PRIMARY'} = 'Control';
64     $cs_modifier_map{'SECONDARY'} = 'Alt';
65     $cs_modifier_map{'TERTIARY'} = 'Shift';
66     $cs_modifier_map{'LEVEL4'} = 'Win';
67     $cs_modifier_map{'WINDOW'} = 'Alt';
68     $cs_modifier_map{$winkey} => 'Win';
69
70     $mouse_modifier_map{'PRIMARY'} = 'Ctl';
71     $mouse_modifier_map{'SECONDARY'} = 'Alt';
72     $mouse_modifier_map{'TERTIARY'} = 'Shift';
73     $mouse_modifier_map{'LEVEL4'} = 'Win';
74     $mouse_modifier_map{'WINDOW'} = 'Alt';
75     $mouse_modifier_map{$winkey} => 'Win';
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' => '&gt;',
91         'leftanglebracket' => '&lt;',
92         'ampersand' => '&',
93         'comma' => ',',
94         'period' => '.',
95         'semicolon' => ';',
96         'colon' => ':',
97         'equal' => '=',
98         'minus' => '-',
99         'plus' => '+',
100         'grave' => '`',
101         'rightarrow' => '&rarr;',
102         'leftarrow' => '&larr;',
103         'uparrow' => '&uarr;',
104         'downarrow' => '&darr;',
105         'Page_Down' => 'PageDown',
106         'Page_Up' => 'PageUp',
107         'space' => 'space',
108         'KP_Right' => 'KP-&rarr;',
109         'KP_Left' => 'KP-&larr;',
110         'KP_Up' => 'KP-&uarr;',
111         'KP_Down' => 'KP-&darr;',
112         'KP_0' => 'KP-0;',
113         'greater' => '&gt;',
114         'less' => '&lt;',
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 \\usepackage{multicol}
373 \\usepackage{calc}
374 \\usepackage{ifthen}
375 \\usepackage{palatino}
376 \\usepackage{geometry}
377
378 \\setlength{\\parskip}{0pt}
379 \\setlength{\\parsep}{0pt}
380 \\setlength{\\headsep}{0pt}
381 \\setlength{\\topskip}{0pt}
382 \\setlength{\\topmargin}{0pt}
383 \\setlength{\\topsep}{0pt}
384 \\setlength{\\partopsep}{0pt}
385
386 % This sets page margins to .5 inch if using letter paper, and to 1cm
387 % if using A4 paper. (This probably isnott strictly necessary.)
388 % If using another size paper, use default 1cm margins.
389 \\ifthenelse{\\lengthtest { \\paperwidth = 11in}}
390         { \\geometry{top=.5in,left=1in,right=0in,bottom=.5in} }
391         {\\ifthenelse{ \\lengthtest{ \\paperwidth = 297mm}}
392                 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
393                 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
394         }
395
396 % Turn off header and footer
397 \\pagestyle{empty}
398  
399 % Redefine section commands to use less space
400 \\makeatletter
401 \\renewcommand{\\section}{\\\@startsection{section}{1}{0mm}%
402                                 {-1ex plus -.5ex minus -.2ex}%
403                                 {0.5ex plus .2ex}%
404                                 {\\normalfont\\large\\bfseries}}
405 \\renewcommand{\\subsection}{\\\@startsection{subsection}{2}{0mm}%
406                                 {-1explus -.5ex minus -.2ex}%
407                                 {0.5ex plus .2ex}%
408                                 {\\normalfont\\normalsize\\bfseries}}
409 \\renewcommand{\\subsubsection}{\\\@startsection{subsubsection}{3}{0mm}%
410                                 {-1ex plus -.5ex minus -.2ex}%
411                                 {1ex plus .2ex}%
412                                 {\\normalfont\\small\\bfseries}}
413 \\makeatother
414
415 % Do not print section numbers% Do not print section numbers
416 \\setcounter{secnumdepth}{0}
417
418 \\setlength{\\parindent}{0pt}
419 \\setlength{\\parskip}{0pt plus 0.5ex}
420
421 %-------------------------------------------
422
423 \\begin{document}
424 \\newlength{\\MyLen}
425 \\raggedright
426 \\footnotesize
427 \\begin{multicols}{3}
428 END_HEADER
429
430 $boilerplate_footer = <<END_FOOTER;
431 \\rule{0.3\\linewidth}{0.25pt}
432 \\scriptsize
433
434 Copyright \\copyright\\ 2009 ardour.org
435
436 % Should change this to be date of file, not current date.
437 %\\verb!$Revision: 1.13 $, $Date: 2008/05/29 06:11:56 $.!
438
439 http://ardour.org/manual
440
441 \\end{multicols}
442 \\end{document}
443 END_FOOTER
444
445 if ($make_cheatsheet) {
446     print $boilerplate_header;
447     print "\\begin{center}\\Large\\bf $title \\end{center}\n";
448 }
449
450 @groups_sorted_by_number = sort { $group_numbering{$a} <=> $group_numbering{$b} } keys %group_numbering; 
451
452 foreach $gk (@groups_sorted_by_number) {
453     # $bref is a reference to the array of arrays for this group
454     $bref = $group_bindings{$gk};
455
456     if (scalar @$bref > 1) {
457         print "\\section{$group_names{$gk}}\n";
458
459         if (!($group_text{$gk} eq  "")) {
460             print "$group_text{$gk}\n\\par\n";
461         }
462         
463         # ignore the first entry, which was empty
464
465         shift (@$bref);
466
467         # find the longest descriptive text (this is not 100% accuracy due to typography)
468
469         $maxtextlen = 0;
470         $maxtext = "";
471
472         for $bbref (@$bref) {
473             # $bbref is a reference to an array
474             $text = @$bbref[1];
475             
476             #
477             # if there is a linebreak, just use everything up the linebreak
478             # to determine the width
479             #
480
481             if ($text =~ /\\linebreak/) {
482                 $matchtext = s/\\linebreak.*//;
483             } else {
484                 $matchtext = $text;
485             }
486             if (length ($matchtext) > $maxtextlen) {
487                 $maxtextlen = length ($matchtext);
488                 $maxtext = $matchtext;
489             }
490         }
491
492         if ($gk =~ /^m/) {
493             # mouse mode: don't extend max text at all - space it tight
494             $maxtext .= ".";
495         } else {
496             $maxtext .= "....";
497         }
498
499         # set up the table
500
501         print "\\settowidth{\\MyLen}{\\texttt{$maxtext}}\n";
502         print "\\begin{tabular}{\@{}p{\\the\\MyLen}% 
503                                 \@{}p{\\linewidth-\\the\\MyLen}%
504                                 \@{}}\n";
505
506         # sort the array of arrays by the descriptive text for nicer appearance,
507         # and print them
508
509         for $bbref (sort { @$a[1] cmp @$b[1] } @$bref) {
510             # $bbref is a reference to an array
511
512             $binding = @$bbref[0];
513             $text = @$bbref[1];
514
515             if ($binding =~ /:/) { # mouse binding with "where" clause
516                 ($binding,$where) = split (/:/, $binding, 2);
517             }
518
519             if ($gk =~ /^m/) {
520                 # mouse mode - use shorter abbrevs
521                 foreach $k (keys %mouse_modifier_map) {
522                     $binding =~ s/\@$k\@/$mouse_modifier_map{$k}/;
523                 }
524             } else {
525                 foreach $k (keys %cs_modifier_map) {
526                     $binding =~ s/\@$k\@/$cs_modifier_map{$k}/;
527                 }
528             }
529
530             $binding =~ s/></\+/g;
531             $binding =~ s/^<//;
532             $binding =~ s/>/\+/;
533
534             # substitute keycode names for something printable
535
536             $re = qr/${ \(join'|', map quotemeta, keys %keycodes)}/;
537             $binding =~ s/($re)/$keycodes{$1}/g;
538
539             # split up mouse bindings to "click" and "where" parts
540
541             if ($gk eq "mobject") {
542                 print "{\\tt @$bbref[1] } & {\\tt $binding} {\\it $where}\\\\\n";
543             } else {
544                 print "{\\tt @$bbref[1] } & {\\tt $binding} \\\\\n";
545             }
546         }
547
548         print "\\end{tabular}\n";
549
550     }
551 }
552
553 print $boilerplate_footer;
554
555 exit 0;