/*
This file is part of LilyPond, the GNU music typesetter.
- Copyright (C) 2009--2011 Joe Neeman <joeneeman@gmail.com>
+ Copyright (C) 2009--2012 Joe Neeman <joeneeman@gmail.com>
LilyPond is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Returns the number of footntoes associated with a given line.
*/
-vsize
-Page_layout_problem::get_footnote_count (SCM lines)
+vector<Grob *>
+Page_layout_problem::get_footnote_grobs (SCM lines)
{
- vsize fn_count = 0;
+ vector<Grob *> footnotes;
for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s))
{
if (Grob *g = unsmob_grob (scm_car (s)))
programming_error ("got a grob for footnotes that wasn't a System");
continue;
}
- fn_count += sys->num_footnotes ();
+ extract_grob_set (sys, "footnotes-after-line-breaking", footnote_grobs);
+ footnotes.insert (footnotes.end (), footnote_grobs.begin (), footnote_grobs.end ());
}
else if (Prob *p = unsmob_prob (scm_car (s)))
{
if (stencils == SCM_EOL)
continue;
for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st))
- fn_count++;
+ footnotes.push_back (0);
}
}
- return fn_count;
+ return footnotes;
+}
+
+vsize
+Page_layout_problem::get_footnote_count (SCM lines)
+{
+ vector<Grob *> notes = get_footnote_grobs (lines);
+ return notes.size ();
+}
+
+SCM
+Page_layout_problem::get_footnotes_from_lines (SCM lines)
+{
+ if (!scm_is_pair (lines))
+ return SCM_EOL;
+
+ bool footnotes_added;
+ if (Grob *g = unsmob_grob (scm_car (lines)))
+ footnotes_added = !scm_is_null (g->get_property ("footnote-stencil"));
+ else if (Prob *p = unsmob_prob (scm_car (lines)))
+ footnotes_added = !scm_is_null (p->get_property ("footnote-stencil"));
+ else
+ {
+ programming_error ("Systems on a page must be a prob or grob.");
+ return SCM_EOL;
+ }
+ if (!footnotes_added)
+ {
+ programming_error ("Footnotes must be added to lines before they are retrieved.");
+ return SCM_EOL;
+ }
+
+ SCM out = SCM_EOL;
+ for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s))
+ {
+ if (Grob *g = unsmob_grob (scm_car (s)))
+ out = scm_cons (g->get_property ("footnote-stencil"), out);
+ else if (Prob *p = unsmob_prob (scm_car (s)))
+ out = scm_cons (p->get_property ("footnote-stencil"), out);
+ else
+ programming_error ("Systems on a page must be a prob or grob.");
+ }
+
+ return scm_reverse (out);
}
/*
- Returns a stencil for the footnote of each system. This stencil may
+ Adds a footnote stencil to each system. This stencil may
itself be comprised of several footnotes.
This is a long function, but it seems better to keep it intact rather than
splitting it into parts.
*/
-SCM
-Page_layout_problem::get_footnotes_from_lines (SCM lines, int counter, Paper_book *pb)
+void
+Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book *pb)
{
/*
first, we have to see how many footnotes are on this page.
if (!paper)
{
programming_error ("Cannot get footnotes because there is no valid paper block.");
- return SCM_EOL;
+ return;
}
SCM number_footnote_table = pb->top_paper ()->c_variable ("number-footnote-table");
Real padding = robust_scm2double (paper->c_variable ("footnote-padding"), 0.0);
Real number_raise = robust_scm2double (paper->c_variable ("footnote-number-raise"), 0.0);
- vsize fn_count = get_footnote_count (lines);
+ vector<Grob *> fn_grobs = get_footnote_grobs (lines);
+ vsize fn_count = fn_grobs.size ();
// now, make the footnote stencils with the numbering function
SCM numbers = SCM_EOL;
vector<Stencil *> footnote_number_stencils; // Holds translated versions of the stencilized numbering markups.
for (vsize i = 0; i < fn_count; i++)
{
+ if (fn_grobs[i])
+ {
+ SCM assertion_function = fn_grobs[i]->get_property ("numbering-assertion-function");
+ if (ly_is_procedure (assertion_function))
+ (void) scm_call_1 (assertion_function, scm_from_int (counter));
+ }
SCM markup = scm_call_1 (numbering_function, scm_from_int (counter));
Stencil *s = unsmob_stencil (Text_interface::interpret_markup (layout, props, markup));
if (!s)
{
*in_text_tail = scm_cons (footnote_number_markups[i], SCM_EOL);
in_text_tail = SCM_CDRLOC (*in_text_tail);
- footnote_number_stencils[i]->translate_axis (max_length - footnote_number_stencils[i]->extent (X_AXIS).length (), X_AXIS);
+ footnote_number_stencils[i]->translate_axis ((max_length
+ - footnote_number_stencils[i]->extent (X_AXIS).length ()),
+ X_AXIS);
*tail = scm_cons (footnote_number_stencils[i]->smobbed_copy (), SCM_EOL);
tail = SCM_CDRLOC (*tail);
}
// build the footnotes
- SCM footnotes = SCM_EOL;
-
for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s))
{
// Take care of musical systems.
continue;
}
Stencil mol;
-
- for (vsize i = 0; i < sys->footnote_grobs ()->size (); i++)
+ Stencil in_note_mol;
+ extract_grob_set (sys, "footnotes-after-line-breaking", footnote_grobs);
+ for (vsize i = 0; i < footnote_grobs.size (); i++)
{
- Grob *footnote = sys->footnote_grobs ()->at (i);
+ Grob *footnote = footnote_grobs[i];
SCM footnote_markup = footnote->get_property ("footnote-text");
if (Spanner *orig = dynamic_cast<Spanner *>(footnote))
if (orig->is_broken ())
footnote_markup = orig->broken_intos_[0]->get_property ("footnote-text");
- if (!Text_interface::is_markup (footnote_markup))
- continue;
-
SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
paper->self_scm ());
{
if (orig->is_broken ())
for (vsize i = 0; i < orig->broken_intos_.size (); i++)
- do_numbering = do_numbering || to_boolean (orig->broken_intos_[i]->get_property ("automatically-numbered"));
+ do_numbering = do_numbering
+ || to_boolean (orig->broken_intos_[i]->get_property ("automatically-numbered"));
}
if (do_numbering)
{
}
Stencil *annotation = unsmob_stencil (scm_car (numbers));
- annotation->translate_axis (footnote_stencil->extent (Y_AXIS)[UP] + number_raise - annotation->extent (Y_AXIS)[UP], Y_AXIS);
+ annotation->translate_axis ((footnote_stencil->extent (Y_AXIS)[UP]
+ + number_raise
+ - annotation->extent (Y_AXIS)[UP]),
+ Y_AXIS);
footnote_stencil->add_at_edge (X_AXIS, LEFT, *annotation, 0.0);
numbers = scm_cdr (numbers);
in_text_numbers = scm_cdr (in_text_numbers);
}
- mol.add_at_edge (Y_AXIS, DOWN, *footnote_stencil, padding);
+ if (!footnote_stencil->is_empty ())
+ {
+ if (to_boolean (footnote->get_property ("footnote")))
+ mol.add_at_edge (Y_AXIS, DOWN, *footnote_stencil, padding);
+ else
+ in_note_mol.add_at_edge (Y_AXIS, DOWN, *footnote_stencil, padding);
+ }
}
- footnotes = scm_cons (mol.smobbed_copy (), footnotes);
+ sys->set_property ("in-note-stencil", in_note_mol.smobbed_copy ());
+ sys->set_property ("footnote-stencil", mol.smobbed_copy ());
}
// Take care of top-level markups
else if (Prob *p = unsmob_prob (scm_car (s)))
{
SCM stencils = p->get_property ("footnotes");
- if (stencils == SCM_EOL)
- continue;
- Stencil footnote_stencil;
+ Stencil mol;
for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st))
{
- Stencil mol;
+ Stencil footnote_stencil;
Stencil *footnote = unsmob_stencil (scm_caddar (st));
- mol.add_stencil (*footnote);
+ footnote_stencil.add_stencil (*footnote);
bool do_numbering = to_boolean (scm_cadar (st));
SCM in_text_stencil = Stencil ().smobbed_copy ();
if (do_numbering)
{
Stencil *annotation = unsmob_stencil (scm_car (numbers));
SCM in_text_annotation = scm_car (in_text_numbers);
- in_text_stencil = Text_interface::interpret_markup (layout, props, in_text_annotation);
+ in_text_stencil = Text_interface::interpret_markup (layout,
+ props,
+ in_text_annotation);
if (!unsmob_stencil (in_text_stencil))
in_text_stencil = SCM_EOL;
- annotation->translate_axis (mol.extent (Y_AXIS)[UP] + number_raise - annotation->extent (Y_AXIS)[UP], Y_AXIS);
- mol.add_at_edge (X_AXIS, LEFT, *annotation, 0.0);
+ annotation->translate_axis ((footnote_stencil.extent (Y_AXIS)[UP]
+ + number_raise
+ - annotation->extent (Y_AXIS)[UP]),
+ Y_AXIS);
+ footnote_stencil.add_at_edge (X_AXIS, LEFT, *annotation, 0.0);
numbers = scm_cdr (numbers);
in_text_numbers = scm_cdr (in_text_numbers);
}
- number_footnote_table = scm_cons (scm_cons (scm_caar (st), in_text_stencil), number_footnote_table);
- footnote_stencil.add_at_edge (Y_AXIS, DOWN, mol, padding);
+ number_footnote_table = scm_cons (scm_cons (scm_caar (st),
+ in_text_stencil),
+ number_footnote_table);
+ if (!footnote_stencil.is_empty ())
+ mol.add_at_edge (Y_AXIS, DOWN, footnote_stencil, padding);
}
- footnotes = scm_cons (footnote_stencil.smobbed_copy (), footnotes);
+ p->set_property ("footnote-stencil", mol.smobbed_copy ());
}
}
// note that this line of code doesn't do anything if numbering isn't turned on
pb->top_paper ()->set_variable (ly_symbol2scm ("number-footnote-table"), number_footnote_table);
- if (!scm_is_pair (footnotes))
- return SCM_EOL;
-
- return scm_reverse (footnotes);
}
Stencil *
}
}
-Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM systems, int footnote_count)
+Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM systems)
: bottom_skyline_ (DOWN)
{
Prob *page = unsmob_prob (page_scm);
header_padding_ = 0;
footer_padding_ = 0;
page_height_ = 100;
+ force_ = 0;
if (page)
{
if (pb && pb->paper_)
{
- if (to_boolean (pb->paper_->c_variable ("reset-footnotes-on-new-page")))
- footnote_count = 0;
- SCM footnotes = get_footnotes_from_lines (systems, footnote_count, pb);
+ SCM footnotes = get_footnotes_from_lines (systems);
add_footnotes_to_footer (footnotes, foot, pb);
}
else
read_spacing_spec (top_system_spacing, &header_padding_, ly_symbol2scm ("padding"));
read_spacing_spec (last_bottom_spacing, &footer_padding_, ly_symbol2scm ("padding"));
+ in_note_padding_ = robust_scm2double (paper->c_variable ("in-note-padding"), 0.5);
+ in_note_direction_ = robust_scm2dir (paper->c_variable ("in-note-direction"), UP);
}
bool last_system_was_title = false;
build_system_skyline (elts, minimum_offsets_with_min_dist, &up_skyline, &down_skyline);
up_skyline.shift (indent);
down_skyline.shift (indent);
+ Stencil *in_note_stencil = unsmob_stencil (sys->get_property ("in-note-stencil"));
+
+ if (in_note_stencil && in_note_stencil->extent (Y_AXIS).length () > 0)
+ {
+ sys->set_property ("in-note-padding", scm_from_double (in_note_padding_));
+ sys->set_property ("in-note-direction", scm_from_int (in_note_direction_));
+ Skyline *sky = in_note_direction_ == UP ? &up_skyline : &down_skyline;
+ sky->set_minimum_height (sky->max_height ()
+ + in_note_direction_
+ * (in_note_padding_
+ + in_note_stencil->extent (Y_AXIS).length ()));
+ }
/*
We need to call distance with skyline-horizontal-padding because
of an individual staff. So we add the padding for the distance check
at the time of adding in the system.
*/
- Real minimum_distance = up_skyline.distance (bottom_skyline_, robust_scm2double (sys->get_property ("skyline-horizontal-padding"), 0)) + padding;
+ Real minimum_distance = up_skyline.distance (bottom_skyline_,
+ robust_scm2double (sys->get_property ("skyline-horizontal-padding"),
+ 0))
+ + padding;
Spring spring_copy = spring;
spring_copy.ensure_min_distance (minimum_distance);
elements_.push_back (Element (prob, padding));
}
+/**
+ For ragged-last pages, we usually want to stretch the page so that it
+ is not much more compressed than the previous page. Here, if ragged is
+ true and you pass a value of fixed_force that !isinf, then I will try
+ to space this page using the given force. If it does not fit, I will
+ resort to just filling the page (non-raggedly).
+*/
void
-Page_layout_problem::solve_rod_spring_problem (bool ragged)
+Page_layout_problem::solve_rod_spring_problem (bool ragged, Real fixed_force)
{
Simple_spacer spacer;
for (vsize i = 0; i < springs_.size (); ++i)
spacer.add_spring (springs_[i]);
- spacer.solve (page_height_, ragged);
+ if (ragged && !isinf (fixed_force))
+ {
+ // We need to tell the spacer it isn't ragged. Otherwise, it will
+ // refuse to stretch.
+ spacer.solve (page_height_, false);
+
+ if (spacer.configuration_length (fixed_force) <= page_height_)
+ spacer.set_force (fixed_force);
+ }
+ else
+ spacer.solve (page_height_, ragged);
+
solution_ = spacer.spring_positions ();
+ force_ = spacer.force ();
if (!spacer.fits ())
{
}
}
+Real
+Page_layout_problem::force () const
+{
+ return force_;
+}
+
// The solution_ vector stores the position of every live VerticalAxisGroup
// and every title. From that information,
// 1) within each system, stretch the staves so they land at the right position
if (staff_idx)
loose_line_min_distances.push_back (min_offsets[staff_idx - 1] - min_offsets[staff_idx]);
else
- loose_line_min_distances.push_back (elements_[i].padding - min_offsets[staff_idx]);
+ {
+ // A null line to break any staff-affinity from the previous system
+ loose_line_min_distances.push_back (0.0);
+ loose_lines.push_back (0);
+ loose_line_min_distances.push_back (elements_[i].padding - min_offsets[0]);
+ }
loose_lines.push_back (staff);
distribute_loose_lines (loose_lines, loose_line_min_distances,
// this is the first line in a system
Real min_dist = 0;
if (loose_lines.back ())
- // distance to the final line in the preceding system,
- // including 'system-system-spacing 'padding
- min_dist = (Axis_group_interface::minimum_distance (loose_lines.back (),
- staff,
- Y_AXIS)
- + elements_[i].padding);
+ {
+ // distance to the final line in the preceding system,
+ // including 'system-system-spacing 'padding
+ min_dist = (Axis_group_interface::minimum_distance (loose_lines.back (),
+ staff, Y_AXIS)
+ + elements_[i].padding);
+ // A null line to break any staff-affinity for the previous system
+ loose_line_min_distances.push_back (0.0);
+ loose_lines.push_back (0);
+ }
else if (!last_title_extent.is_empty ())
// distance to the preceding title,
// including 'markup-system-spacing 'padding
vector<Real> solution = spacer.spring_positions ();
for (vsize i = 1; i + 1 < solution.size (); ++i)
- {
- Real system_offset = scm_to_double (loose_lines[i]->get_property ("system-Y-offset"));
- loose_lines[i]->translate_axis (first_translation - solution[i] - system_offset, Y_AXIS);
- }
+ if (loose_lines[i])
+ {
+ Real system_offset = scm_to_double (loose_lines[i]->get_property ("system-Y-offset"));
+ loose_lines[i]->translate_axis (first_translation - solution[i] - system_offset, Y_AXIS);
+ }
+}
+
+SCM
+Page_layout_problem::fixed_force_solution (Real force)
+{
+ solve_rod_spring_problem (true, force);
+ return find_system_offsets ();
}
SCM
Page_layout_problem::solution (bool ragged)
{
- solve_rod_spring_problem (ragged);
+ solve_rod_spring_problem (ragged, -infinity_f);
return find_system_offsets ();
}