2 # -*- coding: utf-8; -*-
4 ### texi2html customization script for LilyPond
5 ### Author: Reinhold Kainhofer <reinhold@kainhofer.com>, 2008.
6 ### Some code parts copied from texi2html and adapted. These functions
7 ### were written mainly by Patrice Dumas
11 ### Features implemented here:
12 ### -) For split manuals, the main page is index.html.
13 ### -) All @unnumbered* sections are placed into the same file
14 ### (implemented by split_at_numbered_sections)
15 ### -) Use our custom CSS file, with IE-specific fixes in another CSS file,
16 ### impelmented by lilypond_css_lines
17 ### -) TOC (folded, with the current page highlighted) in an overflown <div>
18 ### is added to every page; implemented by:
19 ### lilypond_print_element_header -- building of the TOC
20 ### lilypond_toc_body -- generation of customized TOC output
21 ### lilypond_print_page_head -- start <div id="main">
22 ### print_lilypond_page_foot -- closing id=main, output of footer & TOC
23 ### -) External refs are formatted only as "Text of the node" (not as >>see
24 ### "NODE" section "SECTION" in "BOOK"<< like with default texi2html). Also,
25 ### the leading "(book-name)" is removed.
26 ### Implemented by overriding lilypond_external_ref
27 ### -) Navigation bars on top/bottom of the page and between sections are not
28 ### left-aligned, but use a combination of left/center/right aligned table
29 ### cells; For this, I heavily extend the texi2html code to allow for
30 ### differently aligned cells and for multi-line tables);
31 ### Implemented in lilypond_print_navigation
32 ### -) Different formatting than the default: example uses the same formatting
34 ### -) Allow translated section titles: All section titles can be translated,
35 ### the original (English) title is associated with @translationof. This is
36 ### needed, because the file name / anchor is generated from the original
37 ### English title, since otherwise language-autoselection would break with
39 ### Since it is then no longer possible to obtain the file name from the
40 ### section title, I keep a sectionname<=>filename/anchor around. This way,
41 ### xrefs from other manuals can simply load that map and retrieve the
42 ### correct file name for the link. Implemented in:
43 ### lilypond_unknown (handling of @translationof, in case
44 ### extract_texi_filenames.py messes up...)
45 ### lilypond_element_file_name (correct file name: use the map)
46 ### lilypond_element_target_name (correct anchor: use the map)
47 ### lilypond_init_map (read in the externally created map from disk)
48 ### lilypond_external_href (load the map for xrefs, use the correct
50 ### -) The HTML anchors for all sections are derived from the node name /
51 ### section title (pre-generated in the .xref-map file). Implemented by:
52 ### lilypond_element_target_name (adjust section anchors)
53 ### -) Use the standard footnote format "<sup>nr</sup> text" instead of the
54 ### ugly format of texi2html (<h3>(nr)</h3><p>text</p>). Implemented in
55 ### makeinfo_like_foot_line_and_ref
56 ### makeinfo_like_foot_lines
57 ### makeinfo_like_paragraph
60 ### Useful helper functions:
61 ### -) texinfo_file_name($node_name): returns a texinfo-compatible file name
62 ### for the given string $node_name (whitespace trimmed/replaced by -,
63 ### non-standard chars replaced by _xxxx (ascii char code) and forced to
64 ### start with a letter by prepending t_g if necessary)
67 package Texi2HTML::Config;
70 use Encode qw(decode);
72 #############################################################################
74 #############################################################################
76 my $LY_LANGUAGES = {};
77 $LY_LANGUAGES->{'fr'} = {
78 'Back to Documentation Index' => 'Retour à l\'accueil de la documentation',
80 $LY_LANGUAGES->{'es'} = {
81 'Back to Documentation Index' => 'Volver al índice de la documentación',
83 $LY_LANGUAGES->{'de'} = {
84 'Back to Documentation Index' => 'Zur Dokumentationsübersicht',
86 $LY_LANGUAGES->{'ja'} = {
87 'Back to Documentation Index' => 'ドキュメント インデックスに戻る',
90 $LY_LANGUAGES->{'hu'} = {
91 'Back to Documentation Index' => 'Vissza a dokumentációk jegyzékéhez',
94 # FIXME: request translation and send it to texi2html/texinfo devs
95 $LANGUAGES->{'hu'} = $LANGUAGES->{'en'};
97 sub ly_get_string () {
98 my $lang = $Texi2HTML::THISDOC{current_lang};
100 if ($lang and $lang ne "en" and $LY_LANGUAGES->{$lang}->{$string}) {
101 return $LY_LANGUAGES->{$lang}->{$string};
108 #############################################################################
109 ### FUNCTIONALITY FOR MAIN WEB PAGES
110 #############################################################################
113 sub lilypond_init_web_manual ()
115 if (exists($main::value{'web_manual'}))
117 print STDERR "Initializing settings for web site\n";
122 push @Texi2HTML::Config::command_handler_process, \&lilypond_init_web_manual;
124 #############################################################################
125 ### SETTINGS FOR TEXI2HTML
126 #############################################################################
128 # Validation fix for texi2html<=1.82
129 $Texi2HTML::Config::DOCTYPE = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
133 @Texi2HTML::Config::CSS_REFS = (
134 {FILENAME => "lilypond-web.css", TITLE => "Default style"}
137 @Texi2HTML::Config::CSS_REFS = (
138 {FILENAME => "lilypond-mccarty.css", TITLE => "Patrick McCarty's design"}
142 @Texi2HTML::Config::ALT_CSS_REFS = (
143 {FILENAME => "lilypond.css", TITLE => "Andrew Hawryluk's design" },
144 {FILENAME => "lilypond-blue.css", TITLE => "Kurt Kroon's blue design" },
146 $Texi2HTML::Config::USE_ACCESSKEY = 1;
147 $Texi2HTML::Config::USE_LINKS = 1;
148 $Texi2HTML::Config::USE_REL_REV = 1;
149 $Texi2HTML::Config::SPLIT_INDEX = 0;
150 $Texi2HTML::Config::SEPARATED_FOOTNOTES = 0; # Print footnotes on same page, not separated
152 if ($Texi2HTML::Config::SPLIT eq 'section' or
153 $Texi2HTML::Config::SPLIT eq 'subsubsection') {
154 $Texi2HTML::Config::element_file_name = \&lilypond_element_file_name;
157 $Texi2HTML::Config::element_target_name = \&lilypond_element_target_name;
158 $default_print_element_header = $Texi2HTML::Config::print_element_header;
159 $Texi2HTML::Config::print_element_header = \&lilypond_print_element_header;
160 $Texi2HTML::Config::print_page_foot = \&print_lilypond_page_foot;
161 $Texi2HTML::Config::print_navigation = \&lilypond_print_navigation;
162 $Texi2HTML::Config::external_ref = \&lilypond_external_ref;
163 $default_external_href = $Texi2HTML::Config::external_href;
164 $Texi2HTML::Config::external_href = \&lilypond_external_href;
165 $default_toc_body = $Texi2HTML::Config::toc_body;
166 $Texi2HTML::Config::toc_body = \&lilypond_toc_body;
167 $Texi2HTML::Config::css_lines = \&lilypond_css_lines;
168 $default_unknown = $Texi2HTML::Config::unknown;
169 $Texi2HTML::Config::unknown = \&lilypond_unknown;
170 $default_print_page_head = $Texi2HTML::Config::print_page_head;
171 $Texi2HTML::Config::print_page_head = \&lilypond_print_page_head;
172 # $Texi2HTML::Config::foot_line_and_ref = \&lilypond_foot_line_and_ref;
173 $Texi2HTML::Config::foot_line_and_ref = \&makeinfo_like_foot_line_and_ref;
174 $Texi2HTML::Config::foot_lines = \&makeinfo_like_foot_lines;
175 $Texi2HTML::Config::paragraph = \&makeinfo_like_paragraph;
179 # Examples should be formatted similar to quotes:
180 $Texi2HTML::Config::complex_format_map->{'example'} = {
181 'begin' => q{"<blockquote>"},
182 'end' => q{"</blockquote>\n"},
186 %Texi2HTML::config::misc_pages_targets = (
187 'Overview' => 'Overview',
188 'Contents' => 'Contents',
193 my @section_to_filename;
198 #############################################################################
200 #############################################################################
203 $Data::Dumper::Maxdepth = 2;
205 sub print_element_info($)
208 print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
209 print "Element: $element\n";
210 print Dumper($element);
217 #############################################################################
219 #############################################################################
221 # Convert a given node name to its proper file name (normalization as explained
222 # in the texinfo manual:
223 # http://www.gnu.org/software/texinfo/manual/texinfo/html_node/HTML-Xref-Node-Name-Expansion.html
224 sub texinfo_file_name($)
228 # File name normalization by texinfo:
229 # 1/2: letters and numbers are left unchanged
230 # 3/4: multiple, leading and trailing whitespace is removed
231 $text = main::normalise_space($text);
232 # 5/6: all remaining spaces are converted to '-', all other 7- or 8-bit
233 # chars are replaced by _xxxx (xxxx=ascii character code)
234 while ($text ne '') {
235 if ($text =~ s/^([A-Za-z0-9]+)//o) { # number or letter stay unchanged
237 } elsif ($text =~ s/^ //o) { # space -> '-'
239 } elsif ($text =~ s/^(.)//o) { # Otherwise use _xxxx (ascii char code)
241 if ( $ccode <= 0xFFFF ) {
242 $result .= sprintf("_%04x", $ccode);
244 $result .= sprintf("__%06x", $ccode);
248 # 7: if name does not begin with a letter, prepend 't_g' (so it starts with a letter)
249 if ($result !~ /^[a-zA-Z]/) {
250 $result = 't_g' . $result;
256 # Load a file containing a nodename<=>filename map (tab-sepatared, i.e.
257 # NODENAME\tFILENAME\tANCHOR
258 # Returns a ref to a hash "Node title" => ["FilenameWithoutExt", "Anchor"]
259 sub load_map_file ($)
264 # For some unknown reason, Perl on my system (5.10.0 on Fedora 12)
265 # refuses to open map files of translated documents with
266 # '<:encoding(utf8)', but decoding from UTF-8 line by line works. -jm
267 if (open(XREFFILE,'<', $mapfile)) {
269 while ( $line = decode ('UTF-8', <XREFFILE>) ) {
270 # parse the tab-separated entries and insert them into the map:
272 my @entries = split(/\t/, $line);
273 if (scalar (@entries) == 3) {
274 $node_map->{$entries[0]} = [$entries[1], $entries[2]];
276 print STDERR "Invalid entry in the node file $mapfile: $line\n";
279 # print STDERR %{$node_map};
282 print STDERR "WARNING: Unable to load the map file $mapfile\n";
288 # Split the given path into dir and basename (with .texi removed). Used mainly
289 # to get the path/basename of the original texi input file
290 sub split_texi_filename ($)
293 my ($docu_dir, $docu_name);
294 if ($docu =~ /(.*\/)/) {
295 chop($docu_dir = $1);
297 $docu_name =~ s/.*\///;
302 $docu_name =~ s/\.te?x(i|info)?$//;
303 return ($docu_dir, $docu_name);
310 #############################################################################
312 #############################################################################
314 # Include our standard CSS file, not hard-coded CSS code directly in the HTML!
315 # For IE, conditionally include the lilypond-ie-fixes.css style sheet
316 sub lilypond_css_lines ($$)
318 my $import_lines = shift;
319 my $rule_lines = shift;
320 return if (defined($Texi2HTML::THISDOC{'CSS_LINES'}));
321 if (@$rule_lines or @$import_lines)
323 $Texi2HTML::THISDOC{'CSS_LINES'} = "<style type=\"text/css\">\n<!--\n";
324 $Texi2HTML::THISDOC{'CSS_LINES'} .= join('',@$import_lines) . "\n" if (@$import_lines);
325 $Texi2HTML::THISDOC{'CSS_LINES'} .= join('',@$rule_lines) . "\n" if (@$rule_lines);
326 $Texi2HTML::THISDOC{'CSS_LINES'} .= "-->\n</style>\n";
328 foreach my $ref (@CSS_REFS)
330 $Texi2HTML::THISDOC{'CSS_LINES'} .= "<link rel=\"stylesheet\" type=\"text/css\" title=\"$ref->{TITLE}\" href=\"$ref->{FILENAME}\">\n";
332 foreach my $ref (@Texi2HTML::Config::ALT_CSS_REFS)
334 $Texi2HTML::THISDOC{'CSS_LINES'} .= "<link rel=\"alternate stylesheet\" type=\"text/css\" href=\"$ref->{FILENAME}\" title=\"$ref->{TITLE}\">\n";
336 # FIXME: the website doesn't use ie7-specific fixes; do the
337 # docs still need this? -gp
338 $Texi2HTML::THISDOC{'CSS_LINES'} .= "<!--[if lte IE 7]>\n<link href=\"lilypond-ie-fixes.css\" rel=\"stylesheet\" type=\"text/css\">\n<![endif]-->\n";
345 #############################################################################
346 ### SPLITTING BASED ON NUMBERED SECTIONS
347 #############################################################################
351 my $node_to_filename_map = ();
354 # This function makes sure that files are only generated for numbered sections,
355 # but not for unnumbered ones. It is called after texi2html has done its own
356 # splitting and simply returns the filename for the node given as first argument
357 # Nodes with the same filename will be printed out to the same filename, so
358 # this really all we need. Also, make sure that the file names for sections
359 # are derived from the section title. We also might want to name the anchors
360 # according to node titles, which works by simply overriding the id element of
362 # If an external nodename<=>filename/anchor map file is found (loaded in
363 # the command handler, use the externally created values, otherwise use the
365 sub lilypond_element_file_name($$$)
369 my $docu_name = shift;
370 my $docu_ext = $Texi2HTML::Config::EXTENSION;
372 my $node_name = main::remove_texi($element->{'node_ref'}->{'texi'});
373 # the snippets page does not use nodes for the snippets, so in this case
374 # we'll have to use the section name!
375 if ($node_name eq '') {
376 $node_name = main::remove_texi($element->{'texi'});
379 # If we have an entry in the section<=>filename map, use that one, otherwise
380 # generate the filename/anchor here. In the latter case, external manuals
381 # will not be able to retrieve the file name for xrefs!!! Still, I already
382 # had that code, so I'll leave it in in case something goes wrong with the
383 # extract_texi_filenames.py script in the lilypond build process!
384 if (exists ($node_to_filename_map->{$node_name})) {
385 (my $filename, my $anchor) = @{$node_to_filename_map->{$node_name}};
386 $filename .= ".$docu_ext" if (defined($docu_ext));
388 # unnumbered sections (except those at top-level!) always go to the same
389 # file as the previous numbered section
390 if (not ($element->{number}) and not ($lastfilename eq '') and ($element->{level} > 1)) {
391 $filename = $lastfilename;
393 if (($filename eq $lastfilename)) {
394 $$element{doc_nr} = $docnr;
397 $$element{doc_nr} = $docnr;
398 $lastfilename = $filename;
400 # print STDERR "File name: $filename\n";
401 return lc($filename);
403 } elsif ($type eq "top" or $type eq "toc" or $type eq "doc" or $type eq "stoc" or $type eq "foot" or $type eq "about") {
406 print STDERR "WARNING: Node '$node_name' was NOT found in the map\n"
407 unless ($node_name eq '') or ($element->{'tag'} eq 'unnumberedsec')
408 or ($node_name =~ /NOT REALLY USED/);
410 # Numbered sections will get a filename Node_title, unnumbered sections will use
411 # the file name of the previous numbered section:
412 if (($element->{number}) or ($lastfilename eq '') or ($element->{level} == 1)) {
413 # normalize to the same file name as texinfo
414 if ($element->{translationof}) {
415 $node_name = main::remove_texi($element->{translationof});
417 my $filename = texinfo_file_name($node_name);
418 $filename .= ".$docu_ext" if (defined($docu_ext));
420 $$element{doc_nr} = $docnr;
421 $lastfilename = $filename;
422 print STDERR "File name: $filename\n";
423 return lc($filename);
425 $$element{doc_nr} = $docnr;
426 print STDERR "File name: $filename\n";
427 return lc($filename);
434 sub lilypond_element_target_name($$$)
439 # Target is based on node name (or sec name for secs without node attached)
440 my $node_name = main::remove_texi($element->{'node_ref'}->{'texi'});
441 if ($node_name eq '') {
442 $node_name = main::remove_texi($element->{'texi'});
445 # If we have an entry in the section<=>filename map, use that one, otherwise
446 # generate the anchor here.
447 if (exists ($node_to_filename_map->{$node_name})) {
448 (my $filename, $target) = @{$node_to_filename_map->{$node_name}};
450 my $anchor = $node_name;
451 if ($element->{translationof}) {
452 $anchor = main::remove_texi($element->{translationof});
454 # normalize to the same file name as texinfo
455 $target = texinfo_file_name($anchor);
457 # TODO: Once texi2html correctly prints out the target and not the id for
458 # the sections, change this back to ($id, $target)
459 $target = lc($target);
460 return ($target, $target);
464 ## Load the map file for the corrently processed texi file. We do this
465 # using a command init handler, since texi2html does not have any
466 # other hooks that are called after THISDOC is filled but before phase 2
467 # of the texi2html conversion.
468 sub lilypond_init_map ()
470 my ($docu_dir, $docu_name) = split_texi_filename ($Texi2HTML::THISDOC{'input_file_name'});
471 my $map_filename = main::locate_include_file ("${docu_name}.$Texi2HTML::THISDOC{current_lang}.xref-map")
472 || main::locate_include_file ("${docu_name}.xref-map");
473 print STDERR "Map filename is: $map_filename\nDocu name is $docu_name\n";
474 $node_to_filename_map = load_map_file ($map_filename);
476 push @Texi2HTML::Config::command_handler_init, \&lilypond_init_map;
480 #############################################################################
481 ### CLEANER LINK TITLE FOR EXTERNAL REFS
482 #############################################################################
484 # The default formatting of external refs returns e.g.
485 # "(lilypond-internals)Timing_translator", so we remove all (...) from the
486 # file_and_node argument. Also, we want only a very simple format, so we don't
487 # even call the default handler!
488 sub lilypond_external_ref($$$$$$)
493 my $file_node = shift;
494 my $href = lc(shift);
495 my $cross_ref = shift;
497 my $displaytext = '';
499 # 1) if we have a cross ref name, that's the text to be displayed:
500 # 2) For the top node, use the (printable) name of the manual, unless we
501 # have an explicit cross ref name
502 # 3) In all other cases use the section name
503 if ($cross_ref ne '') {
504 $displaytext = $cross_ref;
505 } elsif (($section eq '') or ($section eq 'Top')) {
506 $displaytext = $book;
508 $displaytext = $section;
511 $displaytext = &$anchor('', $href, $displaytext) if ($displaytext ne '');
512 return &$I('%{node_file_href}', { 'node_file_href' => $displaytext });
519 #############################################################################
520 ### HANDLING TRANSLATED SECTIONS: handle @translationof, secname<->filename
521 ### map stored on disk, xrefs in other manuals load that map
522 #############################################################################
525 # Try to make use of @translationof to generate files according to the original
526 # English section title...
527 sub lilypond_unknown($$$$$)
535 # the @translationof macro provides the original English section title,
536 # which should be used for file/anchor naming, while the title will be
537 # translated to each language
538 # It is already used by extract_texi_filenames.py, so this should not be
539 # necessary here at all. Still, I'll leave the code in just in case the
540 # python script messed up ;-)
541 if ($pass == 1 and $macro eq "translationof") {
542 if (ref($state->{'element'}) eq 'HASH') {
543 $state->{'element'}->{'translationof'} = main::normalise_space($line);
545 return ('', 1, undef, undef);
547 return &$default_unknown($macro, $line, $pass, $stack, $state);
554 my %translated_books = ();
555 # Construct a href to an external source of information.
556 # node is the node with texinfo @-commands
557 # node_id is the node transliterated and transformed as explained in the
559 # node_xhtml_id is the node transformed such that it is unique and can
560 # be used to make an html cross ref as explained in the texinfo manual
561 # file is the file in '(file)node'
562 sub lilypond_external_href($$$)
566 my $node_hxmlt_id = shift;
569 # 1) Keep a hash of book->section_map
570 # 2) if not file in keys hash => try to load the map (assign empty map if
571 # non-existent => will load only once!)
572 # 3) if node in the section=>(file, anchor) map, replace node_id and
573 # node_xhtml_id by the map's values
574 # 4) call the default_external_href with these values (or the old ones if not found)
576 if (($node_id ne '') and defined($file) and ($node_id ne 'Top')) {
577 my $map_name = $file;
578 $map_name =~ s/-big-page//;
580 # Load the map if we haven't done so already
581 if (!exists($translated_books{$map_name})) {
582 my ($docu_dir, $docu_name) = split_texi_filename ($Texi2HTML::THISDOC{'input_file_name'});
583 my $map_filename = main::locate_include_file ("${map_name}.$Texi2HTML::THISDOC{current_lang}.xref-map")
584 || main::locate_include_file ("${map_name}.xref-map");
585 $translated_books{$map_name} = load_map_file ($map_filename);
588 # look up translation. use these values instead of the old filename/anchor
589 my $section_name_map = $translated_books{$map_name};
590 my $node_text = main::remove_texi($node);
591 if (defined($section_name_map->{$node_text})) {
592 ($node_id, $node_hxmlt_id) = @{$section_name_map->{$node_text}};
594 print STDERR "WARNING: Unable to find node '$node_text' in book $map_name.\n";
599 return &$default_external_href($node, $node_id, $node_hxmlt_id, lc($file));
601 return &$default_external_href($node, $node_id, $node_hxmlt_id);
609 #############################################################################
610 ### CUSTOM TOC FOR EACH PAGE (in a frame on the left)
611 #############################################################################
613 my $page_toc_depth = 2;
614 my @default_toc = [];
616 # Initialize the toc_depth to 1 if the command-line option -D=short_toc is given
617 sub lilypond_init_toc_depth ()
619 if (exists($main::value{'short_toc'}) and not exists($main::value{'bigpage'})) {
623 # Set the TOC-depth (depending on a texinfo variable short_toc) in a
624 # command-handler, so we have them available when creating the pages
625 push @Texi2HTML::Config::command_handler_process, \&lilypond_init_toc_depth;
628 # recursively generate the TOC entries for the element and its children (which
629 # are only shown up to maxlevel. All ancestors of the current element are also
630 # shown with their immediate children, irrespective of their level.
631 # Unnumbered entries are only printed out if they are at top-level or 2nd level
632 # or their parent element is an ancestor of the currently viewed node.
633 # The conditions to call this method to print the entry for a child node is:
634 # -) the parent is an ancestor of the current page node
635 # -) the parent is a numbered element at top-level toplevel (i.e. show numbered
636 # and unnumbered 2nd-level children of numbered nodes)
637 # -) the child element is a numbered node below level maxlevel
638 sub generate_ly_toc_entries($$$)
641 my $element_path = shift;
645 my $maxlevel = shift;
647 # Skip undefined sections, plus all sections generated by index splitting
648 return() if (not defined($element) or exists($element->{'index_page'}));
650 my $level = $element->{'toc_level'};
651 my $is_parent_of_current = $element->{'id'} && $element_path->{$element->{'id'}};
652 my $ind = ' ' x $level;
655 $this_css_class = " class=\"";
657 $this_css_class = "";
659 $this_css_class .= $is_parent_of_current ? " toc_current" : "";
662 "Learning", "Glossary", "Essay",
663 "Contact", "Tiny examples", "Bug reports"
666 "Features", "Examples", "Freedom", "Background",
667 "Unix", "MacOS X", "Windows",
668 "Notation", "Usage", "Snippets",
669 "Help us", "Development", "Authors"
672 "Productions", "Testimonials",
673 "Source", "Old downloads",
674 "FAQ", "Changes", "Extend", "Internals",
675 "Publications", "Old news"
678 "Text input", "Alternate input",
680 "Translated", "All", "FDL"
683 my $addColor = " colorDefault";
684 foreach $color (@color_1) {
685 if ($element->{'text'} eq $color) {
686 $addColor = " color1";
689 foreach $color (@color_2) {
690 if ($element->{'text'} eq $color) {
691 $addColor = " color2";
694 foreach $color (@color_3) {
695 if ($element->{'text'} eq $color) {
696 $addColor = " color3";
699 foreach $color (@color_4) {
700 if ($element->{'text'} eq $color) {
701 $addColor = " color4";
705 $this_css_class .= $addColor . "\"";
708 my $entry = "$ind<li$this_css_class>" . &$anchor ($element->{'tocid'}, "$element->{'file'}#$element->{'target'}",$element->{'text'});
710 push (@result, $entry);
711 my $children = $element->{'section_childs'};
712 if (defined($children) and (ref($children) eq "ARRAY")) {
713 my $force_children = $is_parent_of_current or ($level == 1 and $element->{'number'});
714 my @child_result = ();
715 foreach my $c (@$children) {
716 my $is_numbered_child = defined ($c->{'number'});
717 my $below_maxlevel = $c->{'toc_level'} le $maxlevel;
718 if ($force_children or ($is_numbered_child and $below_maxlevel)) {
719 my @child_res = generate_ly_toc_entries($c, $element_path, $maxlevel);
720 push (@child_result, @child_res);
723 # if no child nodes were generated, e.g. for the index, where expanded pages
724 # are ignored, don't generate a list at all...
726 push (@result, "\n$ind<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
728 push (@result, "$ind<li$this_css_class>" . &$anchor ($element->{'tocid'}, "$element->{'file'}#$element->{'target'}","(main)"));
730 push (@result, @child_result);
732 push (@result, "$ind</ul>\n");
735 push (@result, "$ind</li>\n");
740 # Print a customized TOC, containing only the first two levels plus the whole
741 # path to the current page
742 sub lilypond_generate_page_toc_body($)
745 my $current_element = $element;
747 $parentelements{$element->{'id'}} = 1;
748 # Find the path to the current element
749 while ( defined($current_element->{'sectionup'}) and
750 ($current_element->{'sectionup'} ne $current_element) )
752 $parentelements{$current_element->{'sectionup'}->{'id'}} = 1
753 if ($current_element->{'sectionup'}->{'id'} ne '');
754 $current_element = $current_element->{'sectionup'};
756 if (exists($main::value{'shallow_toc'})) {
761 return () if not defined($current_element);
762 # Create the toc entries recursively
763 my @toc_entries = "";
765 push (@toc_entries, "<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
766 # FIXME: add link to main page, really hackily.
767 if ($element->{'sectionup'}) {
768 # it's not the top element
769 push (@toc_entries, "<li><a href=\"index.html\">Main</a></li>\n");
771 push (@toc_entries, "<li class=\"toc_current\"><a href=\"index.html\">Main</a></li>\n");
774 push (@toc_entries, "<div class=\"contents\">\n");
775 push (@toc_entries, "<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
777 my $children = $current_element->{'section_childs'};
778 foreach ( @$children ) {
779 push (@toc_entries, generate_ly_toc_entries($_, \%parentelements, $page_toc_depth));
783 # WTF, perl needs 6 lines of magic to do: ' ' + open ('file-name').read ()?
785 my $name = "search-box.html";
786 open FILE, "$ENV{SRC_DIR}/$name" or open FILE, "$ENV{SRC_DIR}/../$name" or die die "no such file: $name: $!";
789 $string = "<li>\n" . $string . "</li>\n";
790 push (@toc_entries, $string);
793 push (@toc_entries, "</ul>\n");
794 push (@toc_entries, "</div>\n");
798 sub lilypond_print_toc_div ($$)
802 my @lines = @$tocref;
803 # use default TOC if no custom lines have been generated
804 @lines = @default_toc if (not @lines);
807 print $fh "\n\n<div id=\"tocframe\">\n";
809 # Remove the leading "GNU LilyPond --- " from the manual title
810 my $topname = $Texi2HTML::NAME{'Top'};
811 $topname =~ s/^GNU LilyPond(:| &[mn]dash;) //;
813 # construct the top-level Docs index (relative path and including language!)
814 my $lang = $Texi2HTML::THISDOC{current_lang};
815 if ($lang and $lang ne "en") {
821 $reldir = "../" if ($Texi2HTML::Config::SPLIT eq 'section');
822 my $uplink = $reldir."web/manuals.${lang}html";
826 print $fh "<p class=\"toc_uplink\"><a href=\"$uplink\"
827 title=\"Documentation Index\"><< " .
828 &ly_get_string ('Back to Documentation Index') .
831 print $fh '<h4 class="toc_header"> ' . &$anchor('',
832 $Texi2HTML::HREF{'Top'},
834 'title="Start of the manual"'
838 foreach my $line (@lines) {
841 print $fh "</div>\n\n";
845 # Create the custom TOC for this page (partially folded, current page is
846 # highlighted) and store it in a global variable. The TOC is written out after
847 # the html contents (but positioned correctly using CSS), so that browsers with
848 # css turned off still show the contents first.
849 our @this_page_toc = ();
850 sub lilypond_print_element_header
852 my $first_in_page = shift;
853 my $previous_is_top = shift;
854 if ($first_in_page and not @this_page_toc) {
855 if (defined($Texi2HTML::THIS_ELEMENT)) {
856 # Create the TOC for this page
857 @this_page_toc = lilypond_generate_page_toc_body($Texi2HTML::THIS_ELEMENT);
860 return &$default_print_element_header( $first_in_page, $previous_is_top);
863 # Generate the HTML output for the TOC
864 sub lilypond_toc_body($)
866 my $elements_list = shift;
867 # Generate a default TOC for pages without THIS_ELEMENT
868 @default_toc = lilypond_generate_page_toc_body(@$elements_list[0]);
869 return &$default_toc_body($elements_list);
872 # Print out the TOC in a <div> at the beginning of the page
873 sub lilypond_print_page_head($)
876 &$default_print_page_head($fh);
877 print $fh "<div id=\"main\">\n";
880 # Print out the TOC in a <div> at the end of th page, which will be formatted as a
881 # sidebar mimicking a TOC frame
882 sub print_lilypond_page_foot($)
885 my $program_string = &$program_string();
886 # print $fh "<p><font size='-1'>$program_string</font><br>$PRE_BODY_CLOSE</p>\n";
887 print $fh "<!-- FOOTER -->\n\n";
888 print $fh "<!-- end div#main here -->\n</div>\n\n";
891 # FIXME: This div and p#languages need to be in div#footer.
892 # Should we move this div to postprocess_html.py ?
893 print $fh "<div id=\"verifier_texinfo\">\n";
894 print $fh "<h3>Validation</h3>\n";
895 print $fh "<p>Thanks to <a href=\"http://www.webdev.nl/\">webdev.nl</a>";
896 print $fh " for hosting <code>lilypond.org</code>.\n";
897 print $fh "<a href=\"http://validator.w3.org/check?uri=referer\">\n";
898 print $fh "<img src=\"http://www.w3.org/Icons/valid-html401\"\n";
899 print $fh " alt=\"Valid HTML 4.01 Transitional\"\n";
900 print $fh " height=\"31\" width=\"88\"></a></p>\n";
904 # Print the TOC frame and reset the TOC:
905 lilypond_print_toc_div ($fh, \@this_page_toc);
909 print $fh "</body>\n</html>\n";
916 #############################################################################
917 ### NICER / MORE FLEXIBLE NAVIGATION PANELS
918 #############################################################################
920 sub get_navigation_text
923 my $text = $NAVIGATION_TEXT{$button};
924 if ( ($button eq 'Back') or ($button eq 'FastBack') ) {
925 $text = $text . $Texi2HTML::NODE{$button} . " ";
926 } elsif ( ($button eq 'Forward') or ($button eq 'FastForward') ) {
927 $text = " " . $Texi2HTML::NODE{$button} . $text;
928 } elsif ( $button eq 'Up' ) {
929 $text = " ".$text.": " . $Texi2HTML::NODE{$button} . " ";
935 # Don't automatically create left-aligned table cells for every link, but
936 # instead create a <td> only on an appropriate '(left|right|center)-aligned-cell-n'
937 # button text. It's alignment as well as the colspan will be taken from the
938 # name of the button. Also, add 'newline' button text to create a new table
939 # row. The texts of the buttons are generated by get_navigation_text and
940 # will contain the name of the next/previous section/chapter.
941 sub lilypond_print_navigation
944 my $vertical = shift;
946 my $result = "<table class=\"nav_table\">\n";
948 $result .= "<tr>" unless $vertical;
950 foreach my $button (@$buttons)
952 $result .= qq{<tr valign="top" align="left">\n} if $vertical;
953 # Allow (left|right|center)-aligned-cell and newline as buttons!
954 if ( $button =~ /^(.*)-aligned-cell-(.*)$/ )
956 $result .= qq{</td>} unless $beginofline;
957 $result .= qq{<td valign="middle" align="$1" colspan="$2">};
960 elsif ( $button eq 'newline' )
962 $result .= qq{</td>} unless $beginofline;
963 $result .= qq{</tr>};
968 elsif (ref($button) eq 'CODE')
970 $result .= &$button($vertical);
972 elsif (ref($button) eq 'SCALAR')
974 $result .= "$$button" if defined($$button);
976 elsif (ref($button) eq 'ARRAY')
978 my $text = $button->[1];
979 my $button_href = $button->[0];
980 # verify that $button_href is simple text and text is a reference
981 if (defined($button_href) and !ref($button_href)
982 and defined($text) and (ref($text) eq 'SCALAR') and defined($$text))
984 if ($Texi2HTML::HREF{$button_href})
986 my $anchor_attributes = '';
987 if ($USE_ACCESSKEY and (defined($BUTTONS_ACCESSKEY{$button_href})) and ($BUTTONS_ACCESSKEY{$button_href} ne ''))
989 $anchor_attributes = "accesskey=\"$BUTTONS_ACCESSKEY{$button_href}\"";
991 if ($USE_REL_REV and (defined($BUTTONS_REL{$button_href})) and ($BUTTONS_REL{$button_href} ne ''))
993 $anchor_attributes .= " rel=\"$BUTTONS_REL{$button_href}\"";
997 $Texi2HTML::HREF{$button_href},
998 get_navigation_text($$text),
1004 $result .= get_navigation_text($$text);
1008 elsif ($button eq ' ')
1009 { # handle space button
1011 ($ICONS && $ACTIVE_ICONS{' '}) ?
1012 &$button_icon_img($BUTTONS_NAME{$button}, $ACTIVE_ICONS{' '}) :
1013 $NAVIGATION_TEXT{' '};
1016 elsif ($Texi2HTML::HREF{$button})
1017 { # button is active
1018 my $btitle = $BUTTONS_GOTO{$button} ?
1019 'title="' . $BUTTONS_GOTO{$button} . '"' : '';
1020 if ($USE_ACCESSKEY and (defined($BUTTONS_ACCESSKEY{$button})) and ($BUTTONS_ACCESSKEY{$button} ne ''))
1022 $btitle .= " accesskey=\"$BUTTONS_ACCESSKEY{$button}\"";
1024 if ($USE_REL_REV and (defined($BUTTONS_REL{$button})) and ($BUTTONS_REL{$button} ne ''))
1026 $btitle .= " rel=\"$BUTTONS_REL{$button}\"";
1028 if ($ICONS && $ACTIVE_ICONS{$button})
1032 $Texi2HTML::HREF{$button},
1033 &$button_icon_img($BUTTONS_NAME{$button},
1034 $ACTIVE_ICONS{$button},
1035 $Texi2HTML::SIMPLE_TEXT{$button}),
1044 $Texi2HTML::HREF{$button},
1045 get_navigation_text($button),
1052 { # button is passive
1054 $ICONS && $PASSIVE_ICONS{$button} ?
1055 &$button_icon_img($BUTTONS_NAME{$button},
1056 $PASSIVE_ICONS{$button},
1057 $Texi2HTML::SIMPLE_TEXT{$button}) :
1059 "[" . get_navigation_text($button) . "]";
1061 $result .= "</td>\n" if $vertical;
1062 $result .= "</tr>\n" if $vertical;
1064 $result .= "</td>" unless $beginofline;
1065 $result .= "</tr>" unless $vertical;
1066 $result .= "</table>\n";
1075 @Texi2HTML::Config::SECTION_BUTTONS =
1076 ('left-aligned-cell-1', 'FastBack',
1077 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1078 'right-aligned-cell-1', 'FastForward',
1080 'left-aligned-cell-2', 'Back',
1081 'center-aligned-cell-1', 'Up',
1082 'right-aligned-cell-2', 'Forward'
1085 # buttons for misc stuff
1086 @Texi2HTML::Config::MISC_BUTTONS = ('center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About');
1088 # buttons for chapter file footers
1089 # (and headers but only if SECTION_NAVIGATION is false)
1090 @Texi2HTML::Config::CHAPTER_BUTTONS =
1091 ('left-aligned-cell-1', 'FastBack',
1092 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1093 'right-aligned-cell-1', 'FastForward',
1096 # buttons for section file footers
1097 @Texi2HTML::Config::SECTION_FOOTER_BUTTONS =
1098 ('left-aligned-cell-1', 'FastBack',
1099 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1100 'right-aligned-cell-1', 'FastForward',
1102 'left-aligned-cell-2', 'Back',
1103 'center-aligned-cell-1', 'Up',
1104 'right-aligned-cell-2', 'Forward'
1107 @Texi2HTML::Config::NODE_FOOTER_BUTTONS =
1108 ('left-aligned-cell-1', 'FastBack',
1109 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1110 'right-aligned-cell-1', 'FastForward',
1112 'left-aligned-cell-2', 'Back',
1113 'center-aligned-cell-1', 'Up',
1114 'right-aligned-cell-2', 'Forward'
1121 #############################################################################
1122 ### FOOTNOTE FORMATTING
1123 #############################################################################
1125 # Format footnotes in a nicer way: Instead of printing the number in a separate
1126 # (nr) heading line, use the standard way of prepending <sup>nr</sup> immediately
1127 # before the fn text.
1130 # The following code is copied from texi2html's examples/makeinfo.init and
1131 # should be updated when texi2html makes some changes there!
1133 my $makekinfo_like_footnote_absolute_number = 0;
1135 sub makeinfo_like_foot_line_and_ref($$$$$$$$)
1137 my $foot_num = shift;
1138 my $relative_num = shift;
1141 my $from_file = shift;
1142 my $footnote_file = shift;
1146 $makekinfo_like_footnote_absolute_number++;
1148 # this is a bit obscure, this allows to add an anchor only if formatted
1149 # as part of the document.
1150 $docid = '' if ($state->{'outside_document'} or $state->{'multiple_pass'});
1152 if ($from_file eq $footnote_file)
1154 $from_file = $footnote_file = '';
1157 my $foot_anchor = "<sup>" . &$anchor($docid, "$footnote_file#$footid", $relative_num) . "</sup>";
1158 $foot_anchor = &$anchor($docid, "$footnote_file#$footid", "($relative_num)") if ($state->{'preformatted'});
1160 # unshift @$lines, "<li>";
1161 # push @$lines, "</li>\n";
1162 return ($lines, $foot_anchor);
1165 sub makeinfo_like_foot_lines($)
1168 unshift @$lines, "<hr>\n<h4>$Texi2HTML::I18n::WORDS->{'Footnotes_Title'}</h4>\n";
1169 #<ol type=\"1\">\n";
1170 # push @$lines, "</ol>";
1174 my %makekinfo_like_paragraph_in_footnote_nr;
1176 sub makeinfo_like_paragraph ($$$$$$$$$$$$$)
1181 my $paragraph_command = shift;
1182 my $paragraph_command_formatted = shift;
1183 my $paragraph_number = shift;
1185 my $item_nr = shift;
1186 my $enumerate_style = shift;
1188 my $command_stack_at_end = shift;
1189 my $command_stack_at_begin = shift;
1191 #print STDERR "format: $format\n" if (defined($format));
1192 #print STDERR "paragraph @$command_stack_at_end; @$command_stack_at_begin\n";
1193 $paragraph_command_formatted = '' if (!defined($paragraph_command_formatted) or
1194 exists($special_list_commands{$format}->{$paragraph_command}));
1195 return '' if ($text =~ /^\s*$/);
1196 foreach my $style(t2h_collect_styles($command_stack_at_begin))
1198 $text = t2h_begin_style($style, $text);
1200 foreach my $style(t2h_collect_styles($command_stack_at_end))
1202 $text = t2h_end_style($style, $text);
1204 if (defined($paragraph_number) and defined($$paragraph_number))
1206 $$paragraph_number++;
1207 return $text if (($format eq 'itemize' or $format eq 'enumerate') and
1208 ($$paragraph_number == 1));
1213 $open .= " align=\"$paragraph_style{$align}\"";
1215 my $footnote_text = '';
1216 if (defined($command_stack_at_begin->[0]) and $command_stack_at_begin->[0] eq 'footnote')
1218 my $state = $Texi2HTML::THISDOC{'state'};
1219 $makekinfo_like_paragraph_in_footnote_nr{$makekinfo_like_footnote_absolute_number}++;
1220 if ($makekinfo_like_paragraph_in_footnote_nr{$makekinfo_like_footnote_absolute_number} <= 1)
1222 $open.=' class="footnote"';
1223 my $document_file = $state->{'footnote_document_file'};
1224 if ($document_file eq $state->{'footnote_footnote_file'})
1226 $document_file = '';
1228 my $docid = $state->{'footnote_place_id'};
1229 my $doc_state = $state->{'footnote_document_state'};
1230 $docid = '' if ($doc_state->{'outside_document'} or $doc_state->{'multiple_pass'});
1231 my $foot_label = &$anchor($state->{'footnote_footnote_id'},
1232 $document_file . "#$state->{'footnote_place_id'}",
1233 "$state->{'footnote_number_in_page'}");
1234 $footnote_text = "<small>[${foot_label}]</small> ";
1237 return $open.'>'.$footnote_text.$text.'</p>';
1241 #############################################################################
1243 #############################################################################
1245 # For split pages, use index.html as start page!
1246 if ($Texi2HTML::Config::SPLIT eq 'section') {
1247 $Texi2HTML::Config::TOP_FILE = 'index.html';
1251 push @Texi2HTML::Config::command_handler_process, \&lilypond_init_toc_depth;