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