]> git.donarmstrong.com Git - lilypond.git/blob - lily/span-bar-stub-engraver.cc
Issue 4997/3: Use Preinit in Music
[lilypond.git] / lily / span-bar-stub-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2011--2015 Mike Solomon <mike@mikesolomon.org>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <algorithm>
21
22 #include "align-interface.hh"
23 #include "context.hh"
24 #include "grob.hh"
25 #include "grob-properties.hh"
26 #include "item.hh"
27 #include "pointer-group-interface.hh"
28 #include "engraver.hh"
29
30 #include "translator.icc"
31
32 /*
33   The Span_bar_stub_engraver creates SpanBarStub grobs in the contexts
34   that a grouping context contains.  For example, if a PianoStaff contains
35   two Staffs, a Dynamics, and a Lyrics, SpanBarStubs will be created in
36   all contexts that do not have bar lines (Dynamics and Lyrics).
37
38   We only want to create these SpanBarStubs in contexts that the SpanBar
39   traverses.  However, Contexts do not contain layout information and it
40   is thus difficult to know if they will eventually be above or below other
41   Contexts.  To determine this we use the VerticalAxisGroup created in the
42   Context.  We relate VerticalAxisGroups to Contexts in the variable
43   axis_groups_ and weed out unused contexts after each translation timestep.
44
45   Note that SpanBarStubs exist for pure height calculations ONLY.
46   They should never be visually present on the page and should never
47   be engraved in contexts where BarLines are engraved.
48 */
49
50 class Span_bar_stub_engraver : public Engraver
51 {
52   vector<Grob *> spanbars_;
53   SCM axis_groups_;
54
55 public:
56   TRANSLATOR_DECLARATIONS (Span_bar_stub_engraver);
57 protected:
58   void acknowledge_span_bar (Grob_info);
59   void acknowledge_hara_kiri_group_spanner (Grob_info);
60   void process_acknowledged ();
61   void stop_translation_timestep ();
62   virtual void derived_mark () const;
63 };
64
65 Span_bar_stub_engraver::Span_bar_stub_engraver ()
66 {
67   axis_groups_ = SCM_EOL;
68 }
69
70 void
71 Span_bar_stub_engraver::derived_mark () const
72 {
73   scm_gc_mark (axis_groups_);
74 }
75
76 void
77 Span_bar_stub_engraver::acknowledge_span_bar (Grob_info i)
78 {
79   spanbars_.push_back (i.grob ());
80 }
81
82 void
83 Span_bar_stub_engraver::acknowledge_hara_kiri_group_spanner (Grob_info i)
84 {
85   axis_groups_ = scm_cons (scm_cons (i.grob ()->self_scm (), i.context ()->self_scm ()), axis_groups_);
86 }
87
88 void
89 Span_bar_stub_engraver::process_acknowledged ()
90 {
91   if (!spanbars_.size ())
92     return;
93
94   if (!scm_is_pair (axis_groups_))
95     {
96       programming_error ("At least one vertical axis group needs to be created in the first time step.");
97       return;
98     }
99   Grob *vertical_alignment = Grob::get_root_vertical_alignment (unsmob<Grob> (scm_caar (axis_groups_)));
100   if (!vertical_alignment) // we are at the beginning of a score, so no need for stubs
101     return;
102
103   for (vsize i = 0; i < spanbars_.size (); i++)
104     {
105       extract_grob_set (spanbars_[i], "elements", bars);
106       vector<vsize> bar_axis_indices;
107       for (vsize j = 0; j < bars.size (); j++)
108         {
109           int i = Grob::get_vertical_axis_group_index (bars[j]);
110           if (i >= 0)
111             bar_axis_indices.push_back ((vsize) i);
112         }
113       vector<Context *> affected_contexts;
114       vector<Grob *> y_parents;
115       vector<bool> keep_extent;
116       for (SCM s = axis_groups_; scm_is_pair (s); s = scm_cdr (s))
117         {
118           Context *c = unsmob<Context> (scm_cdar (s));
119           Grob *g = unsmob<Grob> (scm_caar (s));
120           if (!c || !g)
121             continue;
122           if (c->is_removable ())
123             continue;
124
125           vsize j = Grob::get_vertical_axis_group_index (g);
126           if (j > bar_axis_indices[0]
127               && j < bar_axis_indices.back ()
128               && find (bar_axis_indices.begin (), bar_axis_indices.end (), j) == bar_axis_indices.end ())
129             {
130               vsize k = 0;
131               for (; k < bar_axis_indices.size (); k++)
132                 if (bar_axis_indices[k] > j)
133                   break;
134
135               k--;
136
137               if (c && c->get_parent_context ())
138                 {
139                   keep_extent.push_back (to_boolean (bars[k]->get_property ("allow-span-bar")));
140                   y_parents.push_back (g);
141                   affected_contexts.push_back (c);
142                 }
143             }
144         }
145
146       for (vsize j = 0; j < affected_contexts.size (); j++)
147         {
148           Item *it = new Item (Grob_property_info (affected_contexts[j], ly_symbol2scm ("SpanBarStub")).updated ());
149           it->set_parent (spanbars_[i], X_AXIS);
150           Grob_info gi = make_grob_info (it, spanbars_[i]->self_scm ());
151           announce_grob (gi, affected_contexts[j]);
152           if (!keep_extent[j])
153             it->suicide ();
154         }
155     }
156
157   spanbars_.clear ();
158 }
159
160 // removes all unused contexts
161 void
162 Span_bar_stub_engraver::stop_translation_timestep ()
163 {
164   SCM axis_groups = SCM_EOL;
165   for (SCM s = axis_groups_; scm_is_pair (s); s = scm_cdr (s))
166     {
167       Context *c = unsmob<Context> (scm_cdar (s));
168       Grob *g = unsmob<Grob> (scm_caar (s));
169       if (!c || !g)
170         continue;
171       if (c->is_removable ())
172         continue;
173       axis_groups = scm_cons (scm_car (s), axis_groups);
174     }
175   axis_groups_ = axis_groups;
176 }
177
178 void
179 Span_bar_stub_engraver::boot ()
180 {
181   ADD_ACKNOWLEDGER (Span_bar_stub_engraver, span_bar);
182   ADD_ACKNOWLEDGER (Span_bar_stub_engraver, hara_kiri_group_spanner);
183 }
184
185 ADD_TRANSLATOR (Span_bar_stub_engraver,
186                 /* doc */
187                 "Make stubs for span bars in all contexts that the span bars cross.",
188
189                 /* create */
190                 "SpanBarStub ",
191
192                 /* read */
193                 "",
194
195                 /* write */
196                 ""
197                );