2 # -*- coding: utf-8; -*-
4 ### WTF, perl cannot read a file?
5 ###use File::Slurp qw( read_file );
7 ### texi2html customization script for LilyPond
8 ### Author: Reinhold Kainhofer <reinhold@kainhofer.com>, 2008.
9 ### Some code parts copied from texi2html and adapted. These functions
10 ### were written mainly by Patrice Dumas
14 ### Features implemented here:
15 ### -) For split manuals, the main page is index.html.
16 ### -) All @unnumbered* sections are placed into the same file
17 ### (implemented by split_at_numbered_sections)
18 ### -) Use our custom CSS file, with IE-specific fixes in another CSS file,
19 ### impelmented by lilypond_css_lines
20 ### -) TOC (folded, with the current page highlighted) in an overflown <div>
21 ### is added to every page; implemented by:
22 ### lilypond_print_element_header -- building of the TOC
23 ### lilypond_toc_body -- generation of customized TOC output
24 ### lilypond_print_page_head -- start <div id="main">
25 ### print_lilypond_page_foot -- closing id=main, output of footer & TOC
26 ### -) External refs are formatted only as "Text of the node" (not as >>see
27 ### "NODE" section "SECTION" in "BOOK"<< like with default texi2html). Also,
28 ### the leading "(book-name)" is removed.
29 ### Implemented by overriding lilypond_external_ref
30 ### -) Navigation bars on top/bottom of the page and between sections are not
31 ### left-aligned, but use a combination of left/center/right aligned table
32 ### cells; For this, I heavily extend the texi2html code to allow for
33 ### differently aligned cells and for multi-line tables);
34 ### Implemented in lilypond_print_navigation
35 ### -) Different formatting than the default: example uses the same formatting
37 ### -) Allow translated section titles: All section titles can be translated,
38 ### the original (English) title is associated with @translationof. This is
39 ### needed, because the file name / anchor is generated from the original
40 ### English title, since otherwise language-autoselection would break with
42 ### Since it is then no longer possible to obtain the file name from the
43 ### section title, I keep a sectionname<=>filename/anchor around. This way,
44 ### xrefs from other manuals can simply load that map and retrieve the
45 ### correct file name for the link. Implemented in:
46 ### lilypond_unknown (handling of @translationof, in case
47 ### extract_texi_filenames.py messes up...)
48 ### lilypond_element_file_name (correct file name: use the map)
49 ### lilypond_element_target_name (correct anchor: use the map)
50 ### lilypond_init_map (read in the externally created map from disk)
51 ### lilypond_external_href (load the map for xrefs, use the correct
53 ### -) The HTML anchors for all sections are derived from the node name /
54 ### section title (pre-generated in the .xref-map file). Implemented by:
55 ### lilypond_element_target_name (adjust section anchors)
56 ### -) Use the standard footnote format "<sup>nr</sup> text" instead of the
57 ### ugly format of texi2html (<h3>(nr)</h3><p>text</p>). Implemented in
58 ### makeinfo_like_foot_line_and_ref
59 ### makeinfo_like_foot_lines
60 ### makeinfo_like_paragraph
63 ### Useful helper functions:
64 ### -) texinfo_file_name($node_name): returns a texinfo-compatible file name
65 ### for the given string $node_name (whitespace trimmed/replaced by -,
66 ### non-standard chars replaced by _xxxx (ascii char code) and forced to
67 ### start with a letter by prepending t_g if necessary)
70 package Texi2HTML::Config;
72 #############################################################################
74 #############################################################################
77 my $LY_LANGUAGES = {};
78 $LY_LANGUAGES->{'fr'} = {
79 'Back to Documentation Index' => 'Retour à l\'accueil de la documentation',
81 $LY_LANGUAGES->{'es'} = {
82 'Back to Documentation Index' => 'Volver al índice de la documentación',
84 $LY_LANGUAGES->{'de'} = {
85 'Back to Documentation Index' => 'Zur Dokumentationsübersicht',
87 $LY_LANGUAGES->{'ja'} = {
88 'Back to Documentation Index' => 'ドキュメント インデックスに戻る',
92 sub ly_get_string () {
93 my $lang = $Texi2HTML::THISDOC{current_lang};
95 if ($lang and $lang ne "en" and $LY_LANGUAGES->{$lang}->{$string}) {
96 return $LY_LANGUAGES->{$lang}->{$string};
103 #############################################################################
104 ### SETTINGS FOR TEXI2HTML
105 #############################################################################
107 # Validation fix for texi2html<=1.82
108 $Texi2HTML::Config::DOCTYPE = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
110 @Texi2HTML::Config::CSS_REFS = (
111 {FILENAME => "lilypond-web.css", TITLE => "Default style"},
114 @Texi2HTML::Config::ALT_CSS_REFS = (
115 {FILENAME => "lilypond-web-alt1.css", TITLE => "Alternate style 1"},
116 {FILENAME => "lilypond-web-alt2.css", TITLE => "Alternate style 2"},
119 $Texi2HTML::Config::USE_ACCESSKEY = 1;
120 $Texi2HTML::Config::USE_LINKS = 1;
121 $Texi2HTML::Config::USE_REL_REV = 1;
122 $Texi2HTML::Config::SPLIT_INDEX = 0;
123 $Texi2HTML::Config::SEPARATED_FOOTNOTES = 0; # Print footnotes on same page, not separated
124 # FIXME: creates duplicate anchors, which causes Opera to barf;
125 # should be fixed in lilypond-texi2html.init too
126 # Uhm, what about a bug report to Opera? We need sane names here.
127 if ($Texi2HTML::Config::SPLIT eq 'section') {
128 $Texi2HTML::Config::element_file_name = \&lilypond_element_file_name;
130 $Texi2HTML::Config::element_target_name = \&lilypond_element_target_name;
131 $default_print_element_header = $Texi2HTML::Config::print_element_header;
132 $Texi2HTML::Config::print_element_header = \&lilypond_print_element_header;
133 $Texi2HTML::Config::print_page_foot = \&print_lilypond_page_foot;
134 $Texi2HTML::Config::print_navigation = \&lilypond_print_navigation;
135 $Texi2HTML::Config::external_ref = \&lilypond_external_ref;
136 $default_external_href = $Texi2HTML::Config::external_href;
137 $Texi2HTML::Config::external_href = \&lilypond_external_href;
138 $default_toc_body = $Texi2HTML::Config::toc_body;
139 $Texi2HTML::Config::toc_body = \&lilypond_toc_body;
140 $Texi2HTML::Config::css_lines = \&lilypond_css_lines;
141 $default_unknown = $Texi2HTML::Config::unknown;
142 $Texi2HTML::Config::unknown = \&lilypond_unknown;
143 $default_print_page_head = $Texi2HTML::Config::print_page_head;
144 $Texi2HTML::Config::print_page_head = \&lilypond_print_page_head;
145 # $Texi2HTML::Config::foot_line_and_ref = \&lilypond_foot_line_and_ref;
146 $Texi2HTML::Config::foot_line_and_ref = \&makeinfo_like_foot_line_and_ref;
147 $Texi2HTML::Config::foot_lines = \&makeinfo_like_foot_lines;
148 $Texi2HTML::Config::paragraph = \&makeinfo_like_paragraph;
152 # Examples should be formatted similar to quotes:
153 $Texi2HTML::Config::complex_format_map->{'example'} = {
154 'begin' => q{"<blockquote>"},
155 'end' => q{"</blockquote>\n"},
159 %Texi2HTML::config::misc_pages_targets = (
160 'Overview' => 'Overview',
161 'Contents' => 'Contents',
166 my @section_to_filename;
171 #############################################################################
173 #############################################################################
176 $Data::Dumper::Maxdepth = 2;
178 sub print_element_info($)
181 print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
182 print "Element: $element\n";
183 print Dumper($element);
190 #############################################################################
192 #############################################################################
194 # Convert a given node name to its proper file name (normalization as explained
195 # in the texinfo manual:
196 # http://www.gnu.org/software/texinfo/manual/texinfo/html_node/HTML-Xref-Node-Name-Expansion.html
197 sub texinfo_file_name($)
201 # File name normalization by texinfo:
202 # 1/2: letters and numbers are left unchanged
203 # 3/4: multiple, leading and trailing whitespace is removed
204 $text = main::normalise_space($text);
205 # 5/6: all remaining spaces are converted to '-', all other 7- or 8-bit
206 # chars are replaced by _xxxx (xxxx=ascii character code)
207 while ($text ne '') {
208 if ($text =~ s/^([A-Za-z0-9]+)//o) { # number or letter stay unchanged
210 } elsif ($text =~ s/^ //o) { # space -> '-'
212 } elsif ($text =~ s/^(.)//o) { # Otherwise use _xxxx (ascii char code)
214 if ( $ccode <= 0xFFFF ) {
215 $result .= sprintf("_%04x", $ccode);
217 $result .= sprintf("__%06x", $ccode);
221 # 7: if name does not begin with a letter, prepend 't_g' (so it starts with a letter)
222 if ($result !~ /^[a-zA-Z]/) {
223 $result = 't_g' . $result;
230 # Load a file containing a nodename<=>filename map (tab-sepatared, i.e.
231 # NODENAME\tFILENAME\tANCHOR
232 # Returns a ref to a hash "Node title" => ["FilenameWithoutExt", "Anchor"]
233 sub load_map_file ($)
238 if (open(XREFFILE, $mapfile)) {
239 print STDERR "Reading map file: $mapfile\n";
241 while ( $line = <XREFFILE> ) {
242 # parse the tab-separated entries and insert them into the map:
244 my @entries = split(/\t/, $line);
245 if (scalar (@entries) == 3) {
246 $node_map->{$entries[0]} = [$entries[1], $entries[2]];
248 print STDERR "Invalid entry in the node file $mapfile: $line\n";
253 print STDERR "warning: cannot load map file: no such file: $mapfile\n";
259 # Split the given path into dir and basename (with .texi removed). Used mainly
260 # to get the path/basename of the original texi input file
261 sub split_texi_filename ($)
264 my ($docu_dir, $docu_name);
265 if ($docu =~ /(.*\/)/) {
266 chop($docu_dir = $1);
268 $docu_name =~ s/.*\///;
273 $docu_name =~ s/\.te?x(i|info)?$//;
274 return ($docu_dir, $docu_name);
281 #############################################################################
283 #############################################################################
285 # Include our standard CSS file, not hard-coded CSS code directly in the HTML!
286 # For IE, conditionally include the lilypond-ie-fixes.css style sheet
287 sub lilypond_css_lines ($$)
289 my $import_lines = shift;
290 my $rule_lines = shift;
291 return if (defined($Texi2HTML::THISDOC{'CSS_LINES'}));
292 if (@$rule_lines or @$import_lines)
294 $Texi2HTML::THISDOC{'CSS_LINES'} = "<style type=\"text/css\">\n<!--\n";
295 $Texi2HTML::THISDOC{'CSS_LINES'} .= join('',@$import_lines) . "\n" if (@$import_lines);
296 $Texi2HTML::THISDOC{'CSS_LINES'} .= join('',@$rule_lines) . "\n" if (@$rule_lines);
297 $Texi2HTML::THISDOC{'CSS_LINES'} .= "-->\n</style>\n";
299 foreach my $ref (@CSS_REFS)
301 $Texi2HTML::THISDOC{'CSS_LINES'} .= "<link rel=\"stylesheet\" type=\"text/css\" title=\"$ref->{TITLE}\" href=\"$ref->{FILENAME}\">\n";
304 foreach my $ref (@Texi2HTML::Config::ALT_CSS_REFS)
306 $Texi2HTML::THISDOC{'CSS_LINES'} .= "<link rel=\"alternate stylesheet\" type=\"text/css\" href=\"$ref->{FILENAME}\" title=\"$ref->{TITLE}\">\n";
309 # GOP tweak: We aren't using an IE-specific stylesheet
310 #$Texi2HTML::THISDOC{'CSS_LINES'} .= "<!--[if lte IE 7]>\n<link href=\"lilypond-ie-fixes.css\" rel=\"stylesheet\" type=\"text/css\">\n<![endif]-->\n";
317 #############################################################################
318 ### SPLITTING BASED ON NUMBERED SECTIONS
319 #############################################################################
321 # FIXME: removed for GOP.
323 # Uhm, yes: that's what I found. It was gone. No need to add that
324 # in a comment, DIFF tells me that it's gone.
326 # Comments can be used to tell *why* something has been removed -- as
327 # it seems to me that we *need* this in order to get sane,
328 # untranslated, html file names? Putting it back for this reason.
332 my $node_to_filename_map = ();
335 # This function makes sure that files are only generated for numbered sections,
336 # but not for unnumbered ones. It is called after texi2html has done its own
337 # splitting and simply returns the filename for the node given as first argument
338 # Nodes with the same filename will be printed out to the same filename, so
339 # this really all we need. Also, make sure that the file names for sections
340 # are derived from the section title. We also might want to name the anchors
341 # according to node titles, which works by simply overriding the id element of
343 # If an external nodename<=>filename/anchor map file is found (loaded in
344 # the command handler, use the externally created values, otherwise use the
346 sub lilypond_element_file_name($$$)
350 my $docu_name = shift;
351 my $docu_ext = $Texi2HTML::Config::EXTENSION;
353 my $node_name = main::remove_texi($element->{'node_ref'}->{'texi'});
354 # the snippets page does not use nodes for the snippets, so in this case
355 # we'll have to use the section name!
356 if ($node_name eq '') {
357 $node_name = main::remove_texi($element->{'texi'});
360 # If we have an entry in the section<=>filename map, use that one, otherwise
361 # generate the filename/anchor here. In the latter case, external manuals
362 # will not be able to retrieve the file name for xrefs!!! Still, I already
363 # had that code, so I'll leave it in in case something goes wrong with the
364 # extract_texi_filenames.py script in the lilypond build process!
365 if (exists ($node_to_filename_map->{$node_name})) {
366 (my $filename, my $anchor) = @{$node_to_filename_map->{$node_name}};
367 $filename .= ".$docu_ext" if (defined($docu_ext));
369 # unnumbered sections (except those at top-level!) always go to the same
370 # file as the previous numbered section
371 if (not ($element->{number}) and not ($lastfilename eq '') and ($element->{level} > 1)) {
372 $filename = $lastfilename;
374 if (($filename eq $lastfilename)) {
375 $$element{doc_nr} = $docnr;
378 $$element{doc_nr} = $docnr;
379 $lastfilename = $filename;
383 } elsif ($type eq "top" or $type eq "toc" or $type eq "doc" or $type eq "stoc" or $type eq "foot" or $type eq "about") {
386 print STDERR "WARNING: Node '$node_name' was NOT found in the map\n"
387 unless ($node_name eq '') or ($element->{'tag'} eq 'unnumberedsec')
388 or ($node_name =~ /NOT REALLY USED/);
390 # Numbered sections will get a filename Node_title, unnumbered sections will use
391 # the file name of the previous numbered section:
392 if (($element->{number}) or ($lastfilename eq '') or ($element->{level} == 1)) {
393 # normalize to the same file name as texinfo
394 if ($element->{translationof}) {
395 $node_name = main::remove_texi($element->{translationof});
397 my $filename = texinfo_file_name($node_name);
398 $filename .= ".$docu_ext" if (defined($docu_ext));
400 $$element{doc_nr} = $docnr;
401 $lastfilename = $filename;
404 $$element{doc_nr} = $docnr;
405 return $lastfilename;
412 sub lilypond_element_target_name($$$)
417 # Target is based on node name (or sec name for secs without node attached)
418 my $node_name = main::remove_texi($element->{'node_ref'}->{'texi'});
419 if ($node_name eq '') {
420 $node_name = main::remove_texi($element->{'texi'});
423 # If we have an entry in the section<=>filename map, use that one, otherwise
424 # generate the anchor here.
425 if (exists ($node_to_filename_map->{$node_name})) {
426 (my $filename, $target) = @{$node_to_filename_map->{$node_name}};
428 my $anchor = $node_name;
429 if ($element->{translationof}) {
430 $anchor = main::remove_texi($element->{translationof});
432 # normalize to the same file name as texinfo
433 $target = texinfo_file_name($anchor);
435 # TODO: Once texi2html correctly prints out the target and not the id for
436 # the sections, change this back to ($id, $target)
437 return ($target, $target);
441 ## Load the map file for the corrently processed texi file. We do this
442 # using a command init handler, since texi2html does not have any
443 # other hooks that are called after THISDOC is filled but before phase 2
444 # of the texi2html conversion.
445 sub lilypond_init_map ()
447 my ($docu_dir, $docu_name) = split_texi_filename ($Texi2HTML::THISDOC{'input_file_name'});
448 my $map_filename = main::locate_include_file ("${docu_name}.$Texi2HTML::THISDOC{current_lang}.xref-map")
449 || main::locate_include_file ("${docu_name}.xref-map");
450 $node_to_filename_map = load_map_file ($map_filename);
452 push @Texi2HTML::Config::command_handler_init, \&lilypond_init_map;
455 #############################################################################
456 ### CLEANER LINK TITLE FOR EXTERNAL REFS
457 #############################################################################
459 # The default formatting of external refs returns e.g.
460 # "(lilypond-internals)Timing_translator", so we remove all (...) from the
461 # file_and_node argument. Also, we want only a very simple format, so we don't
462 # even call the default handler!
463 sub lilypond_external_ref($$$$$$)
468 my $file_node = shift;
470 my $cross_ref = shift;
472 my $displaytext = '';
474 # 1) if we have a cross ref name, that's the text to be displayed:
475 # 2) For the top node, use the (printable) name of the manual, unless we
476 # have an explicit cross ref name
477 # 3) In all other cases use the section name
478 if ($cross_ref ne '') {
479 $displaytext = $cross_ref;
480 } elsif (($section eq '') or ($section eq 'Top')) {
481 $displaytext = $book;
483 $displaytext = $section;
486 $displaytext = &$anchor('', $href, $displaytext) if ($displaytext ne '');
487 return &$I('%{node_file_href}', { 'node_file_href' => $displaytext });
494 #############################################################################
495 ### HANDLING TRANSLATED SECTIONS: handle @translationof, secname<->filename
496 ### map stored on disk, xrefs in other manuals load that map
497 #############################################################################
500 # Try to make use of @translationof to generate files according to the original
501 # English section title...
502 sub lilypond_unknown($$$$$)
510 # the @translationof macro provides the original English section title,
511 # which should be used for file/anchor naming, while the title will be
512 # translated to each language
513 # It is already used by extract_texi_filenames.py, so this should not be
514 # necessary here at all. Still, I'll leave the code in just in case the
515 # python script messed up ;-)
516 if ($pass == 1 and $macro eq "translationof") {
517 if (ref($state->{'element'}) eq 'HASH') {
518 $state->{'element'}->{'translationof'} = main::normalise_space($line);
520 return ('', 1, undef, undef);
522 return &$default_unknown($macro, $line, $pass, $stack, $state);
529 my %translated_books = ();
530 # Construct a href to an external source of information.
531 # node is the node with texinfo @-commands
532 # node_id is the node transliterated and transformed as explained in the
534 # node_xhtml_id is the node transformed such that it is unique and can
535 # be used to make an html cross ref as explained in the texinfo manual
536 # file is the file in '(file)node'
537 sub lilypond_external_href($$$)
541 my $node_hxmlt_id = shift;
544 # 1) Keep a hash of book->section_map
545 # 2) if not file in keys hash => try to load the map (assign empty map if
546 # non-existent => will load only once!)
547 # 3) if node in the section=>(file, anchor) map, replace node_id and
548 # node_xhtml_id by the map's values
549 # 4) call the default_external_href with these values (or the old ones if not found)
551 if (($node_id ne '') and defined($file) and ($node_id ne 'Top')) {
552 my $map_name = $file;
553 $map_name =~ s/-big-page//;
555 # Load the map if we haven't done so already
556 if (!exists($translated_books{$map_name})) {
557 my ($docu_dir, $docu_name) = split_texi_filename ($Texi2HTML::THISDOC{'input_file_name'});
558 my $map_filename = main::locate_include_file ("${map_name}.$Texi2HTML::THISDOC{current_lang}.xref-map")
559 || main::locate_include_file ("${map_name}.xref-map");
560 $translated_books{$map_name} = load_map_file ($map_filename);
563 # look up translation. use these values instead of the old filename/anchor
564 my $section_name_map = $translated_books{$map_name};
565 my $node_text = main::remove_texi($node);
566 if (defined($section_name_map->{$node_text})) {
567 ($node_id, $node_hxmlt_id) = @{$section_name_map->{$node_text}};
569 print STDERR "WARNING: Unable to find node '$node_text' in book $map_name.\n";
574 return &$default_external_href($node, $node_id, $node_hxmlt_id, $file);
576 return &$default_external_href($node, $node_id, $node_hxmlt_id);
584 #############################################################################
585 ### CUSTOM TOC FOR EACH PAGE (in a frame on the left)
586 #############################################################################
588 my $page_toc_depth = 2;
589 my @default_toc = [];
591 # Initialize the toc_depth to 1 if the command-line option -D=short_toc is given
592 sub lilypond_init_toc_depth ()
594 if (exists($main::value{'short_toc'}) and not exists($main::value{'bigpage'})) {
598 # Set the TOC-depth (depending on a texinfo variable short_toc) in a
599 # command-handler, so we have them available when creating the pages
600 push @Texi2HTML::Config::command_handler_process, \&lilypond_init_toc_depth;
603 # recursively generate the TOC entries for the element and its children (which
604 # are only shown up to maxlevel. All ancestors of the current element are also
605 # shown with their immediate children, irrespective of their level.
606 # Unnumbered entries are only printed out if they are at top-level or 2nd level
607 # or their parent element is an ancestor of the currently viewed node.
608 # The conditions to call this method to print the entry for a child node is:
609 # -) the parent is an ancestor of the current page node
610 # -) the parent is a numbered element at top-level toplevel (i.e. show numbered
611 # and unnumbered 2nd-level children of numbered nodes)
612 # -) the child element is a numbered node below level maxlevel
613 sub generate_ly_toc_entries($$$)
616 my $element_path = shift;
618 #my $maxlevel = shift;
620 # Skip undefined sections, plus all sections generated by index splitting
621 return() if (not defined($element) or exists($element->{'index_page'}));
623 my $level = $element->{'toc_level'};
624 my $is_parent_of_current = $element->{'id'} && $element_path->{$element->{'id'}};
625 my $ind = ' ' x $level;
626 # GDP hack: leave this open for color
627 my $this_css_class = " class=\"";
628 $this_css_class .= $is_parent_of_current ? " toc_current" : "";
630 # GDP tweak: if the node name is in the color_X list
631 # TODO: use a hash, load from file?
632 # FIXME: load from file, to allow for translations!
636 "Learning", "Glossary", "Essay",
637 "Contact", "Tiny examples", "Bug reports"
640 "Features", "Examples", "Freedom", "Background",
641 "Unix", "MacOS X", "Windows",
642 "Notation", "Usage", "Snippets",
643 "Help us", "Development", "Authors"
646 "Productions", "Testimonials",
647 "Source", "Old downloads",
648 "FAQ", "Changes", "Internals",
649 "Publications", "Old news"
652 "Text input", "Alternate input",
654 "Translated", "All", "FDL"
657 my $addColor = " colorDefault";
658 foreach $color (@color_1) {
659 if ($element->{'text'} eq $color) {
660 $addColor = " color1";
663 foreach $color (@color_2) {
664 if ($element->{'text'} eq $color) {
665 $addColor = " color2";
668 foreach $color (@color_3) {
669 if ($element->{'text'} eq $color) {
670 $addColor = " color3";
673 foreach $color (@color_4) {
674 if ($element->{'text'} eq $color) {
675 $addColor = " color4";
679 $this_css_class .= $addColor . "\"";
684 my $entry = "$ind<li$this_css_class>" . &$anchor ($element->{'tocid'}, "$element->{'file'}#$element->{'target'}",$element->{'text'});
686 push (@result, $entry);
687 my $children = $element->{'section_childs'};
688 if (defined($children) and (ref($children) eq "ARRAY")) {
689 my $force_children = $is_parent_of_current or ($level == 1 and $element->{'number'});
690 my @child_result = ();
691 foreach my $c (@$children) {
692 my $is_numbered_child = defined ($c->{'number'});
693 my $below_maxlevel = $c->{'toc_level'} le $maxlevel;
694 if ($force_children or ($is_numbered_child and $below_maxlevel)) {
695 my @child_res = generate_ly_toc_entries($c, $element_path, $maxlevel);
696 push (@child_result, @child_res);
699 # if no child nodes were generated, e.g. for the index, where expanded pages
700 # are ignored, don't generate a list at all...
702 push (@result, "\n$ind<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
704 # GOP tweak: (next 2 lines; alternates)
705 push (@result, "$ind<li$this_css_class>" . &$anchor ($element->{'tocid'}, "$element->{'file'}#$element->{'target'}","(main)"));
706 #push (@result, "$ind<li$this_css_class>" . &$anchor ($element->{'tocid'}, "$element->{'file'}#$element->{'target'}","(".$element->{'text'}." main)"));
707 push (@result, @child_result);
708 push (@result, "$ind</ul>\n");
711 push (@result, "$ind</li>\n");
716 # Print a customized TOC, containing only the first two levels plus the whole
717 # path to the current page
718 sub lilypond_generate_page_toc_body($)
721 my $current_element = $element;
723 $parentelements{$element->{'id'}} = 1;
724 # Find the path to the current element
725 while ( defined($current_element->{'sectionup'}) and
726 ($current_element->{'sectionup'} ne $current_element) )
728 $parentelements{$current_element->{'sectionup'}->{'id'}} = 1
729 if ($current_element->{'sectionup'}->{'id'} ne '');
730 $current_element = $current_element->{'sectionup'};
731 if (exists($main::value{'shallow_toc'})) {
735 return () if not defined($current_element);
736 # Create the toc entries recursively
738 my @toc_entries = ("<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
739 # my @toc_entries = ("<div class=\"contents\">\n", "<ul$NO_BULLET_LIST_ATTRIBUTE>\n");
741 # FIXME: add link to main page, really hackily.
742 if ($element->{'sectionup'}) {
743 # it's not the top element
744 push (@toc_entries, "<li><a href=\"index.html\">Main</a></li>\n");
746 push (@toc_entries, "<li class=\"toc_current\"><a href=\"index.html\">Main</a></li>\n");
748 my $children = $current_element->{'section_childs'};
749 # FIXME: generate toc
750 foreach ( @$children ) {
751 push (@toc_entries, generate_ly_toc_entries($_, \%parentelements, $page_toc_depth));
753 if (!exists($main::value{'shallow_toc'})) {
754 # WTF, perl needs 6 lines of magic to do: ' ' + open ('file-name').read ()?
756 my $name = 'search-box.html';
757 open FILE, $name or open FILE, "../$name" or die die "no such file: $name: $!";
759 # All these also seems to work, but fail silently. Great, it runs!
760 # It's late already, let's this broken site.
762 # push (@toc_entries, '<li>\n' + <FILE> + '</li>\n');
763 # push (@toc_entries, '<li>\n' . <FILE> . '</li>\n');
764 # my $string = '<li>\n' + <FILE> + '</li>\n';
765 # my $string = '<li>\n' + <FILE> + '</li>\n';
766 # my $string = '<li>\n' . <FILE> . '</li>\n';
768 # $string = '<li>\n' + $string + '</li>\n';
769 $string = "<li>\n" . $string . "</li>\n";
770 push (@toc_entries, $string);
773 push (@toc_entries, "</ul>\n");
775 # push (@toc_entries, "</div>\n");
776 push (@toc_entries, "\n");
780 sub lilypond_print_toc_div ($$)
784 my @lines = @$tocref;
785 # use default TOC if no custom lines have been generated
786 @lines = @default_toc if (not @lines);
789 print $fh "\n\n<div id=\"tocframe\">\n";
790 #print $fh "<div class=\"contents\">\n";
792 # Remove the leading "GNU LilyPond --- " from the manual title
793 my $topname = $Texi2HTML::NAME{'Top'};
794 $topname =~ s/^GNU LilyPond(:| &[mn]dash;) //;
796 # construct the top-level Docs index (relative path and including language!)
797 my $lang = $Texi2HTML::THISDOC{current_lang};
798 if ($lang and $lang ne "en") {
804 $reldir = "../" if ($Texi2HTML::Config::SPLIT eq 'section');
805 my $uplink = $reldir."index.${lang}html";
807 # print $fh "<p class=\"toc_uplink\"><a href=\"$uplink\"
808 # title=\"Documentation Index\"><< " .
809 # &ly_get_string ('Back to Documentation Index') .
812 # print $fh '<h4 class="toc_header"> ' . &$anchor('',
813 # $Texi2HTML::HREF{'Top'},
815 # 'title="Start of the manual"'
819 foreach my $line (@lines) {
823 print $fh "</div>\n\n";
827 # Create the custom TOC for this page (partially folded, current page is
828 # highlighted) and store it in a global variable. The TOC is written out after
829 # the html contents (but positioned correctly using CSS), so that browsers with
830 # css turned off still show the contents first.
831 our @this_page_toc = ();
832 sub lilypond_print_element_header
834 my $first_in_page = shift;
835 my $previous_is_top = shift;
836 if ($first_in_page and not @this_page_toc) {
837 if (defined($Texi2HTML::THIS_ELEMENT)) {
838 # Create the TOC for this page
839 @this_page_toc = lilypond_generate_page_toc_body($Texi2HTML::THIS_ELEMENT);
842 return &$default_print_element_header( $first_in_page, $previous_is_top);
845 # Generate the HTML output for the TOC
846 sub lilypond_toc_body($)
848 my $elements_list = shift;
849 # Generate a default TOC for pages without THIS_ELEMENT
850 @default_toc = lilypond_generate_page_toc_body(@$elements_list[0]);
851 return &$default_toc_body($elements_list);
854 # Print out the TOC in a <div> at the beginning of the page
855 sub lilypond_print_page_head($)
858 &$default_print_page_head($fh);
859 print $fh "<div id=\"main\">\n";
862 # Print out the TOC in a <div>, which will be formatted as a
863 # sidebar mimicking a TOC frame
864 sub print_lilypond_page_foot($)
867 my $program_string = &$program_string();
868 # print $fh "<p><font size='-1'>$program_string</font><br>$PRE_BODY_CLOSE</p>\n";
870 # Do not include language selection in div#main
871 print $fh "<!-- end div#main here -->\n</div>\n\n";
874 print $fh "<!-- FOOTER -->\n\n";
875 print $fh "<div id=\"footer\">\n";
876 print $fh "<div id=\"language\">\n";
877 print $fh "<h3>Other languages</h3>\n";
878 print $fh "<p><a href=\"\">Deutsch</a>, Español, Français, Magyar.</p>\n";
879 print $fh "<p>About automatic language selection.</p>\n";
880 print $fh "</div>\n";
883 # FIXME: This div and p#languages need to be in div#footer.
884 # Should we move this div to postprocess_html.py ?
885 print $fh "<div id=\"verifier_texinfo\">\n";
886 print $fh "<h3>Validation</h3>\n";
887 print $fh "<p>Thanks to <a href=\"http://www.webdev.nl/\">webdev.nl</a>";
888 print $fh " for hosting <code>lilypond.org</code>.\n";
889 print $fh "<a href=\"http://validator.w3.org/check?uri=referer\">\n";
890 print $fh "<img src=\"http://www.w3.org/Icons/valid-html401\"\n";
891 print $fh " alt=\"Valid HTML 4.01 Transitional\"\n";
892 print $fh " height=\"31\" width=\"88\"></a></p>\n";
895 # Print the TOC frame and reset the TOC:
896 lilypond_print_toc_div ($fh, \@this_page_toc);
900 print $fh "</body>\n</html>\n";
907 #############################################################################
908 ### NICER / MORE FLEXIBLE NAVIGATION PANELS
909 #############################################################################
911 sub get_navigation_text
914 my $text = $NAVIGATION_TEXT{$button};
915 if ( ($button eq 'Back') or ($button eq 'FastBack') ) {
916 $text = $text . $Texi2HTML::NODE{$button} . " ";
917 } elsif ( ($button eq 'Forward') or ($button eq 'FastForward') ) {
918 $text = " " . $Texi2HTML::NODE{$button} . $text;
919 } elsif ( $button eq 'Up' ) {
920 $text = " ".$text.": " . $Texi2HTML::NODE{$button} . " ";
926 # Don't automatically create left-aligned table cells for every link, but
927 # instead create a <td> only on an appropriate '(left|right|center)-aligned-cell-n'
928 # button text. It's alignment as well as the colspan will be taken from the
929 # name of the button. Also, add 'newline' button text to create a new table
930 # row. The texts of the buttons are generated by get_navigation_text and
931 # will contain the name of the next/previous section/chapter.
932 sub lilypond_print_navigation
935 my $vertical = shift;
937 my $result = "<table class=\"nav_table\">\n";
939 $result .= "<tr>" unless $vertical;
941 foreach my $button (@$buttons)
943 $result .= qq{<tr valign="top" align="left">\n} if $vertical;
944 # Allow (left|right|center)-aligned-cell and newline as buttons!
945 if ( $button =~ /^(.*)-aligned-cell-(.*)$/ )
947 $result .= qq{</td>} unless $beginofline;
948 $result .= qq{<td valign="middle" align="$1" colspan="$2">};
951 elsif ( $button eq 'newline' )
953 $result .= qq{</td>} unless $beginofline;
954 $result .= qq{</tr>};
959 elsif (ref($button) eq 'CODE')
961 $result .= &$button($vertical);
963 elsif (ref($button) eq 'SCALAR')
965 $result .= "$$button" if defined($$button);
967 elsif (ref($button) eq 'ARRAY')
969 my $text = $button->[1];
970 my $button_href = $button->[0];
971 # verify that $button_href is simple text and text is a reference
972 if (defined($button_href) and !ref($button_href)
973 and defined($text) and (ref($text) eq 'SCALAR') and defined($$text))
975 if ($Texi2HTML::HREF{$button_href})
977 my $anchor_attributes = '';
978 if ($USE_ACCESSKEY and (defined($BUTTONS_ACCESSKEY{$button_href})) and ($BUTTONS_ACCESSKEY{$button_href} ne ''))
980 $anchor_attributes = "accesskey=\"$BUTTONS_ACCESSKEY{$button_href}\"";
982 if ($USE_REL_REV and (defined($BUTTONS_REL{$button_href})) and ($BUTTONS_REL{$button_href} ne ''))
984 $anchor_attributes .= " rel=\"$BUTTONS_REL{$button_href}\"";
988 $Texi2HTML::HREF{$button_href},
989 get_navigation_text($$text),
995 $result .= get_navigation_text($$text);
999 elsif ($button eq ' ')
1000 { # handle space button
1002 ($ICONS && $ACTIVE_ICONS{' '}) ?
1003 &$button_icon_img($BUTTONS_NAME{$button}, $ACTIVE_ICONS{' '}) :
1004 $NAVIGATION_TEXT{' '};
1007 elsif ($Texi2HTML::HREF{$button})
1008 { # button is active
1009 my $btitle = $BUTTONS_GOTO{$button} ?
1010 'title="' . $BUTTONS_GOTO{$button} . '"' : '';
1011 if ($USE_ACCESSKEY and (defined($BUTTONS_ACCESSKEY{$button})) and ($BUTTONS_ACCESSKEY{$button} ne ''))
1013 $btitle .= " accesskey=\"$BUTTONS_ACCESSKEY{$button}\"";
1015 if ($USE_REL_REV and (defined($BUTTONS_REL{$button})) and ($BUTTONS_REL{$button} ne ''))
1017 $btitle .= " rel=\"$BUTTONS_REL{$button}\"";
1019 if ($ICONS && $ACTIVE_ICONS{$button})
1023 $Texi2HTML::HREF{$button},
1024 &$button_icon_img($BUTTONS_NAME{$button},
1025 $ACTIVE_ICONS{$button},
1026 $Texi2HTML::SIMPLE_TEXT{$button}),
1035 $Texi2HTML::HREF{$button},
1036 get_navigation_text($button),
1043 { # button is passive
1045 $ICONS && $PASSIVE_ICONS{$button} ?
1046 &$button_icon_img($BUTTONS_NAME{$button},
1047 $PASSIVE_ICONS{$button},
1048 $Texi2HTML::SIMPLE_TEXT{$button}) :
1050 "[" . get_navigation_text($button) . "]";
1052 $result .= "</td>\n" if $vertical;
1053 $result .= "</tr>\n" if $vertical;
1055 $result .= "</td>" unless $beginofline;
1056 $result .= "</tr>" unless $vertical;
1057 $result .= "</table>\n";
1063 @Texi2HTML::Config::SECTION_BUTTONS =
1064 ('left-aligned-cell-1', 'FastBack',
1065 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1066 'right-aligned-cell-1', 'FastForward',
1068 'left-aligned-cell-2', 'Back',
1069 'center-aligned-cell-1', 'Up',
1070 'right-aligned-cell-2', 'Forward'
1073 # buttons for misc stuff
1074 @Texi2HTML::Config::MISC_BUTTONS = ('center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About');
1076 # buttons for chapter file footers
1077 # (and headers but only if SECTION_NAVIGATION is false)
1078 @Texi2HTML::Config::CHAPTER_BUTTONS =
1079 ('left-aligned-cell-1', 'FastBack',
1080 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1081 'right-aligned-cell-1', 'FastForward',
1084 # buttons for section file footers
1085 @Texi2HTML::Config::SECTION_FOOTER_BUTTONS =
1086 ('left-aligned-cell-1', 'FastBack',
1087 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1088 'right-aligned-cell-1', 'FastForward',
1090 'left-aligned-cell-2', 'Back',
1091 'center-aligned-cell-1', 'Up',
1092 'right-aligned-cell-2', 'Forward'
1095 @Texi2HTML::Config::NODE_FOOTER_BUTTONS =
1096 ('left-aligned-cell-1', 'FastBack',
1097 'center-aligned-cell-3', 'Top', 'Contents', 'Index', 'About',
1098 'right-aligned-cell-1', 'FastForward',
1100 'left-aligned-cell-2', 'Back',
1101 'center-aligned-cell-1', 'Up',
1102 'right-aligned-cell-2', 'Forward'
1109 #############################################################################
1110 ### FOOTNOTE FORMATTING
1111 #############################################################################
1113 # Format footnotes in a nicer way: Instead of printing the number in a separate
1114 # (nr) heading line, use the standard way of prepending <sup>nr</sup> immediately
1115 # before the fn text.
1118 # The following code is copied from texi2html's examples/makeinfo.init and
1119 # should be updated when texi2html makes some changes there!
1121 my $makekinfo_like_footnote_absolute_number = 0;
1123 sub makeinfo_like_foot_line_and_ref($$$$$$$$)
1125 my $foot_num = shift;
1126 my $relative_num = shift;
1129 my $from_file = shift;
1130 my $footnote_file = shift;
1134 $makekinfo_like_footnote_absolute_number++;
1136 # this is a bit obscure, this allows to add an anchor only if formatted
1137 # as part of the document.
1138 $docid = '' if ($state->{'outside_document'} or $state->{'multiple_pass'});
1140 if ($from_file eq $footnote_file)
1142 $from_file = $footnote_file = '';
1145 my $foot_anchor = "<sup>" . &$anchor($docid, "$footnote_file#$footid", $relative_num) . "</sup>";
1146 $foot_anchor = &$anchor($docid, "$footnote_file#$footid", "($relative_num)") if ($state->{'preformatted'});
1148 # unshift @$lines, "<li>";
1149 # push @$lines, "</li>\n";
1150 return ($lines, $foot_anchor);
1153 sub makeinfo_like_foot_lines($)
1156 unshift @$lines, "<hr>\n<h4>$Texi2HTML::I18n::WORDS->{'Footnotes_Title'}</h4>\n";
1157 #<ol type=\"1\">\n";
1158 # push @$lines, "</ol>";
1162 my %makekinfo_like_paragraph_in_footnote_nr;
1164 sub makeinfo_like_paragraph ($$$$$$$$$$$$$)
1169 my $paragraph_command = shift;
1170 my $paragraph_command_formatted = shift;
1171 my $paragraph_number = shift;
1173 my $item_nr = shift;
1174 my $enumerate_style = shift;
1176 my $command_stack_at_end = shift;
1177 my $command_stack_at_begin = shift;
1179 #print STDERR "format: $format\n" if (defined($format));
1180 #print STDERR "paragraph @$command_stack_at_end; @$command_stack_at_begin\n";
1181 $paragraph_command_formatted = '' if (!defined($paragraph_command_formatted) or
1182 exists($special_list_commands{$format}->{$paragraph_command}));
1183 return '' if ($text =~ /^\s*$/);
1184 foreach my $style(t2h_collect_styles($command_stack_at_begin))
1186 $text = t2h_begin_style($style, $text);
1188 foreach my $style(t2h_collect_styles($command_stack_at_end))
1190 $text = t2h_end_style($style, $text);
1192 if (defined($paragraph_number) and defined($$paragraph_number))
1194 $$paragraph_number++;
1195 return $text if (($format eq 'itemize' or $format eq 'enumerate') and
1196 ($$paragraph_number == 1));
1201 $open .= " align=\"$paragraph_style{$align}\"";
1203 my $footnote_text = '';
1204 if (defined($command_stack_at_begin->[0]) and $command_stack_at_begin->[0] eq 'footnote')
1206 my $state = $Texi2HTML::THISDOC{'state'};
1207 $makekinfo_like_paragraph_in_footnote_nr{$makekinfo_like_footnote_absolute_number}++;
1208 if ($makekinfo_like_paragraph_in_footnote_nr{$makekinfo_like_footnote_absolute_number} <= 1)
1210 $open.=' class="footnote"';
1211 my $document_file = $state->{'footnote_document_file'};
1212 if ($document_file eq $state->{'footnote_footnote_file'})
1214 $document_file = '';
1216 my $docid = $state->{'footnote_place_id'};
1217 my $doc_state = $state->{'footnote_document_state'};
1218 $docid = '' if ($doc_state->{'outside_document'} or $doc_state->{'multiple_pass'});
1219 my $foot_label = &$anchor($state->{'footnote_footnote_id'},
1220 $document_file . "#$state->{'footnote_place_id'}",
1221 "$state->{'footnote_number_in_page'}");
1222 $footnote_text = "<small>[${foot_label}]</small> ";
1225 return $open.'>'.$footnote_text.$text.'</p>';
1229 #############################################################################
1231 #############################################################################
1233 # For split pages, use index.html as start page!
1234 if ($Texi2HTML::Config::SPLIT eq 'section') {
1235 $Texi2HTML::Config::TOP_FILE = 'index.html';