X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fpaper-column.cc;h=53600cbb21142733360c1f258813caf0a2b8a5b5;hb=0996f6663b2635491b5efa385e0e3a581fb96292;hp=5373d06f341796d28e7d4d29265da292361d207c;hpb=a6ee9dcd388111e842064a8d46ab06c4897a00d2;p=lilypond.git diff --git a/lily/paper-column.cc b/lily/paper-column.cc index 5373d06f34..53600cbb21 100644 --- a/lily/paper-column.cc +++ b/lily/paper-column.cc @@ -1,168 +1,414 @@ /* - paper-column.cc -- implement Paper_column + This file is part of LilyPond, the GNU music typesetter. - source file of the GNU LilyPond music typesetter + Copyright (C) 1997--2011 Han-Wen Nienhuys - (c) 1997--2002 Han-Wen Nienhuys + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . */ -#include "moment.hh" #include "paper-column.hh" -#include "paper-score.hh" -#include "warn.hh" + #include "axis-group-interface.hh" -#include "spaceable-grob.hh" -#include "molecule.hh" -#include "text-item.hh" -#include "lookup.hh" +#include "bar-line.hh" +#include "break-align-interface.hh" #include "font-interface.hh" +#include "grob-array.hh" +#include "lookup.hh" +#include "lookup.hh" +#include "moment.hh" +#include "output-def.hh" +#include "paper-score.hh" +#include "pointer-group-interface.hh" +#include "rhythmic-head.hh" +#include "separation-item.hh" +#include "skyline-pair.hh" +#include "spaceable-grob.hh" +#include "spring.hh" +#include "string-convert.hh" +#include "system.hh" +#include "text-interface.hh" +#include "warn.hh" - - - - -ADD_INTERFACE (Paper_column, "paper-column-interface", - " Paper_columns form the top-most item parent. (The Paper_columns X - parent is System, which is a spanner.) - - Paper_columns form the units for the spacing engine. They are - numbered, the first (leftmost) is column 0. Numbering happens before - line-breaking, and columns are not renumbered after line breaking. - - Since many columns go unused, you should only use the rank field to - get ordering information. Two adjacent columns may have - non-adjacent numbers. - - Don't be confused by right-items: each spacing wish can also contain - a number of items, with which a spacing constraint may be kept. It's - a little baroque, but it might come in handy later on? - -", - "between-cols between-system-string when bounded-by-me shortest-playing-duration shortest-starter-duration"); - +Grob * +Paper_column::clone () const +{ + return new Paper_column (*this); +} void Paper_column::do_break_processing () { - Spaceable_grob::remove_interface (this); Item::do_break_processing (); } - int -Paper_column::get_rank (Grob*me) +Paper_column::get_rank (Grob const *me) +{ + return dynamic_cast (me)->rank_; +} + +void +Paper_column::set_rank (int rank) { - return dynamic_cast (me)->rank_; + rank_ = rank; } -System* +System * Paper_column::get_system () const { return system_; } -Paper_column* +void +Paper_column::set_system (System *s) +{ + system_ = s; +} + +Paper_column * Paper_column::get_column () const { - return (Paper_column*) (this); + return (Paper_column *) (this); } Paper_column::Paper_column (SCM l) - : Item (l) // guh.? + : Item (l) { - system_=0; + system_ = 0; rank_ = -1; } +Paper_column::Paper_column (Paper_column const &src) + : Item (src) +{ + system_ = 0; + rank_ = src.rank_; +} + +int +Paper_column::compare (Grob *const &a, + Grob *const &b) +{ + return sign (dynamic_cast (a)->rank_ + - dynamic_cast (b)->rank_); +} + +bool +Paper_column::less_than (Grob *const &a, + Grob *const &b) +{ + Paper_column *pa = dynamic_cast (a); + Paper_column *pb = dynamic_cast (b); + + return pa->rank_ < pb->rank_; +} + Moment -Paper_column::when_mom (Grob*me) +Paper_column::when_mom (Grob *me) { - SCM m = me->get_grob_property ("when"); - Moment s (0); - if (unsmob_moment (m)) - { - return *unsmob_moment (m); - } - return s; + SCM m = me->get_property ("when"); + if (Moment *when = unsmob_moment (m)) + return *when; + return Moment (0); } bool -Paper_column::musical_b (Grob *me) +Paper_column::is_musical (Grob *me) { - SCM m = me->get_grob_property ("shortest-starter-duration"); + SCM m = me->get_property ("shortest-starter-duration"); Moment s (0); if (unsmob_moment (m)) - { - s = *unsmob_moment (m); - } + s = *unsmob_moment (m); return s != Moment (0); - } - bool -Paper_column::used_b (Grob*me) +Paper_column::is_used (Grob *me) { - return gh_pair_p (me->get_grob_property ("elements")) || Item::breakable_b (me) - || gh_pair_p (me->get_grob_property ("bounded-by-me")) - ; + extract_grob_set (me, "elements", elts); + if (elts.size ()) + return true; + + extract_grob_set (me, "bounded-by-me", bbm); + if (bbm.size ()) + return true; + + if (Paper_column::is_breakable (me)) + return true; + + if (to_boolean (me->get_property ("used"))) + return true; + return false; } -/* - Print a vertical line and the rank number, to aid debugging. - */ +bool +Paper_column::is_breakable (Grob *me) +{ + return scm_is_symbol (me->get_property ("line-break-permission")); +} + +Real +Paper_column::minimum_distance (Grob *left, Grob *right) +{ + Drul_array cols (left, right); + Drul_array skys = Drul_array (Skyline (RIGHT), Skyline (LEFT)); + + Direction d = LEFT; + do + { + Skyline_pair *sp = Skyline_pair::unsmob (cols[d]->get_property ("horizontal-skylines")); + if (sp) + skys[d] = (*sp)[-d]; + } + while (flip (&d) != LEFT); -MAKE_SCHEME_CALLBACK(Paper_column,brew_molecule,1); + skys[RIGHT].merge (Separation_item::conditional_skyline (right, left)); + + return max (0.0, skys[LEFT].distance (skys[RIGHT])); +} + +Interval +Paper_column::break_align_width (Grob *me, SCM align_sym) +{ + Grob *p = me->get_parent (X_AXIS); + + if (is_musical (me)) + { + me->programming_error ("tried to get break-align-width of a musical column"); + return Interval (0, 0) + me->relative_coordinate (p, X_AXIS); + } + + Grob *align = 0; + if (align_sym == ly_symbol2scm ("staff-bar") + || align_sym == ly_symbol2scm ("break-alignment")) + align + = Pointer_group_interface::find_grob (me, ly_symbol2scm ("elements"), + (align_sym == ly_symbol2scm ("staff-bar") + ? Bar_line::non_empty_barline + : Break_alignment_interface::has_interface)); + else + { + extract_grob_set (me, "elements", elts); + for (vsize i = 0; i < elts.size (); i++) + { + if (elts[i]->get_property ("break-align-symbol") == align_sym) + { + align = elts[i]; + break; + } + } + } + + if (!align) + return Interval (0, 0) + me->relative_coordinate (p, X_AXIS); + + return align->extent (p, X_AXIS); +} + +/* + Print a vertical line and the rank number, to aid debugging. +*/ +MAKE_SCHEME_CALLBACK (Paper_column, print, 1); SCM -Paper_column::brew_molecule (SCM p) +Paper_column::print (SCM p) { - Grob *me = unsmob_grob (p); + Paper_column *me = dynamic_cast (unsmob_grob (p)); + + string r = to_string (Paper_column::get_rank (me)); + + Moment *mom = unsmob_moment (me->get_property ("when")); + string when = mom ? mom->to_string () : "?/?"; - String r = to_string (Paper_column::get_rank (me)); - SCM properties = Font_interface::font_alist_chain (me); - - Molecule t = Text_item::text2molecule (me, ly_str02scm (r.to_str0 ()), - properties); + Font_metric *musfont = Font_interface::get_default_font (me); + SCM properties = Font_interface::text_font_alist_chain (me); + + SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (), + properties, + ly_string2scm (r)); + SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (), + properties, + ly_string2scm (when)); + Stencil t = *unsmob_stencil (scm_mol); + t.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (when_mol), 0.1); t.align_to (X_AXIS, CENTER); t.align_to (Y_AXIS, DOWN); - - Molecule l = Lookup::filledbox (Box (Interval (-0.01, 0.01), + + Stencil l = Lookup::filled_box (Box (Interval (-0.01, 0.01), Interval (-2, -1))); - t.add_molecule (l); - return t.smobbed_copy (); + SCM small_letters = scm_cons (scm_acons (ly_symbol2scm ("font-size"), + scm_from_int (-6), SCM_EOL), + properties); + + int j = 0; + for (SCM s = me->get_object ("ideal-distances"); + scm_is_pair (s); s = scm_cdr (s)) + { + Spring *sp = unsmob_spring (scm_caar (s)); + if (!unsmob_grob (scm_cdar (s)) + || !unsmob_grob (scm_cdar (s))->get_system ()) + continue; + + j++; + Real y = -j * 1 -3; + vector pts; + pts.push_back (Offset (0, y)); + + Offset p2 (sp->distance (), y); + pts.push_back (p2); + + Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts); + Stencil head (musfont->find_by_name ("arrowheads.open.01")); + + SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (), + small_letters, + ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ()))); + + id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (sp->distance () / 3, y + 1))); + id_stencil.add_stencil (head.translated (p2)); + id_stencil = id_stencil.in_color (0,0,1); + l.add_stencil (id_stencil); + } + + for (SCM s = me->get_object ("minimum-distances"); + scm_is_pair (s); s = scm_cdr (s)) + { + Real dist = scm_to_double (scm_cdar (s)); + Grob *other = unsmob_grob (scm_caar (s)); + if (!other || other->get_system () != me->get_system ()) + continue; + + j++; + + Real y = -j * 1.0 -3.5; + vector pts; + pts.push_back (Offset (0, y)); + + Offset p2 (dist, y); + pts.push_back (p2); + + Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts); + Stencil head (musfont->find_by_name ("arrowheads.open.0M1")); + head.translate_axis (y, Y_AXIS); + id_stencil.add_stencil (head); + + SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (), + small_letters, + ly_string2scm (String_convert::form_string ("%5.2lf", + dist))); + + id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (dist / 3, y - 1))); + + id_stencil = id_stencil.in_color (1,0,0); + l.add_stencil (id_stencil); + } + t.add_stencil (l); + return t.smobbed_copy (); } /* This is all too hairy. We use bounded-by-me to make sure that some columns are kept "alive". Unfortunately, when spanners are suicided, - this falls apart again. (sigh.) + this falls apart again, because suicided spanners are still in + bounded-by-me - THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER. - */ -MAKE_SCHEME_CALLBACK(Paper_column,before_line_breaking,1); + THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER. +*/ +MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1); SCM Paper_column::before_line_breaking (SCM grob) { Grob *me = unsmob_grob (grob); - SCM c = me->get_grob_property ("bounded-by-me"); - SCM *ptrptr = &c; + SCM bbm = me->get_object ("bounded-by-me"); + Grob_array *ga = unsmob_grob_array (bbm); + if (!ga) + return SCM_UNSPECIFIED; + + vector &array (ga->array_reference ()); - while (gh_pair_p (*ptrptr)) + for (vsize i = array.size (); i--;) { - Grob * g = unsmob_grob (gh_car (*ptrptr)); + Grob *g = array[i]; - if (!g || !g->live ()) - { - *ptrptr = gh_cdr (*ptrptr); - } - else - { - ptrptr = SCM_CDRLOC (*ptrptr); - } + if (!g || !g->is_live ()) + /* UGH . potentially quadratic. */ + array.erase (array.begin () + i); } - me->set_grob_property ("bounded-by-me", c); return SCM_UNSPECIFIED; } + +/* FIXME: This is a hack that we use to identify columns that used to + contain note-heads but whose note-heads were moved by one of the ligature + engravers. Once the ligature engravers are fixed to behave nicely, this + function can be removed. +*/ +bool +Paper_column::is_extraneous_column_from_ligature (Grob *me) +{ + if (!is_musical (me)) + return false; + + // If all the note-heads that I think are my children actually belong + // to another column, then I am extraneous. + extract_grob_set (me, "elements", elts); + bool has_notehead = false; + for (vsize i = 0; i < elts.size (); i++) + { + if (Rhythmic_head::has_interface (elts[i])) + { + has_notehead = true; + if (dynamic_cast (elts[i])->get_column () == me) + return false; + } + } + return has_notehead; +} + +ADD_INTERFACE (Paper_column, + "@code{Paper_column} objects form the top-most X@tie{}parents" + " for items. There are two types of columns: musical and" + " non-musical, to which musical and non-musical objects are" + " attached respectively. The spacing engine determines the" + " X@tie{}positions of these objects.\n" + "\n" + "They are numbered, the first (leftmost) is column@tie{}0." + " Numbering happens before line breaking, and columns are not" + " renumbered after line breaking. Since many columns go" + " unused, you should only use the rank field to get ordering" + " information. Two adjacent columns may have non-adjacent" + " numbers.", + + /* properties */ + "between-cols " + "bounded-by-me " + "full-measure-extra-space " + "grace-spacing " + "labels " + "line-break-system-details " + "line-break-penalty " + "line-break-permission " + "maybe-loose " + "page-break-penalty " + "page-break-permission " + "page-turn-penalty " + "page-turn-permission " + "rhythmic-location " + "shortest-playing-duration " + "shortest-starter-duration " + "spacing " + "used " + "when "); +