8 $semicolon = ";"; # help out stupid emacs
9 $title = "Ardour Shortcuts";
31 GetOptions ("platform=s" => \$platform,
32 "winkey=s" => \$winkey,
33 "cheatsheet" => \$make_cheatsheet,
34 "accelmap" => \$make_accelmap,
35 "merge=s" => \$merge_from,
38 if ($platform eq "darwin") {
40 $gtk_modifier_map{'PRIMARY'} = 'Primary'; # GTK supports Primary to allow platform-independent binding to the "primary" modifier, which on OS X is Command
41 $gtk_modifier_map{'SECONDARY'} = 'Control';
42 $gtk_modifier_map{'TERTIARY'} = 'Shift';
43 $gtk_modifier_map{'LEVEL4'} = 'Mod1';
45 # cs_modifier_map == "Cheat Sheet Modifier Map"
46 # Used to control what gets shown in the
47 # cheat sheet for a given (meta)-modifier
49 $cs_modifier_map{'PRIMARY'} = 'Cmd';
50 $cs_modifier_map{'SECONDARY'} = 'Control';
51 $cs_modifier_map{'TERTIARY'} = 'Shift';
52 $cs_modifier_map{'LEVEL4'} = 'Opt';
54 # used to display what gets shown in the
55 # cheat sheet for mouse bindings. Differs
56 # from cs_modifier map in using shorter
59 $mouse_modifier_map{'PRIMARY'} = 'Cmd';
60 $mouse_modifier_map{'SECONDARY'} = 'Ctrl';
61 $mouse_modifier_map{'TERTIARY'} = 'Shift';
62 $mouse_modifier_map{'LEVEL4'} = 'Opt';
66 $gtk_modifier_map{'PRIMARY'} = 'Control';
67 $gtk_modifier_map{'SECONDARY'} = 'Alt';
68 $gtk_modifier_map{'TERTIARY'} = 'Shift';
69 $gtk_modifier_map{'LEVEL4'} = $winkey; # something like "Mod4><Super"
71 # cs_modifier_map == "Cheat Sheet Modifier Map"
72 # Used to control what gets shown in the
73 # cheat sheet for a given (meta)-modifier
75 $cs_modifier_map{'PRIMARY'} = 'Control';
76 $cs_modifier_map{'SECONDARY'} = 'Alt';
77 $cs_modifier_map{'TERTIARY'} = 'Shift';
78 $cs_modifier_map{'LEVEL4'} = 'Win';
80 # used to display what gets shown in the
81 # cheat sheet for mouse bindings. Differs
82 # from cs_modifier map in using shorter
85 $mouse_modifier_map{'PRIMARY'} = 'Ctl';
86 $mouse_modifier_map{'SECONDARY'} = 'Alt';
87 $mouse_modifier_map{'TERTIARY'} = 'Shift';
88 $mouse_modifier_map{'LEVEL4'} = 'Win';
98 'bracketright' => ']',
103 'rightanglebracket' => '>',
104 'leftanglebracket' => '<',
114 'rightarrow' => '→',
115 'leftarrow' => '←',
116 'uparrow' => '↑',
117 'downarrow' => '↓',
118 'Page_Down' => 'PageDown',
119 'Page_Up' => 'PageUp',
121 'KP_Right' => 'KP-→',
122 'KP_Left' => 'KP-←',
123 'KP_Up' => 'KP-↑',
124 'KP_Down' => 'KP-↓',
132 'asciicircum' => '\\verb=^=',
133 'apostrophe' => '\'',
134 'bracketleft' => '[',
135 'bracketright' => ']',
136 'braceleft' => '\\{',
137 'braceright' => '\\}',
138 'backslash' => '$\\backslash$',
140 'rightanglebracket' => '>',
141 'leftanglebracket' => '<',
142 'ampersand' => '\\&',
151 'rightarrow' => '$\rightarrow$',
152 'leftarrow' => '$\\leftarrow$',
153 'uparrow' => '$\\uparrow$',
154 'downarrow' => '$\\downarrow$',
155 'Page_Down' => 'Page Down',
156 'Page_Up' => 'Page Up',
165 open (BINDINGS, $merge_from) || die ("merge from bindings: file not readable");
167 next if (/^$semicolon/);
168 if (/^\(gtk_accel/) {
170 chop; # closing parenthesis
172 ($junk, $action, $binding) = split;
173 $merge_bindings{$action} = $binding;
179 if ($make_accelmap && !$merge_from) {
180 print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
183 $bindings_name = basename ($ARGV[0]);
184 $bindings_name =~ s/.bindings\.in$//;
186 open SOURCE, "<", $ARGV[0] or die $!;
189 next if /^$semicolon/;
201 $group_names{$group_key} = $group_name;
202 $group_text{$group_key} = $group_text;
203 $group_numbering{$group_key} = $group_number;
204 # each binding entry is 2 element array. bindings
205 # are all collected into a container array. create
206 # the first dummy entry so that perl knows what we
208 $group_bindings{$group_key} = [ [] ];
213 ($group_key,$owner,$group_name) = split (/\s+/, $_, 3);
214 if ($make_accelmap) {
215 if (!exists ($owner_bindings{$owner})) {
216 $owner_bindings{$owner} = [ [] ];
218 $group_owners{$group_key} = $owner;
229 $group_names{$group_key} = $group_name;
230 $group_text{$group_key} = $group_text;
233 next if (/^[ \t]+$/);
243 ($key,$action,$binding,$text) = split (/\|/, $_, 4);
247 $owner = $group_owners{$gkey};
249 # substitute bindings
251 $gtk_binding = $binding;
254 $lookup = "<Actions>/" . $action;
255 if ($merge_bindings{$lookup}) {
256 $binding = $merge_bindings{$lookup};
259 # forced inclusion of bindings from template
261 # this action is not defined in the merge from set, so forget it
267 # store the accelmap output
270 # remove + and don't print it in the accelmap
273 # include this in the accelmap if it is part of a group that has an "owner"
274 if (!$merge_from && $make_accelmap && exists ($owner_bindings{$owner})) {
279 $b =~ s/PRIMARY/Primary-/;
280 $b =~ s/SECONDARY/Secondary-/;
281 $b =~ s/TERTIARY/Tertiary-/;
282 $b =~ s/LEVEL4/Level4-/;
284 $g = $group_names{$gkey};
287 $bref = $owner_bindings{$owner};
288 push (@$bref, [ $action, $b, $g]);
293 # do not include this binding in the cheat sheet
297 $bref = $group_bindings{$key};
298 push (@$bref, [$binding, $text]);
300 $sref = $section_text{$key};
301 push (@$sref, [$owner]);
309 if ($make_accelmap) {
310 print "<BindingSet name=\"" . $bindings_name . "\">\n";
312 foreach $owner (keys %owner_bindings) {
313 print " <Bindings name=\"$owner\">\n <Press>\n";
314 $bindings = $owner_bindings{$owner};
315 shift (@$bindings); # remove initial empty element
316 for my $binding (@$bindings) {
317 print ' <Binding key="' . @$binding[1] . '" action="' . @$binding[0] . '" group="' . @$binding[2] . "\"/>\n";
319 print " </Press>\n </Bindings>\n";
322 # merge in the "fixed" bindings that are not defined by the argument given to this program
323 # this covers things like the step editor, monitor and processor box bindings
325 foreach $hardcoded_bindings ("mixer.bindings", "step_editing.bindings", "monitor.bindings", "processor_box.bindings") {
326 $path = File::Spec->catfile (dirname ($ARGV[0]), $hardcoded_bindings);
327 open HARDCODED, "<", $path or die $!;
328 while (<HARDCODED>) {
334 print "</BindingSet>\n";
337 if ($make_accelmap || !$make_cheatsheet) {
343 @groups_sorted_by_number = sort { $group_numbering{$a} <=> $group_numbering{$b} } keys %group_numbering;
345 foreach $gk (@groups_sorted_by_number) {
348 # mouse stuff - ignore
352 # $bref is a reference to the array of arrays for this group
353 $bref = $group_bindings{$gk};
355 if (scalar @$bref > 1) {
357 $name = $group_names{$gk};
358 $name =~ s/\\linebreak.*//;
360 $name =~ s/\$\\_\$/-/g;
361 $name =~ s/\\[a-z]+ //g;
365 print "<h3>$name</h3>\n";
367 $gtext = $group_text{$gk};
368 $gtext =~ s/\\linebreak.*//;
370 $gtext =~ s/\$\\_\$/-/g;
371 $gtext =~ s/\\[a-z]+ //g;
373 $gtext =~ s/\\par//g;
375 if (!($gtext eq "")) {
379 # ignore the first entry, which was empty
385 print "<dl class=\"bindings\">\n";
387 # sort the array of arrays by the descriptive text for nicer appearance,
390 for $bbref (sort { @$a[1] cmp @$b[1] } @$bref) {
391 # $bbref is a reference to an array
393 $binding = @$bbref[0];
396 if ($binding =~ /:/) { # mouse binding with "where" clause
397 ($binding,$where) = split (/:/, $binding, 2);
400 foreach $k (keys %cs_modifier_map) {
401 $binding =~ s/\@$k\@/$cs_modifier_map{$k}/;
404 # remove braces for HTML
406 $binding =~ s/></\+/g;
410 # substitute keycode names for something printable
412 $re = qr/${ \(join'|', map quotemeta, keys %keycodes)}/;
413 $binding =~ s/($re)/$keycodes{$1}/g;
415 # tidy up description
418 $descr =~ s/\\linebreak.*//;
420 $descr =~ s/\$\\_\$/-/g;
421 $descr =~ s/\\[a-z]+ //g;
423 $descr =~ s/\\par//g;
425 print "<dt>$descr</dt><dd>$binding</dd>\n";
432 print " <!-- remove this if more text is added below -->\n";
437 # Now print the cheatsheet
439 $boilerplate_header = <<END_HEADER;
440 \\documentclass[10pt,landscape]{article}
441 %\\documentclass[10pt,landscape,a4paper]{article}
442 %\\documentclass[10pt,landscape,letterpaper]{article}
443 \\usepackage{multicol}
446 \\usepackage{palatino}
447 \\usepackage{geometry}
449 \\setlength{\\parskip}{0pt}
450 \\setlength{\\parsep}{0pt}
451 \\setlength{\\headsep}{0pt}
452 \\setlength{\\topskip}{0pt}
453 \\setlength{\\topmargin}{0pt}
454 \\setlength{\\topsep}{0pt}
455 \\setlength{\\partopsep}{0pt}
457 % This sets page margins to .5 inch if using letter paper, and to 1cm
458 % if using A4 paper. (This probably isnott strictly necessary.)
459 % If using another size paper, use default 1cm margins.
460 \\ifthenelse{\\lengthtest { \\paperwidth = 11in}}
461 { \\geometry{top=.5in,left=.5in,right=.5in,bottom=.5in} }
462 {\\ifthenelse{ \\lengthtest{ \\paperwidth = 297mm}}
463 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
464 {\\geometry{top=1cm,left=1cm,right=1cm,bottom=1cm} }
467 % Turn off header and footer
470 % Redefine section commands to use less space
472 \\renewcommand{\\section}{\\\@startsection{section}{1}{0mm}%
473 {-1ex plus -.5ex minus -.2ex}%
475 {\\normalfont\\large\\bfseries}}
476 \\renewcommand{\\subsection}{\\\@startsection{subsection}{2}{0mm}%
477 {-1explus -.5ex minus -.2ex}%
479 {\\normalfont\\normalsize\\bfseries}}
480 \\renewcommand{\\subsubsection}{\\\@startsection{subsubsection}{3}{0mm}%
481 {-1ex plus -.5ex minus -.2ex}%
483 {\\normalfont\\small\\bfseries}}
486 % Do not print section numbers% Do not print section numbers
487 \\setcounter{secnumdepth}{0}
489 \\setlength{\\parindent}{0pt}
490 \\setlength{\\parskip}{0pt plus 0.5ex}
492 %-------------------------------------------
498 \\begin{multicols}{3}
501 $boilerplate_footer = <<END_FOOTER;
502 \\rule{0.3\\linewidth}{0.25pt}
505 Copyright \\copyright\\ 2013 ardour.org
507 % Should change this to be date of file, not current date.
509 http://manual.ardour.org
515 if ($make_cheatsheet) {
516 print $boilerplate_header;
517 print "\\begin{center}\\Large\\bf $title \\end{center}\n";
520 @groups_sorted_by_number = sort { $group_numbering{$a} <=> $group_numbering{$b} } keys %group_numbering;
522 foreach $gk (@groups_sorted_by_number) {
523 # $bref is a reference to the array of arrays for this group
524 $bref = $group_bindings{$gk};
526 if (scalar @$bref > 1) {
527 print "\\section{$group_names{$gk}}\n";
529 if (!($group_text{$gk} eq "")) {
530 print "$group_text{$gk}\n\\par\n";
533 # ignore the first entry, which was empty
537 # find the longest descriptive text (this is not 100% accuracy due to typography)
542 for $bbref (@$bref) {
543 # $bbref is a reference to an array
547 # if there is a linebreak, just use everything up the linebreak
548 # to determine the width
551 if ($text =~ /\\linebreak/) {
552 $matchtext = s/\\linebreak.*//;
556 if (length ($matchtext) > $maxtextlen) {
557 $maxtextlen = length ($matchtext);
558 $maxtext = $matchtext;
563 # mouse mode: don't extend max text at all - space it tight
571 print "\\settowidth{\\MyLen}{\\texttt{$maxtext}}\n";
572 print "\\begin{tabular}{\@{}p{\\the\\MyLen}%
573 \@{}p{\\linewidth-\\the\\MyLen}%
576 # sort the array of arrays by the descriptive text for nicer appearance,
579 for $bbref (sort { @$a[1] cmp @$b[1] } @$bref) {
580 # $bbref is a reference to an array
582 $binding = @$bbref[0];
585 if ($binding =~ /:/) { # mouse binding with "where" clause
586 ($binding,$where) = split (/:/, $binding, 2);
590 # mouse mode - use shorter abbrevs
591 foreach $k (keys %mouse_modifier_map) {
592 $binding =~ s/\@$k\@/$mouse_modifier_map{$k}/;
595 foreach $k (keys %cs_modifier_map) {
596 $binding =~ s/\@$k\@/$cs_modifier_map{$k}/;
600 $binding =~ s/></\+/g;
604 # substitute keycode names for something printable
606 $re = qr/${ \(join'|', map quotemeta, keys %keycodes)}/;
607 $binding =~ s/($re)/$keycodes{$1}/g;
609 # split up mouse bindings to "click" and "where" parts
611 if ($gk eq "mobject") {
612 print "{\\tt @$bbref[1] } & {\\tt $binding} {\\it $where}\\\\\n";
614 print "{\\tt @$bbref[1] } & {\\tt $binding} \\\\\n";
618 print "\\end{tabular}\n";
623 print $boilerplate_footer;