]> git.donarmstrong.com Git - lilypond.git/blob - lily/sequential-iterator.cc
8679bcbbe158843dbdaeffa00b84cf1d6699429b
[lilypond.git] / lily / sequential-iterator.cc
1 /*
2   sequential-iterator.cc -- implement Sequential_iterator
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "sequential-iterator.hh"
10 #include "music.hh"
11 #include "translator-group.hh"
12 #include "context.hh"
13 #include "grace-fixup.hh"
14
15 /*
16   TODO: handling of grace notes is exquisite pain.  This handling
17   should be formally specified and then the implementation verified.
18 */
19
20 /*
21   Invariant for the data structure.
22
23
24   if (scm_is_pair (cursor_))
25   iter_->music_ == unsmob_music (scm_car (cursor_))
26   else
27   iter_ == 0;
28
29   The length of musiclist from start to up to cursor_ (cursor_ not
30   including), is summed
31
32   here_mom_  = sum (length (musiclist [start ... cursor>))  %)
33 */
34 Sequential_iterator::Sequential_iterator ()
35 {
36   here_mom_ = Moment (0);
37   cursor_ = SCM_EOL;
38   grace_fixups_ = 0;
39   iter_ = 0;
40 }
41
42 SCM
43 Sequential_iterator::get_music_list () const
44 {
45   Music *m = get_music ();
46   SCM proc = m->get_property ("elements-callback");
47   if (scm_procedure_p (proc))
48     return scm_call_1 (proc, m->self_scm ());
49   else
50     return SCM_EOL;
51 }
52
53 void
54 Sequential_iterator::do_quit ()
55 {
56   if (iter_)
57     iter_->quit ();
58 }
59
60 void
61 Sequential_iterator::derived_mark () const
62 {
63   if (iter_)
64     scm_gc_mark (iter_->self_scm ());
65   scm_gc_mark (cursor_);
66 }
67
68 void
69 Sequential_iterator::derived_substitute (Context *f, Context *t)
70 {
71   if (iter_)
72     iter_->substitute_outlet (f, t);
73 }
74
75 /*
76   TODO: this should be made lazily.
77 */
78 Grace_fixup *
79 create_grace_fixup_list (SCM cursor)
80 {
81   Moment here;
82   Moment last (-1);
83   Grace_fixup *head = 0;
84   Grace_fixup **tail = &head;
85
86   for (; scm_is_pair (cursor); cursor = scm_cdr (cursor))
87     {
88       Music *mus = unsmob_music (scm_car (cursor));
89       Moment s = mus->start_mom ();
90       Moment l = mus->get_length () - s;
91
92       if (s.grace_part_)
93         {
94           if (last != Moment (-1))
95             {
96               Grace_fixup *p = new Grace_fixup;
97               p->start_ = last;
98               p->length_ = here - last;
99               p->grace_start_ = s.grace_part_;
100               p->next_ = 0;
101               *tail = p;
102               tail = &(*tail)->next_;
103             }
104
105           here.grace_part_ = s.grace_part_;
106         }
107
108       if (l.to_bool ())
109         {
110           last = here;
111           here += l;
112         }
113     }
114
115   return head;
116 }
117
118 void
119 Sequential_iterator::construct_children ()
120 {
121   cursor_ = get_music_list ();
122
123   iter_ = 0;
124   if (scm_is_pair (cursor_))
125     {
126       Music *m = unsmob_music (scm_car (cursor_));
127       iter_ = unsmob_iterator (get_iterator (m));
128     }
129
130   while (iter_ && !iter_->ok ())
131     next_element (true);
132
133   last_mom_ = Moment (-1);
134   here_mom_ = get_music ()->start_mom ();
135   grace_fixups_ = create_grace_fixup_list (cursor_);
136
137   /*
138     iter_->ok () is tautology, but what the heck.
139   */
140   if (iter_ && iter_->ok ())
141     descend_to_child (iter_->get_outlet ());
142 }
143
144 /*
145   maintain invariants: change cursor, iter and here_mom_ in one fell
146   swoop.
147 */
148 void
149 Sequential_iterator::next_element (bool)
150 {
151   Moment len = iter_->music_get_length () - iter_->music_start_mom ();
152   assert (!grace_fixups_ || grace_fixups_->start_ >= here_mom_);
153
154   if (len.main_part_
155       && get_grace_fixup ())
156     {
157       Grace_fixup *gf = get_grace_fixup ();
158
159       last_mom_ = here_mom_;
160       here_mom_ += gf->length_;
161       here_mom_.grace_part_ += gf->grace_start_;
162
163       next_grace_fixup ();
164     }
165   else if (len.grace_part_ && !len.main_part_)
166     {
167       last_mom_ = here_mom_;
168       here_mom_.grace_part_ = 0;
169     }
170   else
171     {
172       /*
173         !len.grace_part_ || len.main_part_
174
175         We skip over a big chunk (mainpart != 0). Any starting graces
176         in that chunk should be in len.grace_part_
177
178       */
179       last_mom_ = here_mom_;
180       here_mom_ += len;
181     }
182
183   cursor_ = scm_cdr (cursor_);
184
185   iter_->quit ();
186   if (scm_is_pair (cursor_))
187     iter_ = unsmob_iterator (get_iterator (unsmob_music (scm_car (cursor_))));
188   else
189     iter_ = 0;
190 }
191
192 void
193 Sequential_iterator::process (Moment until)
194 {
195   while (iter_)
196     {
197       Grace_fixup *gf = get_grace_fixup ();
198       if (gf
199           && gf->start_ + gf->length_
200           + Moment (Rational (0), gf->grace_start_) == until)
201         {
202           /*
203             do the stuff/note/rest preceding a grace.
204           */
205           iter_->process (iter_->music_get_length ());
206         }
207       else
208         {
209           Moment w = until - here_mom_ + iter_->music_start_mom ();
210           iter_->process (w);
211         }
212
213       /*
214         if the iter is still OK, there must be events left that have
215
216         TIME > LEFT
217
218       */
219       if (iter_->ok ())
220         return;
221
222       descend_to_child (iter_->get_outlet ());
223       next_element (true);
224     }
225 }
226
227 Moment
228 Sequential_iterator::pending_moment () const
229 {
230   Moment cp = iter_->pending_moment ();
231
232   /*
233     Fix-up a grace note halfway in the music.
234   */
235   Grace_fixup *gf = get_grace_fixup ();
236   if (gf
237       && gf->length_ + iter_->music_start_mom () == cp)
238     return here_mom_ + gf->length_ + Moment (0, gf->grace_start_);
239
240   /*
241     Fix-up a grace note at  the start of the music.
242   */
243   return cp + here_mom_ - iter_->music_start_mom ();
244 }
245
246 bool
247 Sequential_iterator::ok () const
248 {
249   return iter_;
250 }
251
252 IMPLEMENT_CTOR_CALLBACK (Sequential_iterator);
253
254 bool
255 Sequential_iterator::run_always () const
256 {
257   return iter_ ? iter_->run_always () : false;
258 }
259
260 void
261 Sequential_iterator::next_grace_fixup ()
262 {
263   Grace_fixup *n = grace_fixups_->next_;
264   delete grace_fixups_;
265   grace_fixups_ = n;
266 }
267
268 Grace_fixup *
269 Sequential_iterator::get_grace_fixup () const
270 {
271   if (grace_fixups_ && grace_fixups_->start_ == here_mom_)
272     return grace_fixups_;
273   else
274     return 0;
275 }