2 # -*- coding: utf-8; -*-
4 # this file is a complete mess. Some things are commented out
5 # because that's the only way it works. About 70% of the code in
6 # this file is never called. All in all, it's in drastic need of
7 # a complete review/editing, but we don't have the time right now.
10 ### WTF, perl cannot read a file?
11 ###use File::Slurp qw( read_file );
13 ### texi2html customization script for LilyPond
14 ### Author: Reinhold Kainhofer <reinhold@kainhofer.com>, 2008.
15 ### Some code parts copied from texi2html and adapted. These functions
16 ### were written mainly by Patrice Dumas
20 ### Features implemented here:
21 ### -) For split manuals, the main page is index.html.
22 ### -) All @unnumbered* sections are placed into the same file
23 ### (implemented by split_at_numbered_sections)
24 ### -) Use our custom CSS file, with IE-specific fixes in another CSS file,
25 ### impelmented by lilypond_css_lines
26 ### -) TOC (folded, with the current page highlighted) in an overflown <div>
27 ### is added to every page; implemented by:
28 ### lilypond_print_element_header -- building of the TOC
29 ### lilypond_toc_body -- generation of customized TOC output
30 ### lilypond_print_page_head -- start <div id="main">
31 ### print_lilypond_page_foot -- closing id=main, output of footer & TOC
32 ### -) External refs are formatted only as "Text of the node" (not as >>see
33 ### "NODE" section "SECTION" in "BOOK"<< like with default texi2html). Also,
34 ### the leading "(book-name)" is removed.
35 ### Implemented by overriding lilypond_external_ref
36 ### -) Navigation bars on top/bottom of the page and between sections are not
37 ### left-aligned, but use a combination of left/center/right aligned table
38 ### cells; For this, I heavily extend the texi2html code to allow for
39 ### differently aligned cells and for multi-line tables);
40 ### Implemented in lilypond_print_navigation
41 ### -) Different formatting than the default: example uses the same formatting
43 ### -) Allow translated section titles: All section titles can be translated,
44 ### the original (English) title is associated with @translationof. This is
45 ### needed, because the file name / anchor is generated from the original
46 ### English title, since otherwise language-autoselection would break with
48 ### Since it is then no longer possible to obtain the file name from the
49 ### section title, I keep a sectionname<=>filename/anchor around. This way,
50 ### xrefs from other manuals can simply load that map and retrieve the
51 ### correct file name for the link. Implemented in:
52 ### lilypond_unknown (handling of @translationof, in case
53 ### extract_texi_filenames.py messes up...)
54 ### lilypond_element_file_name (correct file name: use the map)
55 ### lilypond_element_target_name (correct anchor: use the map)
56 ### lilypond_init_map (read in the externally created map from disk)
57 ### lilypond_external_href (load the map for xrefs, use the correct
59 ### -) The HTML anchors for all sections are derived from the node name /
60 ### section title (pre-generated in the .xref-map file). Implemented by:
61 ### lilypond_element_target_name (adjust section anchors)
62 ### -) Use the standard footnote format "<sup>nr</sup> text" instead of the
63 ### ugly format of texi2html (<h3>(nr)</h3><p>text</p>). Implemented in
64 ### makeinfo_like_foot_line_and_ref
65 ### makeinfo_like_foot_lines
66 ### makeinfo_like_paragraph
69 ### Useful helper functions:
70 ### -) texinfo_file_name($node_name): returns a texinfo-compatible file name
71 ### for the given string $node_name (whitespace trimmed/replaced by -,
72 ### non-standard chars replaced by _xxxx (ascii char code) and forced to
73 ### start with a letter by prepending t_g if necessary)
76 package Texi2HTML::Config;
78 #############################################################################
80 #############################################################################
83 my $LY_LANGUAGES = {};
84 $LY_LANGUAGES->{'fr'} = {
85 'Back to Documentation Index' => 'Retour à l\'accueil de la documentation',
87 $LY_LANGUAGES->{'es'} = {
88 'Back to Documentation Index' => 'Volver al índice de la documentación',
90 $LY_LANGUAGES->{'de'} = {
91 'Back to Documentation Index' => 'Zur Dokumentationsübersicht',
93 $LY_LANGUAGES->{'ja'} = {
94 'Back to Documentation Index' => 'ドキュメント インデックスに戻る',
98 sub ly_get_string () {
99 my $lang = $Texi2HTML::THISDOC{current_lang};
101 if ($lang and $lang ne "en" and $LY_LANGUAGES->{$lang}->{$string}) {
102 return $LY_LANGUAGES->{$lang}->{$string};
109 #############################################################################
110 ### SETTINGS FOR TEXI2HTML
111 #############################################################################
113 # Validation fix for texi2html<=1.82
114 $Texi2HTML::Config::DOCTYPE = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
116 @Texi2HTML::Config::CSS_REFS = (
117 {FILENAME => "lilypond-web.css", TITLE => "Default style"},
120 @Texi2HTML::Config::ALT_CSS_REFS = (
121 {FILENAME => "lilypond-web-alt1.css", TITLE => "Alternate style 1"},
122 {FILENAME => "lilypond-web-alt2.css", TITLE => "Alternate style 2"},
125 $Texi2HTML::Config::USE_ACCESSKEY = 1;
126 $Texi2HTML::Config::USE_LINKS = 1;
127 $Texi2HTML::Config::USE_REL_REV = 1;
128 $Texi2HTML::Config::SPLIT_INDEX = 0;
129 $Texi2HTML::Config::SEPARATED_FOOTNOTES = 0; # Print footnotes on same page, not separated
130 # FIXME: creates duplicate anchors, which causes Opera to barf;
131 # should be fixed in lilypond-texi2html.init too
132 # Uhm, what about a bug report to Opera? We need sane names here.
133 #if ($Texi2HTML::Config::SPLIT eq 'section') {
134 # $Texi2HTML::Config::element_file_name = \&lilypond_element_file_name;
136 $Texi2HTML::Config::element_target_name = \&lilypond_element_target_name;
137 $default_print_element_header = $Texi2HTML::Config::print_element_header;
138 $Texi2HTML::Config::print_element_header = \&lilypond_print_element_header;
139 $Texi2HTML::Config::print_page_foot = \&print_lilypond_page_foot;
140 $Texi2HTML::Config::print_navigation = \&lilypond_print_navigation;
141 $Texi2HTML::Config::external_ref = \&lilypond_external_ref;
142 $default_external_href = $Texi2HTML::Config::external_href;
143 $Texi2HTML::Config::external_href = \&lilypond_external_href;
144 $default_toc_body = $Texi2HTML::Config::toc_body;
145 $Texi2HTML::Config::toc_body = \&lilypond_toc_body;
146 $Texi2HTML::Config::css_lines = \&lilypond_css_lines;
147 $default_unknown = $Texi2HTML::Config::unknown;
148 $Texi2HTML::Config::unknown = \&lilypond_unknown;
149 $default_print_page_head = $Texi2HTML::Config::print_page_head;
150 $Texi2HTML::Config::print_page_head = \&lilypond_print_page_head;
151 # $Texi2HTML::Config::foot_line_and_ref = \&lilypond_foot_line_and_ref;
152 $Texi2HTML::Config::foot_line_and_ref = \&makeinfo_like_foot_line_and_ref;
153 $Texi2HTML::Config::foot_lines = \&makeinfo_like_foot_lines;
154 $Texi2HTML::Config::paragraph = \&makeinfo_like_paragraph;
158 # Examples should be formatted similar to quotes:
159 $Texi2HTML::Config::complex_format_map->{'example'} = {
160 'begin' => q{"<blockquote>"},
161 'end' => q{"</blockquote>\n"},
165 %Texi2HTML::config::misc_pages_targets = (
166 'Overview' => 'Overview',
167 'Contents' => 'Contents',
172 my @section_to_filename;
177 #############################################################################
179 #############################################################################
182 $Data::Dumper::Maxdepth = 2;
184 sub print_element_info($)
187 print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
188 print "Element: $element\n";
189 print Dumper($element);
196 #############################################################################
198 #############################################################################
200 # Convert a given node name to its proper file name (normalization as explained
201 # in the texinfo manual:
202 # http://www.gnu.org/software/texinfo/manual/texinfo/html_node/HTML-Xref-Node-Name-Expansion.html
203 sub texinfo_file_name($)
207 # File name normalization by texinfo:
208 # 1/2: letters and numbers are left unchanged
209 # 3/4: multiple, leading and trailing whitespace is removed
210 $text = main::normalise_space($text);
211 # 5/6: all remaining spaces are converted to '-', all other 7- or 8-bit
212 # chars are replaced by _xxxx (xxxx=ascii character code)
213 while ($text ne '') {
214 if ($text =~ s/^([A-Za-z0-9]+)//o) { # number or letter stay unchanged
216 } elsif ($text =~ s/^ //o) { # space -> '-'
218 } elsif ($text =~ s/^(.)//o) { # Otherwise use _xxxx (ascii char code)
220 if ( $ccode <= 0xFFFF ) {
221 $result .= sprintf("_%04x", $ccode);
223 $result .= sprintf("__%06x", $ccode);
227 # 7: if name does not begin with a letter, prepend 't_g' (so it starts with a letter)
228 if ($result !~ /^[a-zA-Z]/) {
229 $result = 't_g' . $result;
236 # Load a file containing a nodename<=>filename map (tab-sepatared, i.e.
237 # NODENAME\tFILENAME\tANCHOR
238 # Returns a ref to a hash "Node title" => ["FilenameWithoutExt", "Anchor"]
239 sub load_map_file ($)
244 if (open(XREFFILE, $mapfile)) {
245 print STDERR "Reading map file: $mapfile\n";
247 while ( $line = <XREFFILE> ) {
248 # parse the tab-separated entries and insert them into the map:
250 my @entries = split(/\t/, $line);
251 if (scalar (@entries) == 3) {
252 $node_map->{$entries[0]} = [$entries[1], $entries[2]];
254 print STDERR "Invalid entry in the node file $mapfile: $line\n";
259 print STDERR "warning: cannot load map file: no such file: $mapfile\n";
265 # Split the given path into dir and basename (with .texi removed). Used mainly
266 # to get the path/basename of the original texi input file
267 sub split_texi_filename ($)
270 my ($docu_dir, $docu_name);
271 if ($docu =~ /(.*\/)/) {
272 chop($docu_dir = $1);
274 $docu_name =~ s/.*\///;
279 $docu_name =~ s/\.te?x(i|info)?$//;
280 return ($docu_dir, $docu_name);
287 #############################################################################
289 #############################################################################
291 # Include our standard CSS file, not hard-coded CSS code directly in the HTML!
292 # For IE, conditionally include the lilypond-ie-fixes.css style sheet
293 sub lilypond_css_lines ($$)
295 my $import_lines = shift;
296 my $rule_lines = shift;
297 return if (defined($Texi2HTML::THISDOC{'CSS_LINES'}));
298 if (@$rule_lines or @$import_lines)
300 $Texi2HTML::THISDOC{'CSS_LINES'} = "<style type=\"text/css\">\n<!--\n";
301 $Texi2HTML::THISDOC{'CSS_LINES'} .= join('',@$import_lines) . "\n" if (@$import_lines);
302 $Texi2HTML::THISDOC{'CSS_LINES'} .= join('',@$rule_lines) . "\n" if (@$rule_lines);
303 $Texi2HTML::THISDOC{'CSS_LINES'} .= "-->\n</style>\n";
305 foreach my $ref (@CSS_REFS)
307 $Texi2HTML::THISDOC{'CSS_LINES'} .= "<link rel=\"stylesheet\" type=\"text/css\" title=\"$ref->{TITLE}\" href=\"$ref->{FILENAME}\">\n";
310 foreach my $ref (@Texi2HTML::Config::ALT_CSS_REFS)
312 $Texi2HTML::THISDOC{'CSS_LINES'} .= "<link rel=\"alternate stylesheet\" type=\"text/css\" href=\"$ref->{FILENAME}\" title=\"$ref->{TITLE}\">\n";
315 # GOP tweak: We aren't using an IE-specific stylesheet
316 #$Texi2HTML::THISDOC{'CSS_LINES'} .= "<!--[if lte IE 7]>\n<link href=\"lilypond-ie-fixes.css\" rel=\"stylesheet\" type=\"text/css\">\n<![endif]-->\n";
323 #############################################################################
324 ### SPLITTING BASED ON NUMBERED SECTIONS
325 #############################################################################
327 # FIXME: removed for GOP.
329 # Uhm, yes: that's what I found. It was gone. No need to add that
330 # in a comment, DIFF tells me that it's gone.
332 # Comments can be used to tell *why* something has been removed -- as
333 # it seems to me that we *need* this in order to get sane,
334 # untranslated, html file names? Putting it back for this reason.
338 my $node_to_filename_map = ();
341 # This function makes sure that files are only generated for numbered sections,
342 # but not for unnumbered ones. It is called after texi2html has done its own
343 # splitting and simply returns the filename for the node given as first argument
344 # Nodes with the same filename will be printed out to the same filename, so
345 # this really all we need. Also, make sure that the file names for sections
346 # are derived from the section title. We also might want to name the anchors
347 # according to node titles, which works by simply overriding the id element of
349 # If an external nodename<=>filename/anchor map file is found (loaded in
350 # the command handler, use the externally created values, otherwise use the
352 sub lilypond_element_file_name($$$)
356 my $docu_name = shift;
357 my $docu_ext = $Texi2HTML::Config::EXTENSION;
359 my $node_name = main::remove_texi($element->{'node_ref'}->{'texi'});
360 # the snippets page does not use nodes for the snippets, so in this case
361 # we'll have to use the section name!
362 if ($node_name eq '') {
363 $node_name = main::remove_texi($element->{'texi'});
366 # If we have an entry in the section<=>filename map, use that one, otherwise
367 # generate the filename/anchor here. In the latter case, external manuals
368 # will not be able to retrieve the file name for xrefs!!! Still, I already
369 # had that code, so I'll leave it in in case something goes wrong with the
370 # extract_texi_filenames.py script in the lilypond build process!
371 if (exists ($node_to_filename_map->{$node_name})) {
372 (my $filename, my $anchor) = @{$node_to_filename_map->{$node_name}};
373 $filename .= ".$docu_ext" if (defined($docu_ext));
375 # unnumbered sections (except those at top-level!) always go to the same
376 # file as the previous numbered section
377 if (not ($element->{number}) and not ($lastfilename eq '') and ($element->{level} > 1)) {
378 $filename = $lastfilename;
380 if (($filename eq $lastfilename)) {
381 $$element{doc_nr} = $docnr;
384 $$element{doc_nr} = $docnr;
385 $lastfilename = $filename;
389 } elsif ($type eq "top" or $type eq "toc" or $type eq "doc" or $type eq "stoc" or $type eq "foot" or $type eq "about") {
392 print STDERR "WARNING: Node '$node_name' was NOT found in the map\n"
393 unless ($node_name eq '') or ($element->{'tag'} eq 'unnumberedsec')
394 or ($node_name =~ /NOT REALLY USED/);
396 # Numbered sections will get a filename Node_title, unnumbered sections will use
397 # the file name of the previous numbered section:
398 if (($element->{number}) or ($lastfilename eq '') or ($element->{level} == 1)) {
399 # normalize to the same file name as texinfo
400 if ($element->{translationof}) {
401 $node_name = main::remove_texi($element->{translationof});
403 my $filename = texinfo_file_name($node_name);
404 $filename .= ".$docu_ext" if (defined($docu_ext));
406 $$element{doc_nr} = $docnr;
407 $lastfilename = $filename;
410 $$element{doc_nr} = $docnr;
411 return $lastfilename;
418 sub lilypond_element_target_name($$$)
423 # Target is based on node name (or sec name for secs without node attached)
424 my $node_name = main::remove_texi($element->{'node_ref'}->{'texi'});
425 if ($node_name eq '') {
426 $node_name = main::remove_texi($element->{'texi'});
429 # If we have an entry in the section<=>filename map, use that one, otherwise
430 # generate the anchor here.
431 if (exists ($node_to_filename_map->{$node_name})) {
432 (my $filename, $target) = @{$node_to_filename_map->{$node_name}};
434 my $anchor = $node_name;
435 if ($element->{translationof}) {
436 $anchor = main::remove_texi($element->{translationof});
438 # normalize to the same file name as texinfo
439 $target = texinfo_file_name($anchor);
441 # TODO: Once texi2html correctly prints out the target and not the id for
442 # the sections, change this back to ($id, $target)
443 return ($target, $target);
447 ## Load the map file for the corrently processed texi file. We do this
448 # using a command init handler, since texi2html does not have any
449 # other hooks that are called after THISDOC is filled but before phase 2
450 # of the texi2html conversion.
451 sub lilypond_init_map ()
453 my ($docu_dir, $docu_name) = split_texi_filename ($Texi2HTML::THISDOC{'input_file_name'});
454 my $map_filename = main::locate_include_file ("${docu_name}.$Texi2HTML::THISDOC{current_lang}.xref-map")
455 || main::locate_include_file ("${docu_name}.xref-map");
456 $node_to_filename_map = load_map_file ($map_filename);
458 push @Texi2HTML::Config::command_handler_init, \&lilypond_init_map;
461 #############################################################################
462 ### CLEANER LINK TITLE FOR EXTERNAL REFS
463 #############################################################################
465 # The default formatting of external refs returns e.g.
466 # "(lilypond-internals)Timing_translator", so we remove all (...) from the
467 # file_and_node argument. Also, we want only a very simple format, so we don't
468 # even call the default handler!
469 sub lilypond_external_ref($$$$$$)
474 my $file_node = shift;
476 my $cross_ref = shift;
478 my $displaytext = '';
480 # 1) if we have a cross ref name, that's the text to be displayed:
481 # 2) For the top node, use the (printable) name of the manual, unless we
482 # have an explicit cross ref name
483 # 3) In all other cases use the section name
484 if ($cross_ref ne '') {
485 $displaytext = $cross_ref;
486 } elsif (($section eq '') or ($section eq 'Top')) {
487 $displaytext = $book;
489 $displaytext = $section;
492 $displaytext = &$anchor('', $href, $displaytext) if ($displaytext ne '');
493 return &$I('%{node_file_href}', { 'node_file_href' => $displaytext });
500 #############################################################################
501 ### HANDLING TRANSLATED SECTIONS: handle @translationof, secname<->filename
502 ### map stored on disk, xrefs in other manuals load that map
503 #############################################################################
506 # Try to make use of @translationof to generate files according to the original
507 # English section title...
508 sub lilypond_unknown($$$$$)
516 # the @translationof macro provides the original English section title,
517 # which should be used for file/anchor naming, while the title will be
518 # translated to each language
519 # It is already used by extract_texi_filenames.py, so this should not be
520 # necessary here at all. Still, I'll leave the code in just in case the
521 # python script messed up ;-)
522 if ($pass == 1 and $macro eq "translationof") {
523 if (ref($state->{'element'}) eq 'HASH') {
524 $state->{'element'}->{'translationof'} = main::normalise_space($line);
526 return ('', 1, undef, undef);
528 return &$default_unknown($macro, $line, $pass, $stack, $state);
535 my %translated_books = ();
536 # Construct a href to an external source of information.
537 # node is the node with texinfo @-commands
538 # node_id is the node transliterated and transformed as explained in the
540 # node_xhtml_id is the node transformed such that it is unique and can
541 # be used to make an html cross ref as explained in the texinfo manual
542 # file is the file in '(file)node'
543 sub lilypond_external_href($$$)
547 my $node_hxmlt_id = shift;
550 # 1) Keep a hash of book->section_map
551 # 2) if not file in keys hash => try to load the map (assign empty map if
552 # non-existent => will load only once!)
553 # 3) if node in the section=>(file, anchor) map, replace node_id and
554 # node_xhtml_id by the map's values
555 # 4) call the default_external_href with these values (or the old ones if not found)
557 if (($node_id ne '') and defined($file) and ($node_id ne 'Top')) {
558 my $map_name = $file;
559 $map_name =~ s/-big-page//;
561 # Load the map if we haven't done so already
562 if (!exists($translated_books{$map_name})) {
563 my ($docu_dir, $docu_name) = split_texi_filename ($Texi2HTML::THISDOC{'input_file_name'});
564 my $map_filename = main::locate_include_file ("${map_name}.$Texi2HTML::THISDOC{current_lang}.xref-map")
565 || main::locate_include_file ("${map_name}.xref-map");
566 $translated_books{$map_name} = load_map_file ($map_filename);
569 # look up translation. use these values instead of the old filename/anchor
570 my $section_name_map = $translated_books{$map_name};
571 my $node_text = main::remove_texi($node);
572 if (defined($section_name_map->{$node_text})) {
573 ($node_id, $node_hxmlt_id) = @{$section_name_map->{$node_text}};
575 print STDERR "WARNING: Unable to find node '$node_text' in book $map_name.\n";
580 return &$default_external_href($node, $node_id, $node_hxmlt_id, $file);
582 return &$default_external_href($node, $node_id, $node_hxmlt_id);
590 #############################################################################
591 ### CUSTOM TOC FOR EACH PAGE (in a frame on the left)
592 #############################################################################
594 my $page_toc_depth = 2;
595 my @default_toc = [];
597 # Initialize the toc_depth to 1 if the command-line option -D=short_toc is given
598 sub lilypond_init_toc_depth ()
600 if (exists($main::value{'short_toc'}) and not exists($main::value{'bigpage'})) {
604 # Set the TOC-depth (depending on a texinfo variable short_toc) in a
605 # command-handler, so we have them available when creating the pages
606 push @Texi2HTML::Config::command_handler_process, \&lilypond_init_toc_depth;
609 # recursively generate the TOC entries for the element and its children (which
610 # are only shown up to maxlevel. All ancestors of the current element are also
611 # shown with their immediate children, irrespective of their level.
612 # Unnumbered entries are only printed out if they are at top-level or 2nd level
613 # or their parent element is an ancestor of the currently viewed node.
614 # The conditions to call this method to print the entry for a child node is:
615 # -) the parent is an ancestor of the current page node
616 # -) the parent is a numbered element at top-level toplevel (i.e. show numbered
617 # and unnumbered 2nd-level children of numbered nodes)
618 # -) the child element is a numbered node below level maxlevel
619 sub generate_ly_toc_entries($$$)
622 my $element_path = shift;
624 #my $maxlevel = shift;
626 # Skip undefined sections, plus all sections generated by index splitting
627 return() if (not defined($element) or exists($element->{'index_page'}));
629 my $level = $element->{'toc_level'};
630 my $is_parent_of_current = $element->{'id'} && $element_path->{$element->{'id'}};
631 my $ind = ' ' x $level;
632 # GDP hack: leave this open for color
633 my $this_css_class = " class=\"";
634 $this_css_class .= $is_parent_of_current ? " toc_current" : "";
636 # GDP tweak: if the node name is in the color_X list
637 # TODO: use a hash, load from file?
638 # FIXME: load from file, to allow for translations!
642 "Learning", "Glossary", "Essay",
643 "Contact", "Tiny examples", "Bug reports"
646 "Features", "Examples", "Freedom", "Background",
647 "Unix", "MacOS X", "Windows",
648 "Notation", "Usage", "Snippets",
649 "Help us", "Development", "Authors"
652 "Productions", "Testimonials",
653 "Source", "Old downloads",
654 "FAQ", "Changes", "Internals",
655 "Publications", "Old news"
658 "Text input", "Alternate input",
660 "Translated", "All", "FDL"
663 my $addColor = " colorDefault";
664 foreach $color (@color_1) {
665 if ($element->{'text'} eq $color) {
666 $addColor = " color1";
669 foreach $color (@color_2) {
670 if ($element->{'text'} eq $color) {
671 $addColor = " color2";
674 foreach $color (@color_3) {
675 if ($element->{'text'} eq $color) {
676 $addColor = " color3";
679 foreach $color (@color_4) {
680 if ($element->{'text'} eq $color) {
681 $addColor = " color4";
685 $this_css_class .= $addColor . "\"";
690 my $entry = "$ind<li$this_css_class>" . &$anchor ($element->{'tocid'}, "$element->{'file'}#$element->{'target'}",$element->{'text'});
692 push (@result, $entry);
693 my $children = $element->{'section_childs'};
694 if (defined($children) and (ref($children) eq "ARRAY")) {
695 my $force_children = $is_parent_of_current or ($level == 1 and $element->{'number'});
696 my @child_result = ();
697 foreach my $c (@$children) {
698 my $is_numbered_child = defined ($c->{'number'});
699 my $below_maxlevel = $c->{'toc_level'} le $maxlevel;
700 if ($force_children or ($is_numbered_child and $below_maxlevel)) {
701 my @child_res = generate_ly_toc_entries($c, $element_path, $maxlevel);
702 push (@child_result, @child_res);
705 # if no child nodes were generated, e.g. for the index, where expanded pages
706 # are ignored, don't generate a list at all...
708 push (@result, "\n$ind<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
710 # GOP tweak: (next 2 lines; alternates)
711 push (@result, "$ind<li$this_css_class>" . &$anchor ($element->{'tocid'}, "$element->{'file'}#$element->{'target'}","(main)"));
712 #push (@result, "$ind<li$this_css_class>" . &$anchor ($element->{'tocid'}, "$element->{'file'}#$element->{'target'}","(".$element->{'text'}." main)"));
713 push (@result, @child_result);
714 push (@result, "$ind</ul>\n");
717 push (@result, "$ind</li>\n");
722 # Print a customized TOC, containing only the first two levels plus the whole
723 # path to the current page
724 sub lilypond_generate_page_toc_body($)
727 my $current_element = $element;
729 $parentelements{$element->{'id'}} = 1;
730 # Find the path to the current element
731 while ( defined($current_element->{'sectionup'}) and
732 ($current_element->{'sectionup'} ne $current_element) )
734 $parentelements{$current_element->{'sectionup'}->{'id'}} = 1
735 if ($current_element->{'sectionup'}->{'id'} ne '');
736 $current_element = $current_element->{'sectionup'};
737 if (exists($main::value{'shallow_toc'})) {
741 return () if not defined($current_element);
742 # Create the toc entries recursively
744 my @toc_entries = ("<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
745 # my @toc_entries = ("<div class=\"contents\">\n", "<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
747 # FIXME: add link to main page, really hackily.
748 if ($element->{'sectionup'}) {
749 # it's not the top element
750 push (@toc_entries, "<li><a href=\"index.html\">Main</a></li>\n");
752 push (@toc_entries, "<li class=\"toc_current\"><a href=\"index.html\">Main</a></li>\n");
754 my $children = $current_element->{'section_childs'};
755 # FIXME: generate toc
756 foreach ( @$children ) {
757 push (@toc_entries, generate_ly_toc_entries($_, \%parentelements, $page_toc_depth));
759 if (!exists($main::value{'shallow_toc'})) {
760 # WTF, perl needs 6 lines of magic to do: ' ' + open ('file-name').read ()?
762 my $name = "search-box.html";
763 open FILE, $name or open FILE, "$ENV{SRC_DIR}/$name" or die die "no such file: $name: $!";
765 # All these also seems to work, but fail silently. Great, it runs!
766 # It's late already, let's this broken site.
768 # push (@toc_entries, '<li>\n' + <FILE> + '</li>\n');
769 # push (@toc_entries, '<li>\n' . <FILE> . '</li>\n');
770 # my $string = '<li>\n' + <FILE> + '</li>\n';
771 # my $string = '<li>\n' + <FILE> + '</li>\n';
772 # my $string = '<li>\n' . <FILE> . '</li>\n';
774 # $string = '<li>\n' + $string + '</li>\n';
775 $string = "<li>\n" . $string . "</li>\n";
776 push (@toc_entries, $string);
779 push (@toc_entries, "</ul>\n");
781 # push (@toc_entries, "</div>\n");
782 push (@toc_entries, "\n");
786 sub lilypond_print_toc_div ($$)
790 my @lines = @$tocref;
791 # use default TOC if no custom lines have been generated
792 @lines = @default_toc if (not @lines);
795 print $fh "\n\n<div id=\"tocframe\">\n";
796 #print $fh "<div class=\"contents\">\n";
798 # Remove the leading "GNU LilyPond --- " from the manual title
799 my $topname = $Texi2HTML::NAME{'Top'};
800 $topname =~ s/^GNU LilyPond(:| &[mn]dash;) //;
802 # construct the top-level Docs index (relative path and including language!)
803 my $lang = $Texi2HTML::THISDOC{current_lang};
804 if ($lang and $lang ne "en") {
810 $reldir = "../" if ($Texi2HTML::Config::SPLIT eq 'section');
811 my $uplink = $reldir."index.${lang}html";
813 # print $fh "<p class=\"toc_uplink\"><a href=\"$uplink\"
814 # title=\"Documentation Index\"><< " .
815 # &ly_get_string ('Back to Documentation Index') .
818 # print $fh '<h4 class="toc_header"> ' . &$anchor('',
819 # $Texi2HTML::HREF{'Top'},
821 # 'title="Start of the manual"'
825 foreach my $line (@lines) {
829 print $fh "</div>\n\n";
833 # Create the custom TOC for this page (partially folded, current page is
834 # highlighted) and store it in a global variable. The TOC is written out after
835 # the html contents (but positioned correctly using CSS), so that browsers with
836 # css turned off still show the contents first.
837 our @this_page_toc = ();
838 sub lilypond_print_element_header
840 my $first_in_page = shift;
841 my $previous_is_top = shift;
842 if ($first_in_page and not @this_page_toc) {
843 if (defined($Texi2HTML::THIS_ELEMENT)) {
844 # Create the TOC for this page
845 @this_page_toc = lilypond_generate_page_toc_body($Texi2HTML::THIS_ELEMENT);
848 return &$default_print_element_header( $first_in_page, $previous_is_top);
851 # Generate the HTML output for the TOC
852 sub lilypond_toc_body($)
854 my $elements_list = shift;
855 # Generate a default TOC for pages without THIS_ELEMENT
856 @default_toc = lilypond_generate_page_toc_body(@$elements_list[0]);
857 return &$default_toc_body($elements_list);
860 # Print out the TOC in a <div> at the beginning of the page
861 sub lilypond_print_page_head($)
864 &$default_print_page_head($fh);
865 print $fh "<div id=\"main\">\n";
868 # Print out the TOC in a <div>, which will be formatted as a
869 # sidebar mimicking a TOC frame
870 sub print_lilypond_page_foot($)
873 my $program_string = &$program_string();
874 # print $fh "<p><font size='-1'>$program_string</font><br>$PRE_BODY_CLOSE</p>\n";
876 # Do not include language selection in div#main
877 print $fh "<!-- end div#main here -->\n</div>\n\n";
880 print $fh "<!-- FOOTER -->\n\n";
881 print $fh "<div id=\"footer\">\n";
882 print $fh "<div id=\"language\">\n";
883 print $fh "<h3>Other languages</h3>\n";
884 print $fh "<p><a href=\"\">Deutsch</a>, Español, Français, Magyar.</p>\n";
885 print $fh "<p>About automatic language selection.</p>\n";
886 print $fh "</div>\n";
889 # FIXME: This div and p#languages need to be in div#footer.
890 # Should we move this div to postprocess_html.py ?
891 print $fh "<div id=\"verifier_texinfo\">\n";
892 print $fh "<h3>Validation</h3>\n";
893 print $fh "<p>Thanks to <a href=\"http://www.webdev.nl/\">webdev.nl</a>";
894 print $fh " for hosting <code>lilypond.org</code>.\n";
895 print $fh "<a href=\"http://validator.w3.org/check?uri=referer\">\n";
896 print $fh "<img src=\"http://www.w3.org/Icons/valid-html401\"\n";
897 print $fh " alt=\"Valid HTML 4.01 Transitional\"\n";
898 print $fh " height=\"31\" width=\"88\"></a></p>\n";
901 # Print the TOC frame and reset the TOC:
902 lilypond_print_toc_div ($fh, \@this_page_toc);
906 print $fh "</body>\n</html>\n";
913 #############################################################################
914 ### NICER / MORE FLEXIBLE NAVIGATION PANELS
915 #############################################################################
917 sub get_navigation_text
920 my $text = $NAVIGATION_TEXT{$button};
921 if ( ($button eq 'Back') or ($button eq 'FastBack') ) {
922 $text = $text . $Texi2HTML::NODE{$button} . " ";
923 } elsif ( ($button eq 'Forward') or ($button eq 'FastForward') ) {
924 $text = " " . $Texi2HTML::NODE{$button} . $text;
925 } elsif ( $button eq 'Up' ) {
926 $text = " ".$text.": " . $Texi2HTML::NODE{$button} . " ";
932 # Don't automatically create left-aligned table cells for every link, but
933 # instead create a <td> only on an appropriate '(left|right|center)-aligned-cell-n'
934 # button text. It's alignment as well as the colspan will be taken from the
935 # name of the button. Also, add 'newline' button text to create a new table
936 # row. The texts of the buttons are generated by get_navigation_text and
937 # will contain the name of the next/previous section/chapter.
938 sub lilypond_print_navigation
941 my $vertical = shift;
943 my $result = "<table class=\"nav_table\">\n";
945 $result .= "<tr>" unless $vertical;
947 foreach my $button (@$buttons)
949 $result .= qq{<tr valign="top" align="left">\n} if $vertical;
950 # Allow (left|right|center)-aligned-cell and newline as buttons!
951 if ( $button =~ /^(.*)-aligned-cell-(.*)$/ )
953 $result .= qq{</td>} unless $beginofline;
954 $result .= qq{<td valign="middle" align="$1" colspan="$2">};
957 elsif ( $button eq 'newline' )
959 $result .= qq{</td>} unless $beginofline;
960 $result .= qq{</tr>};
965 elsif (ref($button) eq 'CODE')
967 $result .= &$button($vertical);
969 elsif (ref($button) eq 'SCALAR')
971 $result .= "$$button" if defined($$button);
973 elsif (ref($button) eq 'ARRAY')
975 my $text = $button->[1];
976 my $button_href = $button->[0];
977 # verify that $button_href is simple text and text is a reference
978 if (defined($button_href) and !ref($button_href)
979 and defined($text) and (ref($text) eq 'SCALAR') and defined($$text))
981 if ($Texi2HTML::HREF{$button_href})
983 my $anchor_attributes = '';
984 if ($USE_ACCESSKEY and (defined($BUTTONS_ACCESSKEY{$button_href})) and ($BUTTONS_ACCESSKEY{$button_href} ne ''))
986 $anchor_attributes = "accesskey=\"$BUTTONS_ACCESSKEY{$button_href}\"";
988 if ($USE_REL_REV and (defined($BUTTONS_REL{$button_href})) and ($BUTTONS_REL{$button_href} ne ''))
990 $anchor_attributes .= " rel=\"$BUTTONS_REL{$button_href}\"";
994 $Texi2HTML::HREF{$button_href},
995 get_navigation_text($$text),
1001 $result .= get_navigation_text($$text);
1005 elsif ($button eq ' ')
1006 { # handle space button
1008 ($ICONS && $ACTIVE_ICONS{' '}) ?
1009 &$button_icon_img($BUTTONS_NAME{$button}, $ACTIVE_ICONS{' '}) :
1010 $NAVIGATION_TEXT{' '};
1013 elsif ($Texi2HTML::HREF{$button})
1014 { # button is active
1015 my $btitle = $BUTTONS_GOTO{$button} ?
1016 'title="' . $BUTTONS_GOTO{$button} . '"' : '';
1017 if ($USE_ACCESSKEY and (defined($BUTTONS_ACCESSKEY{$button})) and ($BUTTONS_ACCESSKEY{$button} ne ''))
1019 $btitle .= " accesskey=\"$BUTTONS_ACCESSKEY{$button}\"";
1021 if ($USE_REL_REV and (defined($BUTTONS_REL{$button})) and ($BUTTONS_REL{$button} ne ''))
1023 $btitle .= " rel=\"$BUTTONS_REL{$button}\"";
1025 if ($ICONS && $ACTIVE_ICONS{$button})
1029 $Texi2HTML::HREF{$button},
1030 &$button_icon_img($BUTTONS_NAME{$button},
1031 $ACTIVE_ICONS{$button},
1032 $Texi2HTML::SIMPLE_TEXT{$button}),
1041 $Texi2HTML::HREF{$button},
1042 get_navigation_text($button),
1049 { # button is passive
1051 $ICONS && $PASSIVE_ICONS{$button} ?
1052 &$button_icon_img($BUTTONS_NAME{$button},
1053 $PASSIVE_ICONS{$button},
1054 $Texi2HTML::SIMPLE_TEXT{$button}) :
1056 "[" . get_navigation_text($button) . "]";
1058 $result .= "</td>\n" if $vertical;
1059 $result .= "</tr>\n" if $vertical;
1061 $result .= "</td>" unless $beginofline;
1062 $result .= "</tr>" unless $vertical;
1063 $result .= "</table>\n";
1069 @Texi2HTML::Config::SECTION_BUTTONS =
1070 ('left-aligned-cell-1', 'FastBack',
1071 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1072 'right-aligned-cell-1', 'FastForward',
1074 'left-aligned-cell-2', 'Back',
1075 'center-aligned-cell-1', 'Up',
1076 'right-aligned-cell-2', 'Forward'
1079 # buttons for misc stuff
1080 @Texi2HTML::Config::MISC_BUTTONS = ('center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About');
1082 # buttons for chapter file footers
1083 # (and headers but only if SECTION_NAVIGATION is false)
1084 @Texi2HTML::Config::CHAPTER_BUTTONS =
1085 ('left-aligned-cell-1', 'FastBack',
1086 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1087 'right-aligned-cell-1', 'FastForward',
1090 # buttons for section file footers
1091 @Texi2HTML::Config::SECTION_FOOTER_BUTTONS =
1092 ('left-aligned-cell-1', 'FastBack',
1093 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1094 'right-aligned-cell-1', 'FastForward',
1096 'left-aligned-cell-2', 'Back',
1097 'center-aligned-cell-1', 'Up',
1098 'right-aligned-cell-2', 'Forward'
1101 @Texi2HTML::Config::NODE_FOOTER_BUTTONS =
1102 ('left-aligned-cell-1', 'FastBack',
1103 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1104 'right-aligned-cell-1', 'FastForward',
1106 'left-aligned-cell-2', 'Back',
1107 'center-aligned-cell-1', 'Up',
1108 'right-aligned-cell-2', 'Forward'
1115 #############################################################################
1116 ### FOOTNOTE FORMATTING
1117 #############################################################################
1119 # Format footnotes in a nicer way: Instead of printing the number in a separate
1120 # (nr) heading line, use the standard way of prepending <sup>nr</sup> immediately
1121 # before the fn text.
1124 # The following code is copied from texi2html's examples/makeinfo.init and
1125 # should be updated when texi2html makes some changes there!
1127 my $makekinfo_like_footnote_absolute_number = 0;
1129 sub makeinfo_like_foot_line_and_ref($$$$$$$$)
1131 my $foot_num = shift;
1132 my $relative_num = shift;
1135 my $from_file = shift;
1136 my $footnote_file = shift;
1140 $makekinfo_like_footnote_absolute_number++;
1142 # this is a bit obscure, this allows to add an anchor only if formatted
1143 # as part of the document.
1144 $docid = '' if ($state->{'outside_document'} or $state->{'multiple_pass'});
1146 if ($from_file eq $footnote_file)
1148 $from_file = $footnote_file = '';
1151 my $foot_anchor = "<sup>" . &$anchor($docid, "$footnote_file#$footid", $relative_num) . "</sup>";
1152 $foot_anchor = &$anchor($docid, "$footnote_file#$footid", "($relative_num)") if ($state->{'preformatted'});
1154 # unshift @$lines, "<li>";
1155 # push @$lines, "</li>\n";
1156 return ($lines, $foot_anchor);
1159 sub makeinfo_like_foot_lines($)
1162 unshift @$lines, "<hr>\n<h4>$Texi2HTML::I18n::WORDS->{'Footnotes_Title'}</h4>\n";
1163 #<ol type=\"1\">\n";
1164 # push @$lines, "</ol>";
1168 my %makekinfo_like_paragraph_in_footnote_nr;
1170 sub makeinfo_like_paragraph ($$$$$$$$$$$$$)
1175 my $paragraph_command = shift;
1176 my $paragraph_command_formatted = shift;
1177 my $paragraph_number = shift;
1179 my $item_nr = shift;
1180 my $enumerate_style = shift;
1182 my $command_stack_at_end = shift;
1183 my $command_stack_at_begin = shift;
1185 #print STDERR "format: $format\n" if (defined($format));
1186 #print STDERR "paragraph @$command_stack_at_end; @$command_stack_at_begin\n";
1187 $paragraph_command_formatted = '' if (!defined($paragraph_command_formatted) or
1188 exists($special_list_commands{$format}->{$paragraph_command}));
1189 return '' if ($text =~ /^\s*$/);
1190 foreach my $style(t2h_collect_styles($command_stack_at_begin))
1192 $text = t2h_begin_style($style, $text);
1194 foreach my $style(t2h_collect_styles($command_stack_at_end))
1196 $text = t2h_end_style($style, $text);
1198 if (defined($paragraph_number) and defined($$paragraph_number))
1200 $$paragraph_number++;
1201 return $text if (($format eq 'itemize' or $format eq 'enumerate') and
1202 ($$paragraph_number == 1));
1207 $open .= " align=\"$paragraph_style{$align}\"";
1209 my $footnote_text = '';
1210 if (defined($command_stack_at_begin->[0]) and $command_stack_at_begin->[0] eq 'footnote')
1212 my $state = $Texi2HTML::THISDOC{'state'};
1213 $makekinfo_like_paragraph_in_footnote_nr{$makekinfo_like_footnote_absolute_number}++;
1214 if ($makekinfo_like_paragraph_in_footnote_nr{$makekinfo_like_footnote_absolute_number} <= 1)
1216 $open.=' class="footnote"';
1217 my $document_file = $state->{'footnote_document_file'};
1218 if ($document_file eq $state->{'footnote_footnote_file'})
1220 $document_file = '';
1222 my $docid = $state->{'footnote_place_id'};
1223 my $doc_state = $state->{'footnote_document_state'};
1224 $docid = '' if ($doc_state->{'outside_document'} or $doc_state->{'multiple_pass'});
1225 my $foot_label = &$anchor($state->{'footnote_footnote_id'},
1226 $document_file . "#$state->{'footnote_place_id'}",
1227 "$state->{'footnote_number_in_page'}");
1228 $footnote_text = "<small>[${foot_label}]</small> ";
1231 return $open.'>'.$footnote_text.$text.'</p>';
1235 #############################################################################
1237 #############################################################################
1239 # For split pages, use index.html as start page!
1240 if ($Texi2HTML::Config::SPLIT eq 'section') {
1241 $Texi2HTML::Config::TOP_FILE = 'index.html';