include mouse bindings even in merged cheat sheets
[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
26 GetOptions ("platform=s" => \$platform,
27             "winkey=s" => \$winkey,
28             "cheatsheet" => \$make_cheatsheet,
29             "accelmap" => \$make_accelmap,
30             "merge=s" => \$merge_from);
31
32 if ($platform eq "osx") {
33
34     $gtk_modifier_map{'PRIMARY'} = 'meta';
35     $gtk_modifier_map{'SECONDARY'} = 'Mod1';
36     $gtk_modifier_map{'TERTIARY'} = 'Shift';
37     $gtk_modifier_map{'LEVEL4'} = 'Control';
38     $gtk_modifier_map{'WINDOW'} = 'Mod1';
39
40     $cs_modifier_map{'PRIMARY'} = 'Command';
41     $cs_modifier_map{'SECONDARY'} = 'Opt';
42     $cs_modifier_map{'TERTIARY'} = 'Shift';
43     $cs_modifier_map{'LEVEL4'} = 'Control';
44     $cs_modifier_map{'WINDOW'} = 'Opt';
45
46     $mouse_modifier_map{'PRIMARY'} = 'Cmd';
47     $mouse_modifier_map{'SECONDARY'} = 'Opt';
48     $mouse_modifier_map{'TERTIARY'} = 'Shift';
49     $mouse_modifier_map{'LEVEL4'} = 'Control';
50     $mouse_modifier_map{'WINDOW'} = 'Opt';
51
52 } else {
53
54     $gtk_modifier_map{'PRIMARY'} = 'Control';
55     $gtk_modifier_map{'SECONDARY'} = 'Alt';
56     $gtk_modifier_map{'TERTIARY'} = 'Shift';
57     $gtk_modifier_map{'LEVEL4'} = $winkey;
58     $gtk_modifier_map{'WINDOW'} = 'Alt';
59     $gtk_modifier_map{$winkey} => 'Win';
60
61     $cs_modifier_map{'PRIMARY'} = 'Control';
62     $cs_modifier_map{'SECONDARY'} = 'Alt';
63     $cs_modifier_map{'TERTIARY'} = 'Shift';
64     $cs_modifier_map{'LEVEL4'} = 'Win';
65     $cs_modifier_map{'WINDOW'} = 'Alt';
66     $cs_modifier_map{$winkey} => 'Win';
67
68     $mouse_modifier_map{'PRIMARY'} = 'Ctl';
69     $mouse_modifier_map{'SECONDARY'} = 'Alt';
70     $mouse_modifier_map{'TERTIARY'} = 'Shift';
71     $mouse_modifier_map{'LEVEL4'} = 'Win';
72     $mouse_modifier_map{'WINDOW'} = 'Alt';
73     $mouse_modifier_map{$winkey} => 'Win';
74 }
75
76 %keycodes = (
77     'asciicircum' => '\\verb=^=',
78     'apostrophe' => '\'',
79     'bracketleft' => '[',
80     'bracketright' => ']',
81     'braceleft' => '\\{',
82     'braceright' => '\\}',
83     'backslash' => '$\\backslash$',
84     'slash' => '/',
85     'rightanglebracket' => '>',
86     'leftanglebracket' => '<',
87     'ampersand' => '\\&',
88     'comma' => ',',
89     'period' => '.',
90     'semicolon' => ';',
91     'colon' => ':',
92     'equal' => '=',
93     'minus' => '-',
94     'plus' => '+',
95     'grave' => '`',
96     'rightarrow' => '$\rightarrow$',
97     'leftarrow' => '$\\leftarrow$',
98     'uparrow' => '$\\uparrow$',
99     'downarrow' => '$\\downarrow$',
100     'Page_Down' => 'Page Down',
101     'Page_Up' => 'Page Up',
102     'space' => 'space',
103     'KP_' => 'KP$\_$',
104     );
105
106 if ($merge_from) {
107     open (BINDINGS, $merge_from) || die ("merge from bindings: file not readable");
108     while (<BINDINGS>) {
109         next if (/^$semicolon/);
110         if (/^\(gtk_accel/) {
111             chop; # newline
112             chop; # closing parenthesis
113             s/"//g;
114             ($junk, $action, $binding) = split;
115             $merge_bindings{$action} = $binding;
116         }
117     }
118     close (BINDINGS);
119 }
120
121 if ($make_accelmap && !$merge_from) {
122     print ";; this accelmap was produced by tools/fmt-bindings\n";
123 }
124
125 while (<>) {
126     next if /^$semicolon/;
127
128     if (/^\$/) {
129         s/^\$//;
130         $title = $_;
131         next;
132     }
133
134     if (/^%/) {
135         
136         if ($in_group_def) {
137             chop $group_text;
138             $group_names{$group_key} = $group_name;
139             $group_text{$group_key} = $group_text;
140             $group_numbering{$group_key} = $group_number;
141             # each binding entry is 2 element array. bindings
142             # are all collected into a container array. create
143             # the first dummy entry so that perl knows what we
144             # are doing.
145             $group_bindings{$group_key} = [ [] ];
146         }
147
148         s/^%//;
149         chop;
150         ($group_key,$group_name) = split (/\s+/, $_, 2);
151         $group_number++;
152         $group_text = "";
153         $in_group_def = 1;
154         next;
155     }
156
157     if ($in_group_def) {
158         if (/^@/) {
159             chop $group_text;
160             $group_names{$group_key} = $group_name;
161             $group_text{$group_key} = $group_text;
162             $in_group_def = 0;
163         } else {
164             next if (/^[ \t]+$/);
165             $group_text .= $_;
166             $group_text;
167             next;
168         }
169     }
170
171     if (/^@/) {
172         s/^@//;
173         chop;
174         ($key,$action,$binding,$text) = split (/\|/, $_, 4);
175
176         # substitute bindings
177
178         $gtk_binding = $binding;
179
180         if ($merge_from) {
181             $lookup = "<Actions>/" . $action;
182             if ($merge_bindings{$lookup}) {
183                 $binding = $merge_bindings{$lookup};
184             } else {
185                 if ($key =~ /^\+/) {
186                     # forced inclusion of bindings from template
187                 } else {
188                     # this action is not defined in the merge from set, so forget it 
189                     next;
190                 }
191             }
192         } 
193
194         # print the accelmap output
195
196         if ($key =~ /^\+/) {
197             # remove + and don't print it in the accelmap
198             $key =~ s/^\+//;
199         } else {
200             # include this in the accelmap
201             if (!$merge_from && $make_accelmap) {
202                 foreach $k (keys %gtk_modifier_map) {
203                     $gtk_binding =~ s/\@$k\@/$gtk_modifier_map{$k}/;
204                 }
205                 print "(gtk_accel_map \"<Actions>/$action\" \"$gtk_binding\")\n";
206             }
207         }
208
209         if ($key =~ /^-/) {
210             # do not include this binding in the cheat sheet
211             next;
212         }
213
214         $bref = $group_bindings{$key};
215         push (@$bref, [$binding, $text]);
216
217         next;
218     }
219
220     next;
221 }
222
223 if ($make_accelmap || !$make_cheatsheet) {
224     exit 0;
225 }
226
227 # Now print the cheatsheet
228
229 $boilerplate_header = <<END_HEADER;
230 \\documentclass[10pt,landscape]{article}
231 \\usepackage{multicol}
232 \\usepackage{calc}
233 \\usepackage{ifthen}
234 \\usepackage{palatino}
235 \\usepackage{geometry}
236
237 \\setlength{\\parskip}{0pt}
238 \\setlength{\\parsep}{0pt}
239 \\setlength{\\headsep}{0pt}
240 \\setlength{\\topskip}{0pt}
241 \\setlength{\\topmargin}{0pt}
242 \\setlength{\\topsep}{0pt}
243 \\setlength{\\partopsep}{0pt}
244
245 % This sets page margins to .5 inch if using letter paper, and to 1cm
246 % if using A4 paper. (This probably isnott strictly necessary.)
247 % If using another size paper, use default 1cm margins.
248 \\ifthenelse{\\lengthtest { \\paperwidth = 11in}}
249         { \\geometry{top=.5in,left=1in,right=0in,bottom=.5in} }
250         {\\ifthenelse{ \\lengthtest{ \\paperwidth = 297mm}}
251                 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
252                 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
253         }
254
255 % Turn off header and footer
256 \\pagestyle{empty}
257  
258 % Redefine section commands to use less space
259 \\makeatletter
260 \\renewcommand{\\section}{\\\@startsection{section}{1}{0mm}%
261                                 {-1ex plus -.5ex minus -.2ex}%
262                                 {0.5ex plus .2ex}%
263                                 {\\normalfont\\large\\bfseries}}
264 \\renewcommand{\\subsection}{\\\@startsection{subsection}{2}{0mm}%
265                                 {-1explus -.5ex minus -.2ex}%
266                                 {0.5ex plus .2ex}%
267                                 {\\normalfont\\normalsize\\bfseries}}
268 \\renewcommand{\\subsubsection}{\\\@startsection{subsubsection}{3}{0mm}%
269                                 {-1ex plus -.5ex minus -.2ex}%
270                                 {1ex plus .2ex}%
271                                 {\\normalfont\\small\\bfseries}}
272 \\makeatother
273
274 % Do not print section numbers% Do not print section numbers
275 \\setcounter{secnumdepth}{0}
276
277 \\setlength{\\parindent}{0pt}
278 \\setlength{\\parskip}{0pt plus 0.5ex}
279
280 %-------------------------------------------
281
282 \\begin{document}
283 \\newlength{\\MyLen}
284 \\raggedright
285 \\footnotesize
286 \\begin{multicols}{3}
287 END_HEADER
288
289 $boilerplate_footer = <<END_FOOTER;
290 \\rule{0.3\\linewidth}{0.25pt}
291 \\scriptsize
292
293 Copyright \\copyright\\ 2009 ardour.org
294
295 % Should change this to be date of file, not current date.
296 %\\verb!$Revision: 1.13 $, $Date: 2008/05/29 06:11:56 $.!
297
298 http://ardour.org/manual
299
300 \\end{multicols}
301 \\end{document}
302 END_FOOTER
303
304 if ($make_cheatsheet) {
305     print $boilerplate_header;
306     print "\\begin{center}\\Large\\bf $title \\end{center}\n";
307 }
308
309 @groups_sorted_by_number = sort { $group_numbering{$a} <=> $group_numbering{$b} } keys %group_numbering; 
310
311 foreach $gk (@groups_sorted_by_number) {
312     # $bref is a reference to the array of arrays for this group
313     $bref = $group_bindings{$gk};
314
315     if (scalar @$bref > 1) {
316         print "\\section{$group_names{$gk}}\n";
317
318         if (!($group_text{$gk} eq  "")) {
319             print "$group_text{$gk}\n\\par\n";
320         }
321         
322         # ignore the first entry, which was empty
323
324         shift (@$bref);
325
326         # find the longest descriptive text (this is not 100% accuracy due to typography)
327
328         $maxtextlen = 0;
329         $maxtext = "";
330
331         for $bbref (@$bref) {
332             # $bbref is a reference to an array
333             $text = @$bbref[1];
334             
335             #
336             # if there is a linebreak, just use everything up the linebreak
337             # to determine the width
338             #
339
340             if ($text =~ /\\linebreak/) {
341                 $matchtext = s/\\linebreak.*//;
342             } else {
343                 $matchtext = $text;
344             }
345             if (length ($matchtext) > $maxtextlen) {
346                 $maxtextlen = length ($matchtext);
347                 $maxtext = $matchtext;
348             }
349         }
350
351         if ($gk =~ /^m/) {
352             # mouse mode: don't extend max text at all - space it tight
353             $maxtext .= ".";
354         } else {
355             $maxtext .= "....";
356         }
357
358         # set up the table
359
360         print "\\settowidth{\\MyLen}{\\texttt{$maxtext}}\n";
361         print "\\begin{tabular}{\@{}p{\\the\\MyLen}% 
362                                 \@{}p{\\linewidth-\\the\\MyLen}%
363                                 \@{}}\n";
364
365         # sort the array of arrays by the descriptive text for nicer appearance,
366         # and print them
367
368         for $bbref (sort { @$a[1] cmp @$b[1] } @$bref) {
369             # $bbref is a reference to an array
370
371             $binding = @$bbref[0];
372             $text = @$bbref[1];
373
374             if ($binding =~ /:/) { # mouse binding with "where" clause
375                 ($binding,$where) = split (/:/, $binding, 2);
376             }
377
378             if ($gk =~ /^m/) {
379                 # mouse mode - use shorter abbrevs
380                 foreach $k (keys %mouse_modifier_map) {
381                     $binding =~ s/\@$k\@/$mouse_modifier_map{$k}/;
382                 }
383             } else {
384                 foreach $k (keys %cs_modifier_map) {
385                     $binding =~ s/\@$k\@/$cs_modifier_map{$k}/;
386                 }
387             }
388
389             $binding =~ s/></\+/g;
390             $binding =~ s/^<//;
391             $binding =~ s/>/\+/;
392
393             # substitute keycode names for something printable
394
395             $re = qr/${ \(join'|', map quotemeta, keys %keycodes)}/;
396             $binding =~ s/($re)/$keycodes{$1}/g;
397
398             # split up mouse bindings to "click" and "where" parts
399
400             if ($gk eq "mobject") {
401                 print "{\\tt @$bbref[1] } & {\\tt $binding} {\\it $where}\\\\\n";
402             } else {
403                 print "{\\tt @$bbref[1] } & {\\tt $binding} \\\\\n";
404             }
405         }
406
407         print "\\end{tabular}\n";
408
409     }
410 }
411
412 print $boilerplate_footer;
413
414 exit 0;