]> git.donarmstrong.com Git - lilypond.git/blob - lily/span-bar-stub-engraver.cc
Prevents segfault in Span_bar_stub_engraver through derived_mark.
[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--2012 Mike Solomon <mike@apollinemike.com>
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 "bar-line.hh"
24 #include "context.hh"
25 #include "grob.hh"
26 #include "item.hh"
27 #include "pointer-group-interface.hh"
28 #include "span-bar.hh"
29 #include "engraver.hh"
30
31 /*
32   The Span_bar_stub_engraver creates SpanBarStub grobs in the contexts
33   that a grouping context contains.  For example, if a PianoStaff contains
34   two Staffs, a Dynamics, and a Lyrics, SpanBarStubs will be created in
35   all contexts that do not have bar lines (Dynamics and Lyrics).
36
37   We only want to create these SpanBarStubs in contexts that the SpanBar
38   traverses.  However, Contexts do not contain layout information and it
39   is thus difficult to know if they will eventually be above or below other
40   Contexts.  To determine this we use the VerticalAxisGroup created in the
41   Context.  We relate VerticalAxisGroups to Contexts in the variable
42   axis_groups_ and weed out unused contexts after each translation timestep.
43
44   Note that SpanBarStubs exist for pure height calculations ONLY.
45   They should never be visually present on the page and should never
46   be engraved in contexts where BarLines are engraved.
47 */
48
49 class Span_bar_stub_engraver : public Engraver
50 {
51   vector<Grob *> spanbars_;
52   SCM axis_groups_;
53
54 public:
55   TRANSLATOR_DECLARATIONS (Span_bar_stub_engraver);
56 protected:
57   DECLARE_ACKNOWLEDGER (span_bar);
58   DECLARE_ACKNOWLEDGER (hara_kiri_group_spanner);
59   void process_acknowledged ();
60   void stop_translation_timestep ();
61   virtual void derived_mark () const;
62 };
63
64 Span_bar_stub_engraver::Span_bar_stub_engraver ()
65 {
66   axis_groups_ = SCM_EOL;
67 }
68
69 void
70 Span_bar_stub_engraver::derived_mark () const
71 {
72   scm_gc_mark (axis_groups_);
73 }
74
75 void
76 Span_bar_stub_engraver::acknowledge_span_bar (Grob_info i)
77 {
78   spanbars_.push_back (i.grob ());
79 }
80
81 void
82 Span_bar_stub_engraver::acknowledge_hara_kiri_group_spanner (Grob_info i)
83 {
84   axis_groups_ = scm_cons (scm_cons (i.grob ()->self_scm (), i.context ()->self_scm ()), axis_groups_);
85 }
86
87 void
88 Span_bar_stub_engraver::process_acknowledged ()
89 {
90   if (!spanbars_.size ())
91     return;
92
93   if (!scm_is_pair (axis_groups_))
94     {
95       programming_error ("At least one vertical axis group needs to be created in the first time step.");
96       return;
97     }
98   Grob *vertical_alignment = Grob::get_root_vertical_alignment (unsmob_grob (scm_caar (axis_groups_)));
99   if (!vertical_alignment) // we are at the beginning of a score, so no need for stubs
100     return;
101
102   for (vsize i = 0; i < spanbars_.size (); i++)
103     {
104       extract_grob_set (spanbars_[i], "elements", bars);
105       vector<vsize> bar_axis_indices;
106       for (vsize j = 0; j < bars.size (); j++)
107         {
108           int i = Grob::get_vertical_axis_group_index (bars[j]);
109           if (i >= 0)
110             bar_axis_indices.push_back ((vsize) i);
111         }
112       vector<Context *> affected_contexts;
113       vector<Grob *> y_parents;
114       vector<bool> keep_extent;
115       for (SCM s = axis_groups_; scm_is_pair (s); s = scm_cdr (s))
116         {
117           Context *c = unsmob_context (scm_cdar (s));
118           Grob *g = unsmob_grob (scm_caar (s));
119           if (!c || !g)
120             continue;
121           if (c->is_removable ())
122             continue;
123
124           vsize j = Grob::get_vertical_axis_group_index (g);
125           if (j > bar_axis_indices[0]
126               && j < bar_axis_indices.back ()
127               && find (bar_axis_indices.begin (), bar_axis_indices.end (), j) == bar_axis_indices.end ())
128             {
129               vsize k = 0;
130               for (; k < bar_axis_indices.size (); k++)
131                 if (bar_axis_indices[k] > j)
132                   break;
133
134               k--;
135
136               if (c && c->get_parent_context ())
137                 {
138                   keep_extent.push_back (to_boolean (bars[k]->get_property ("allow-span-bar")));
139                   y_parents.push_back (g);
140                   affected_contexts.push_back (c);
141                 }
142             }
143         }
144
145       for (vsize j = 0; j < affected_contexts.size (); j++)
146         {
147           Item *it = new Item (updated_grob_properties (affected_contexts[j], ly_symbol2scm ("SpanBarStub")));
148           it->set_parent (spanbars_[i], X_AXIS);
149           Grob_info gi = make_grob_info (it, spanbars_[i]->self_scm ());
150           gi.rerouting_daddy_context_ = affected_contexts[j];
151           announce_grob (gi);
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 #include "translator.icc"
179
180 ADD_ACKNOWLEDGER (Span_bar_stub_engraver, span_bar);
181 ADD_ACKNOWLEDGER (Span_bar_stub_engraver, hara_kiri_group_spanner);
182 ADD_TRANSLATOR (Span_bar_stub_engraver,
183                 /* doc */
184                 "Make stubs for span bars in all contexts that the span bars cross.",
185
186                 /* create */
187                 "SpanBarStub ",
188
189                 /* read */
190                 "",
191
192                 /* write */
193                 ""
194                );