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