]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* lily/paper-book.cc (split_string): new function
[lilypond.git] / lily / grob.cc
1 /*
2   grob.cc -- implement Grob
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9
10 #include <string.h>
11 #include <math.h>
12
13 #include "main.hh"
14 #include "input-smob.hh"
15 #include "warn.hh"
16 #include "group-interface.hh"
17 #include "misc.hh"
18 #include "paper-score.hh"
19 #include "stencil.hh"
20 #include "grob.hh"
21 #include "warn.hh"
22 #include "spanner.hh"
23 #include "system.hh"
24 #include "item.hh"
25 #include "stencil.hh"
26 #include "misc.hh"
27 #include "music.hh"
28 #include "item.hh"
29
30 #include "ly-smobs.icc"
31
32 /*
33 TODO:
34
35 remove dynamic_cast<Spanner,Item> and put this code into respective
36   subclass.
37 */
38
39 #define HASH_SIZE 3
40 #define INFINITY_MSG "Infinity or NaN encountered"
41
42 Grob::Grob (SCM basicprops)
43 {
44   /*
45     fixme: default should be no callback.
46    */
47   self_scm_ = SCM_EOL;
48   pscore_=0;
49   status_ = 0;
50   original_ = 0;
51   immutable_property_alist_ =  basicprops;
52   mutable_property_alist_ = SCM_EOL;
53
54   /*
55     We do smobify_self () as the first step. Since the object lives on
56     the heap, none of its SCM variables are protected from GC. After
57     smobify_self (), they are.
58    */
59   smobify_self ();
60
61
62   
63   SCM meta = get_property ("meta");
64   if (ly_c_pair_p (meta))
65     {
66       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
67
68       /*
69         Switch off interface checks for the moment.
70        */
71       bool itc = internal_type_checking_global_b;
72       internal_type_checking_global_b = false;
73       internal_set_property (ly_symbol2scm ("interfaces"), ly_cdr (ifs));
74       internal_type_checking_global_b = itc;
75     }
76   
77   /*
78     TODO:
79
80     destill this into a function, so we can re-init the immutable
81     properties with a new BASICPROPS value after creation. Convenient
82     eg. when using \override with StaffSymbol.  */
83   
84   char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
85   char const*xnames[] = {"X-extent", "Y-extent"};
86   char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
87   
88   for (int a = X_AXIS; a <= Y_AXIS; a++)
89     {
90       SCM l = get_property (onames[a]);
91
92       if (scm_ilength (l) >=0)
93         {
94           dim_cache_[a].offset_callbacks_ = l;
95           dim_cache_[a].offsets_left_ = scm_ilength (l);
96         }
97       else
98         {
99           programming_error ("[XY]-offset-callbacks must be a list");
100         }
101
102       SCM cb = get_property (enames[a]);
103       SCM xt = get_property (xnames[a]);
104       
105       /*
106         Should change default to empty? 
107       */
108       if (is_number_pair (xt))
109         cb = xt;
110       else if (cb != SCM_BOOL_F
111           && !ly_c_procedure_p (cb) && !ly_c_pair_p (cb)
112           && ly_c_procedure_p (get_property ("print-function")))
113         cb = stencil_extent_proc;
114     
115       dim_cache_[a].dimension_ = cb;
116     }
117
118 }
119
120 Grob::Grob (Grob const&s)
121    : dim_cache_ (s.dim_cache_)
122 {
123   original_ = (Grob*) &s;
124   self_scm_ = SCM_EOL;
125
126   immutable_property_alist_ = s.immutable_property_alist_;
127   mutable_property_alist_ = SCM_EOL;
128   
129   /*
130     No properties are copied. That is the job of handle_broken_dependencies.
131    */
132   
133   status_ = s.status_;
134   pscore_ = 0;
135
136   smobify_self ();
137 }
138
139 Grob::~Grob ()
140 {
141 }
142
143
144 MAKE_SCHEME_CALLBACK (Grob,stencil_extent,2);
145 SCM
146 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
147 {
148   Grob *s = unsmob_grob (element_smob);
149   Axis a = (Axis) ly_scm2int (scm_axis);
150
151   Stencil *m = s->get_stencil ();
152   Interval e ;
153   if (m)
154     e = m->extent (a);
155   return ly_interval2scm (e);
156 }
157
158 Output_def*
159 Grob::get_paper ()  const
160 {
161  return pscore_ ? pscore_->paper_ : 0;
162 }
163
164
165 /*
166   Recursively track all dependencies of this Grob.  The
167   status_ field is used as a mark-field.  It is marked with
168   BUSY during execution of this function, and marked with FINAL
169   when finished.
170
171   FUNCPTR is the function to call to update this element.
172 */
173 void
174 Grob::calculate_dependencies (int final, int busy, SCM funcname)
175 {
176   if (status_ >= final)
177     return;
178
179   if (status_ == busy)
180     {
181       programming_error ("Element is busy, come back later");
182       return;
183     }
184   
185   status_= busy;
186
187   for (SCM d = get_property ("dependencies"); ly_c_pair_p (d);
188        d = ly_cdr (d))
189     {
190       unsmob_grob (ly_car (d))
191         ->calculate_dependencies (final, busy, funcname);
192     }
193
194   
195   SCM proc = internal_get_property (funcname);
196   if (ly_c_procedure_p (proc))
197     scm_call_1 (proc, this->self_scm ());
198  
199   status_ = final;
200 }
201
202 Stencil *
203 Grob::get_stencil ()  const
204 {
205   if (!is_live ())
206     {
207       return 0;
208     }
209   
210   SCM mol = get_property ("stencil");
211   if (unsmob_stencil (mol))
212     return unsmob_stencil (mol);
213
214   mol = get_uncached_stencil ();
215   
216   if (is_live ())
217     {
218       Grob *me = (Grob*)this;
219       me->set_property ("stencil", mol);
220     }
221   
222   return unsmob_stencil (mol);  
223 }
224
225 SCM
226 Grob::get_uncached_stencil ()const
227 {
228   SCM proc = get_property ("print-function");
229
230   SCM  mol = SCM_EOL;
231   if (ly_c_procedure_p (proc)) 
232     mol = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
233   
234   if (Stencil *m = unsmob_stencil (mol))
235      {
236       if (to_boolean (get_property ("transparent")))
237         mol = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
238     else
239       {
240         SCM origin = ly_symbol2scm ("no-origin");
241       
242         if (store_locations_global_b)
243           {
244             SCM cause = get_property ("cause");
245             if (Music*m = unsmob_music (cause))
246               {
247                 SCM music_origin = m->get_property ("origin");
248                 if (unsmob_input (music_origin))
249                   origin = music_origin;
250               }
251           }
252
253         // ugr.
254         mol = Stencil (m->extent_box (),
255                        scm_list_n (origin, m->expr (), SCM_UNDEFINED)
256                        ). smobbed_copy ();
257       }
258     }  
259
260   return mol;
261 }
262
263 /*
264   
265   VIRTUAL STUBS
266
267  */
268 void
269 Grob::do_break_processing ()
270 {
271 }
272
273 System *
274 Grob::get_system () const
275 {
276   return 0;
277 }
278
279 void
280 Grob::add_dependency (Grob*e)
281 {
282   if (e)
283     {
284       Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
285     }
286   else
287     programming_error ("Null dependency added");
288 }
289
290 void
291 Grob::handle_broken_dependencies ()
292 {
293   Spanner * sp = dynamic_cast<Spanner*> (this);
294   if (original_ && sp)
295     return;
296
297   if (sp)
298     {
299       /*
300         This is the original spanner. We use a special function
301         because some Spanners have enormously long lists in their
302         properties.
303        */
304       for (SCM s = mutable_property_alist_; ly_c_pair_p (s);
305            s = ly_cdr (s))
306         {
307           sp->substitute_one_mutable_property (ly_caar (s),
308                                               ly_cdar (s));
309         }
310     }
311
312   System *system = get_system ();
313
314   if (is_live ()
315       && system && common_refpoint (system, X_AXIS) && common_refpoint (system, Y_AXIS))
316     {
317       substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED,
318                                      mutable_property_alist_);
319     }
320   else if (dynamic_cast <System*> (this))
321     {
322       substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
323     }
324   else
325     {
326       /*
327         This element is `invalid'; it has been removed from all
328         dependencies, so let's junk the element itself.
329
330         do not do this for System, since that would remove references
331         to the originals of score-grobs, which get then GC'd (a bad
332         thing.)
333  
334       */
335       suicide ();
336     }
337 }
338
339 /*
340  Note that we still want references to this element to be
341  rearranged, and not silently thrown away, so we keep pointers
342  like {broken_into_{drul,array}, original}
343 */
344 void
345 Grob::suicide ()
346 {
347   if (!is_live ())
348     return; 
349
350   
351   mutable_property_alist_ = SCM_EOL;
352   immutable_property_alist_ = SCM_EOL;
353
354   set_extent (SCM_EOL, Y_AXIS);
355   set_extent (SCM_EOL, X_AXIS);
356
357   for (int a= X_AXIS; a <= Y_AXIS; a++)
358     {
359       dim_cache_[a].offset_callbacks_ = SCM_EOL;
360       dim_cache_[a].offsets_left_ = 0;
361     }
362 }
363
364 void
365 Grob::handle_prebroken_dependencies ()
366 {
367   /*
368     Don't do this in the derived method, since we want to keep access to
369     mutable_property_alist_ centralized.
370    */
371   if (original_)
372     {
373       Item * it = dynamic_cast<Item*> (this);
374       substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
375                                original_->mutable_property_alist_);
376     }
377 }
378
379 Grob*
380 Grob::find_broken_piece (System*) const
381 {
382   return 0;
383 }
384
385 /*
386   translate in one direction
387 */
388 void
389 Grob::translate_axis (Real y, Axis a)
390 {
391   if (isinf (y) || isnan (y))
392     programming_error (_ (INFINITY_MSG));
393   else
394     {
395       dim_cache_[a].offset_ += y;
396     }
397 }  
398
399
400 /*
401   Find the offset relative to D.  If   D equals THIS, then it is 0.
402   Otherwise, it recursively defd as
403   
404   OFFSET_ + PARENT_L_->relative_coordinate (D)
405 */
406 Real
407 Grob::relative_coordinate (Grob const*refp, Axis a) const
408 {
409   if (refp == this)
410     return 0.0;
411
412   /*
413     We catch PARENT_L_ == nil case with this, but we crash if we did
414     not ask for the absolute coordinate (ie. REFP == nil.)
415     
416    */
417   if (refp == dim_cache_[a].parent_)
418     return get_offset (a);
419   else
420     return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
421 }
422
423
424   
425 /*
426   Invoke callbacks to get offset relative to parent.
427 */
428 Real
429 Grob::get_offset (Axis a) const
430 {
431   Grob *me = (Grob*) this;
432   while (dim_cache_[a].offsets_left_)
433     {
434       int l = --me->dim_cache_[a].offsets_left_;
435       SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_,  scm_int2num (l));
436       SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
437
438       Real r =  ly_scm2double (retval);
439       if (isinf (r) || isnan (r))
440         {
441           programming_error (INFINITY_MSG);
442           r = 0.0;
443         }
444       me->dim_cache_[a].offset_ +=r;
445     }
446   return dim_cache_[a].offset_;
447 }
448
449
450 bool
451 Grob::is_empty (Axis a)const
452 {
453   return ! (ly_c_pair_p (dim_cache_[a].dimension_) ||
454             ly_c_procedure_p (dim_cache_[a].dimension_));
455 }
456
457 Interval
458 Grob::extent (Grob * refp, Axis a) const
459 {
460   Real x = relative_coordinate (refp, a);
461
462   
463   Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
464   Interval ext ;   
465   if (ly_c_pair_p (d->dimension_))
466     ;
467   else if (ly_c_procedure_p (d->dimension_))
468     {
469       /*
470         FIXME: add doco on types, and should typecheck maybe? 
471        */
472       d->dimension_= scm_call_2 (d->dimension_, self_scm (), scm_int2num (a));
473     }
474   else
475     return ext;
476
477   if (!ly_c_pair_p (d->dimension_))
478     return ext;
479   
480   ext = ly_scm2interval (d->dimension_);
481
482   SCM extra = get_property (a == X_AXIS
483                                 ? "extra-X-extent"
484                                 : "extra-Y-extent");
485
486   /*
487     signs ?
488    */
489   if (ly_c_pair_p (extra))
490     {
491       ext[BIGGER] +=  ly_scm2double (ly_cdr (extra));
492       ext[SMALLER] +=   ly_scm2double (ly_car (extra));
493     }
494   
495   extra = get_property (a == X_AXIS
496                                 ? "minimum-X-extent"
497                                 : "minimum-Y-extent");
498   if (ly_c_pair_p (extra))
499     {
500       ext.unite (Interval (ly_scm2double (ly_car (extra)),
501                            ly_scm2double (ly_cdr (extra))));
502     }
503
504   ext.translate (x);
505   
506   return ext;
507 }
508
509 /*
510   Find the group-element which has both #this# and #s#
511 */
512 Grob * 
513 Grob::common_refpoint (Grob const* s, Axis a) const
514 {
515   /*
516     I don't like the quadratic aspect of this code, but I see no other
517     way. The largest chain of parents might be 10 high or so, so
518     it shouldn't be a real issue. */
519   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
520     for (Grob const * d = s; d; d = d->dim_cache_[a].parent_)
521       if (d == c)
522         return (Grob*)d;
523
524   return 0;
525 }
526
527
528 Grob *
529 common_refpoint_of_list (SCM elist, Grob *common, Axis a) 
530 {
531   for (; ly_c_pair_p (elist); elist = ly_cdr (elist))
532     {
533       Grob * s = unsmob_grob (ly_car (elist));
534       if (!s)
535         continue;
536       if (common)
537         common = common->common_refpoint (s, a);
538       else
539         common = s;
540     }
541
542   return common;
543 }
544
545
546
547 Grob *
548 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a) 
549 {
550   for (int i = arr.size () ; i--; )
551     {
552       Grob * s = arr[i];
553       if (!s)
554         continue;
555
556       if (common)
557         common = common->common_refpoint (s, a);
558       else
559         common = s;
560     }
561
562   return common;
563 }
564
565 String
566 Grob::name () const
567 {
568   SCM meta = get_property ("meta");
569   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
570   nm = (ly_c_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
571   return  ly_c_symbol_p (nm) ? ly_symbol2string (nm) :  classname (this);  
572 }
573
574 void
575 Grob::add_offset_callback (SCM cb, Axis a)
576 {
577   if (!has_offset_callback (cb, a))
578   {
579     dim_cache_[a].offset_callbacks_ = scm_cons (cb, dim_cache_[a].offset_callbacks_);
580     dim_cache_[a].offsets_left_ ++;
581   }
582 }
583
584 bool
585 Grob::has_extent_callback (SCM cb, Axis a)const
586 {
587   return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
588 }
589
590
591 bool
592 Grob::has_offset_callback (SCM cb, Axis a)const
593 {
594   return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
595 }
596
597 void
598 Grob::set_extent (SCM dc, Axis a)
599 {
600   dim_cache_[a].dimension_ = dc;
601 }
602
603 void
604 Grob::set_parent (Grob *g, Axis a)
605 {
606   dim_cache_[a].parent_ = g;
607 }
608
609 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
610 SCM
611 Grob::fixup_refpoint (SCM smob)
612 {
613   Grob *me = unsmob_grob (smob);
614   for (int a = X_AXIS; a < NO_AXES; a ++)
615     {
616       Axis ax = (Axis)a;
617       Grob * parent = me->get_parent (ax);
618
619       if (!parent)
620         continue;
621       
622       if (parent->get_system () != me->get_system () && me->get_system ())
623         {
624           Grob * newparent = parent->find_broken_piece (me->get_system ());
625           me->set_parent (newparent, ax);
626         }
627
628       if (Item * i  = dynamic_cast<Item*> (me))
629         {
630           Item *parenti = dynamic_cast<Item*> (parent);
631
632           if (parenti && i)
633             {
634               Direction  my_dir = i->break_status_dir () ;
635               if (my_dir!= parenti->break_status_dir ())
636                 {
637                   Item *newparent =  parenti->find_prebroken_piece (my_dir);
638                   me->set_parent (newparent, ax);
639                 }
640             }
641         }
642     }
643   return smob;
644 }
645
646 void
647 Grob::warning (String s)const
648 {
649   SCM cause = self_scm ();
650   while (Grob * g = unsmob_grob (cause))
651     {
652       cause = g->get_property ("cause");
653     }
654
655   if (Music *m = unsmob_music (cause))
656     {
657       m->origin ()->warning (s);
658     }
659   else
660     ::warning (s);
661 }
662
663 void
664 Grob::programming_error (String s)const
665 {
666   s = "Programming error: "  + s;
667   warning (s);
668 }
669
670
671 /****************************************************
672   SMOB funcs
673  ****************************************************/
674
675
676
677 IMPLEMENT_SMOBS (Grob);
678 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
679
680 SCM
681 Grob::mark_smob (SCM ses)
682 {
683   Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
684   scm_gc_mark (s->immutable_property_alist_);
685
686   for (int a =0 ; a < 2; a++)
687     {
688       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
689       scm_gc_mark (s->dim_cache_[a].dimension_);
690       
691       /*
692         don't mark the parents. The pointers in the mutable property
693         list form two tree like structures (one for X relations, one
694         for Y relations). Marking these can be done in limited stack
695         space.  If we add the parents, we will jump between X and Y in
696         an erratic manner, leading to much more recursion depth (and
697         core dumps if we link to pthreads.)
698        */
699     }
700   
701   if (s->original_)
702     scm_gc_mark (s->original_->self_scm ());
703
704   s->do_derived_mark ();  
705   return s->mutable_property_alist_;
706 }
707
708 int
709 Grob::print_smob (SCM s, SCM port, scm_print_state *)
710 {
711   Grob *sc = (Grob *) ly_cdr (s);
712      
713   scm_puts ("#<Grob ", port);
714   scm_puts ((char *)sc->name ().to_str0 (), port);
715
716   /*
717     don't try to print properties, that is too much hassle.
718    */
719   scm_puts (" >", port);
720   return 1;
721 }
722
723 SCM
724 Grob::do_derived_mark () const
725 {
726   return SCM_EOL;
727 }
728
729
730
731 void
732 Grob::discretionary_processing ()
733 {
734 }
735
736 bool
737 Grob::internal_has_interface (SCM k)
738 {
739   SCM ifs = get_property ("interfaces");
740
741   return scm_c_memq (k, ifs) != SCM_BOOL_F;
742 }
743
744
745 /** Return Array of Grobs in SCM list L */
746 Link_array<Grob>
747 ly_scm2grobs (SCM l)
748 {
749   Link_array<Grob> arr;
750
751   for (SCM s = l; ly_c_pair_p (s); s = ly_cdr (s))
752     {
753       SCM e = ly_car (s);
754       arr.push (unsmob_grob (e));
755     }
756
757   arr.reverse ();
758   return arr;
759 }
760
761 /** Return SCM list of Grob array A */
762 SCM
763 ly_grobs2scm (Link_array<Grob> a)
764 {
765   SCM s = SCM_EOL;
766   for (int i = a.size (); i; i--)
767     s = scm_cons (a[i-1]->self_scm (), s);
768
769   return s;
770 }
771
772
773 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
774
775 ADD_INTERFACE (Grob, "grob-interface",
776                "A grob represents a piece of music notation\n"
777                "\n"
778 "All grobs have an X and Y-position on the page.  These X and Y positions\n"
779 "are stored in a relative format, so they can easily be combined by\n"
780 "stacking them, hanging one grob to the side of another, and coupling\n"
781 "them into a grouping objects.\n"
782 "\n"
783 "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
784 "is stored relative to that reference point. For example the X-reference\n"
785 "point of a staccato dot usually is the note head that it applies\n"
786 "to. When the note head is moved, the staccato dot moves along\n"
787 "automatically.\n"
788 "\n"
789 "A grob is often associated with a symbol, but some grobs do not print\n"
790 "any symbols. They take care of grouping objects. For example, there is a\n"
791 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
792 "is also an abstract grob: it only moves around chords, but doesn't print\n"
793 "anything.\n"
794 "\n"
795                "Grobs have a properties: Scheme variables, that can be read and set. "
796                "They have two types. Immutable variables "
797                "define the default style and behavior.  They are shared between  many objects. "
798                "They can be changed using @code{\\override} and @code{\\revert}. "
799                "\n\n"
800                "Mutable properties are variables that are specific to one grob. Typically, "
801                "lists of other objects, or results from computations are stored in"
802                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
803                "sets a mutable property. " 
804
805 ,
806                "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
807                "Y-extent-callback print-function extra-offset spacing-procedure "
808                "staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
809                "meta layer before-line-breaking-callback "
810                "after-line-breaking-callback extra-Y-extent minimum-X-extent "
811                // FIXME: page-penalty?
812                "minimum-Y-extent page-penalty transparent "
813                );
814
815