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