]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Merge remote branch 'origin' into release/unstable
[lilypond.git] / lily / grob.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "grob.hh"
21
22 #include <cstring>
23
24 #include "align-interface.hh"
25 #include "axis-group-interface.hh"
26 #include "input.hh"
27 #include "international.hh"
28 #include "item.hh"
29 #include "main.hh"
30 #include "misc.hh"
31 #include "music.hh"
32 #include "output-def.hh"
33 #include "pointer-group-interface.hh"
34 #include "program-option.hh"
35 #include "stencil.hh"
36 #include "stream-event.hh"
37 #include "system.hh"
38 #include "warn.hh"
39
40 #include "ly-smobs.icc"
41
42 Grob *
43 Grob::clone () const
44 {
45   return new Grob (*this);
46 }
47
48 Grob::Grob (SCM basicprops)
49 {
50
51   /* FIXME: default should be no callback.  */
52   self_scm_ = SCM_EOL;
53   layout_ = 0;
54   original_ = 0;
55   interfaces_ = SCM_EOL;
56   immutable_property_alist_ = basicprops;
57   mutable_property_alist_ = SCM_EOL;
58   object_alist_ = SCM_EOL;
59
60   /* We do smobify_self () as the first step.  Since the object lives
61      on the heap, none of its SCM variables are protected from
62      GC. After smobify_self (), they are.  */
63   smobify_self ();
64
65   SCM meta = get_property ("meta");
66   if (scm_is_pair (meta))
67     {
68       interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
69
70       SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
71       if (scm_is_pair (object_cbs))
72         {
73           for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
74             set_object (scm_caar (s), scm_cdar (s));
75         }
76     }
77
78   if (get_property_data ("X-extent") == SCM_EOL)
79     set_property ("X-extent", Grob::stencil_width_proc);
80   if (get_property_data ("Y-extent") == SCM_EOL)
81     set_property ("Y-extent", Grob::stencil_height_proc);
82 }
83
84 Grob::Grob (Grob const &s)
85   : dim_cache_ (s.dim_cache_)
86 {
87   original_ = (Grob *) & s;
88   self_scm_ = SCM_EOL;
89
90   immutable_property_alist_ = s.immutable_property_alist_;
91   mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
92   interfaces_ = s.interfaces_;
93   object_alist_ = SCM_EOL;
94
95   layout_ = 0;
96
97   smobify_self ();
98 }
99
100 Grob::~Grob ()
101 {
102 }
103 /****************************************************************
104   STENCILS
105 ****************************************************************/
106
107 Stencil *
108 Grob::get_stencil () const
109 {
110   if (!is_live ())
111     return 0;
112
113   SCM stil = get_property ("stencil");
114   return unsmob_stencil (stil);
115 }
116
117 Stencil
118 Grob::get_print_stencil () const
119 {
120   SCM stil = get_property ("stencil");
121
122   Stencil retval;
123   if (Stencil *m = unsmob_stencil (stil))
124     {
125       retval = *m;
126       bool transparent = to_boolean (get_property ("transparent"));
127
128       if (transparent)
129         retval = Stencil (m->extent_box (), SCM_EOL);
130       else
131         {
132           SCM expr = m->expr ();
133           expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
134                              self_scm (), expr);
135
136           retval = Stencil (m->extent_box (), expr);
137         }
138
139       SCM rot = get_property ("rotation");
140       if (scm_is_pair (rot))
141         {
142           Real angle = scm_to_double (scm_car (rot));
143           Real x = scm_to_double (scm_cadr (rot));
144           Real y = scm_to_double (scm_caddr (rot));
145
146           retval.rotate_degrees (angle, Offset (x, y));
147         }
148
149       /* color support... see interpret_stencil_expression () for more... */
150       SCM color = get_property ("color");
151       if (scm_is_pair (color))
152         {
153           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
154                                  color,
155                                  retval.expr ());
156
157           retval = Stencil (retval.extent_box (), expr);
158         }
159
160       /* process whiteout */
161       /* a grob has to be visible, otherwise the whiteout property has no effect */
162       if (!transparent && to_boolean (get_property ("whiteout")))
163         {
164           /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
165           /* to add a round-filled-box stencil to the stencil list */
166           retval
167             = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
168                                            retval.smobbed_copy ()));
169         }
170     }
171
172   return retval;
173 }
174
175 /****************************************************************
176   VIRTUAL STUBS
177 ****************************************************************/
178 void
179 Grob::do_break_processing ()
180 {
181 }
182
183 void
184 Grob::discretionary_processing ()
185 {
186 }
187
188 System *
189 Grob::get_system () const
190 {
191   return 0;
192 }
193
194 /* This version of get_system is more reliable than this->get_system ()
195    before line-breaking has been done, at which point there is only
196    one system in the whole score and we can find it just by following
197    parent pointers. */
198 System *
199 Grob::get_system (Grob *me)
200 {
201   Grob *p = me->get_parent (X_AXIS);
202   return p ? get_system (p) : dynamic_cast<System *>(me);
203 }
204
205 void
206 Grob::handle_broken_dependencies ()
207 {
208   Spanner *sp = dynamic_cast<Spanner *> (this);
209   if (original () && sp)
210     return;
211
212   if (sp)
213     /* THIS, SP is the original spanner.  We use a special function
214        because some Spanners have enormously long lists in their
215        properties, and a special function fixes FOO  */
216     {
217       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
218         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
219     }
220   System *system = get_system ();
221
222   if (is_live ()
223       && system
224       && common_refpoint (system, X_AXIS)
225       && common_refpoint (system, Y_AXIS))
226     substitute_object_links (system->self_scm (), object_alist_);
227   else if (dynamic_cast<System *> (this))
228     substitute_object_links (SCM_UNDEFINED, object_alist_);
229   else
230     /* THIS element is `invalid'; it has been removed from all
231        dependencies, so let's junk the element itself.
232
233        Do not do this for System, since that would remove references
234        to the originals of score-grobs, which get then GC'd (a bad
235        thing).  */
236     suicide ();
237 }
238
239 /* Note that we still want references to this element to be
240    rearranged, and not silently thrown away, so we keep pointers like
241    {broken_into_{drul, array}, original}
242 */
243 void
244 Grob::suicide ()
245 {
246   if (!is_live ())
247     return;
248
249   for (int a = X_AXIS; a < NO_AXES; a++)
250     dim_cache_[a].clear ();
251
252   mutable_property_alist_ = SCM_EOL;
253   object_alist_ = SCM_EOL;
254   immutable_property_alist_ = SCM_EOL;
255   interfaces_ = SCM_EOL;
256 }
257
258 void
259 Grob::handle_prebroken_dependencies ()
260 {
261   /* Don't do this in the derived method, since we want to keep access to
262      object_alist_ centralized.  */
263   if (original ())
264     {
265       Item *it = dynamic_cast<Item *> (this);
266       substitute_object_links (scm_from_int (it->break_status_dir ()),
267                                original ()->object_alist_);
268     }
269 }
270
271 Grob *
272 Grob::find_broken_piece (System *) const
273 {
274   return 0;
275 }
276
277 /****************************************************************
278    OFFSETS
279 ****************************************************************/
280
281 void
282 Grob::translate_axis (Real y, Axis a)
283 {
284   if (isinf (y) || isnan (y))
285     {
286       programming_error (_ ("Infinity or NaN encountered"));
287       return;
288     }
289
290   if (!dim_cache_[a].offset_)
291     dim_cache_[a].offset_ = new Real (y);
292   else
293     *dim_cache_[a].offset_ += y;
294 }
295
296 /* Find the offset relative to D.  If D equals THIS, then it is 0.
297    Otherwise, it recursively defd as
298
299    OFFSET_ + PARENT_L_->relative_coordinate (D) */
300 Real
301 Grob::relative_coordinate (Grob const *refp, Axis a) const
302 {
303   /* eaa - hmmm, should we do a programming_error() here? */
304   if ((this == NULL) || (refp == this))
305     return 0.0;
306
307   /* We catch PARENT_L_ == nil case with this, but we crash if we did
308      not ask for the absolute coordinate (ie. REFP == nil.)  */
309   Real off = get_offset (a);
310   if (refp == dim_cache_[a].parent_)
311     return off;
312
313   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
314
315   return off;
316 }
317
318 Real
319 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
320 {
321   if (refp == this)
322     return 0.0;
323
324   Real off = 0;
325
326   if (dim_cache_[Y_AXIS].offset_)
327     {
328       if (to_boolean (get_property ("pure-Y-offset-in-progress")))
329         programming_error ("cyclic chain in pure-Y-offset callbacks");
330
331       off = *dim_cache_[Y_AXIS].offset_;
332     }
333   else
334     {
335       SCM proc = get_property_data ("Y-offset");
336
337       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
338       set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
339       off = robust_scm2double (call_pure_function (proc,
340                                                    scm_list_1 (self_scm ()),
341                                                    start, end),
342                                0.0);
343       del_property ("pure-Y-offset-in-progress");
344       delete dim_cache_[Y_AXIS].offset_;
345       dim_cache_[Y_AXIS].offset_ = 0;
346     }
347
348   /* we simulate positioning-done if we are the child of a VerticalAlignment,
349      but only if we don't have a cached offset. If we do have a cached offset,
350      it probably means that the Alignment was fixed and it has already been
351      calculated.
352   */
353   if (Grob *p = get_parent (Y_AXIS))
354     {
355       Real trans = 0;
356       if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
357         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
358
359       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
360     }
361   return off;
362 }
363
364 /* Invoke callbacks to get offset relative to parent.  */
365 Real
366 Grob::get_offset (Axis a) const
367 {
368   if (dim_cache_[a].offset_)
369     return *dim_cache_[a].offset_;
370
371   Grob *me = (Grob *) this;
372
373   SCM sym = axis_offset_symbol (a);
374   me->dim_cache_[a].offset_ = new Real (0.0);
375
376   /*
377     UGH: can't fold next 2 statements together. Apparently GCC thinks
378     dim_cache_[a].offset_ is unaliased.
379   */
380   Real off = robust_scm2double (internal_get_property (sym), 0.0);
381   if (me->dim_cache_[a].offset_)
382     {
383       *me->dim_cache_[a].offset_ += off;
384       me->del_property (sym);
385       return *me->dim_cache_[a].offset_;
386     }
387   else
388     return 0.0;
389 }
390
391 Real
392 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
393 {
394   if (pure && a != Y_AXIS)
395     programming_error ("tried to get pure X-offset");
396   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
397          : relative_coordinate (refp, a);
398 }
399
400 /****************************************************************
401   extents
402 ****************************************************************/
403
404 void
405 Grob::flush_extent_cache (Axis axis)
406 {
407   if (dim_cache_[axis].extent_)
408     {
409       /*
410         Ugh, this is not accurate; will flush property, causing
411         callback to be called if.
412        */
413       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
414       delete dim_cache_[axis].extent_;
415       dim_cache_[axis].extent_ = 0;
416       if (get_parent (axis))
417         get_parent (axis)->flush_extent_cache (axis);
418     }
419 }
420
421 Interval
422 Grob::extent (Grob *refp, Axis a) const
423 {
424   Real offset = relative_coordinate (refp, a);
425   Interval real_ext;
426   if (dim_cache_[a].extent_)
427     {
428       real_ext = *dim_cache_[a].extent_;
429     }
430   else
431     {
432       /*
433         Order is significant: ?-extent may trigger suicide.
434        */
435       SCM ext_sym
436         = (a == X_AXIS)
437           ? ly_symbol2scm ("X-extent")
438           : ly_symbol2scm ("Y-extent");
439
440       SCM ext = internal_get_property (ext_sym);
441       if (is_number_pair (ext))
442         real_ext.unite (ly_scm2interval (ext));
443
444       SCM min_ext_sym
445         = (a == X_AXIS)
446           ? ly_symbol2scm ("minimum-X-extent")
447           : ly_symbol2scm ("minimum-Y-extent");
448       SCM min_ext = internal_get_property (min_ext_sym);
449       if (is_number_pair (min_ext))
450         real_ext.unite (ly_scm2interval (min_ext));
451
452       ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
453     }
454
455   real_ext.translate (offset);
456
457   return real_ext;
458 }
459
460 Interval
461 Grob::pure_height (Grob *refp, int start, int end)
462 {
463   SCM iv_scm = get_pure_property ("Y-extent", start, end);
464   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
465   Real offset = pure_relative_y_coordinate (refp, start, end);
466
467   SCM min_ext = get_property ("minimum-Y-extent");
468
469   /* we don't add minimum-Y-extent if the extent is empty. This solves
470      a problem with Hara-kiri spanners. They would request_suicide and
471      return empty extents, but we would force them here to be large. */
472   if (!iv.is_empty () && is_number_pair (min_ext))
473     iv.unite (ly_scm2interval (min_ext));
474
475   if (!iv.is_empty ())
476     iv.translate (offset);
477   return iv;
478 }
479
480 Interval
481 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
482 {
483   if (pure && a != Y_AXIS)
484     programming_error ("tried to get pure width");
485   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
486 }
487
488 Interval_t<int>
489 Grob::spanned_rank_interval () const
490 {
491   return Interval_t<int> (-1, 0);
492 }
493
494 bool
495 Grob::pure_is_visible (int /* start */, int /* end */) const
496 {
497   return true;
498 }
499
500 /* Sort grobs according to their starting column. */
501 bool
502 Grob::less (Grob *g1, Grob *g2)
503 {
504   return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
505 }
506
507 /****************************************************************
508   REFPOINTS
509 ****************************************************************/
510
511 /* Find the group-element which has both #this# and #s#  */
512 Grob *
513 Grob::common_refpoint (Grob const *s, Axis a) const
514 {
515   /* I don't like the quadratic aspect of this code, but I see no
516      other way.  The largest chain of parents might be 10 high or so,
517      so it shouldn't be a real issue.  */
518   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
519     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
520       if (d == c)
521         return (Grob *) d;
522
523   return 0;
524 }
525
526 void
527 Grob::set_parent (Grob *g, Axis a)
528 {
529   dim_cache_[a].parent_ = g;
530 }
531
532 Grob *
533 Grob::get_parent (Axis a) const
534 {
535   return dim_cache_[a].parent_;
536 }
537
538 void
539 Grob::fixup_refpoint ()
540 {
541   for (int a = X_AXIS; a < NO_AXES; a++)
542     {
543       Axis ax = (Axis)a;
544       Grob *parent = get_parent (ax);
545
546       if (!parent)
547         continue;
548
549       if (parent->get_system () != get_system () && get_system ())
550         {
551           Grob *newparent = parent->find_broken_piece (get_system ());
552           set_parent (newparent, ax);
553         }
554
555       if (Item *i = dynamic_cast<Item *> (this))
556         {
557           Item *parenti = dynamic_cast<Item *> (parent);
558
559           if (parenti && i)
560             {
561               Direction my_dir = i->break_status_dir ();
562               if (my_dir != parenti->break_status_dir ())
563                 {
564                   Item *newparent = parenti->find_prebroken_piece (my_dir);
565                   set_parent (newparent, ax);
566                 }
567             }
568         }
569     }
570 }
571
572 /****************************************************************
573   MESSAGES
574 ****************************************************************/
575 void
576 Grob::programming_error (string s) const
577 {
578   SCM cause = self_scm ();
579   while (Grob *g = unsmob_grob (cause))
580     cause = g->get_property ("cause");
581
582   /* ES TODO: cause can't be Music*/
583   if (Music *m = unsmob_music (cause))
584     m->origin ()->programming_error (s);
585   else if (Stream_event *ev = unsmob_stream_event (cause))
586     ev->origin ()->programming_error (s);
587   else
588     ::programming_error (s);
589 }
590
591 void
592 Grob::warning (string s) const
593 {
594   SCM cause = self_scm ();
595   while (Grob *g = unsmob_grob (cause))
596     cause = g->get_property ("cause");
597
598   /* ES TODO: cause can't be Music*/
599   if (Music *m = unsmob_music (cause))
600     m->origin ()->warning (s);
601   else if (Stream_event *ev = unsmob_stream_event (cause))
602     ev->origin ()->warning (s);
603   else
604     ::warning (s);
605 }
606
607 string
608 Grob::name () const
609 {
610   SCM meta = get_property ("meta");
611   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
612   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
613   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
614 }
615
616 ADD_INTERFACE (Grob,
617                "A grob represents a piece of music notation.\n"
618                "\n"
619                "All grobs have an X and Y@tie{}position on the page.  These"
620                " X and Y@tie{}positions are stored in a relative format, thus"
621                " they can easily be combined by stacking them, hanging one"
622                " grob to the side of another, or coupling them into grouping"
623                " objects.\n"
624                "\n"
625                "Each grob has a reference point (a.k.a.@: parent): The"
626                " position of a grob is stored relative to that reference"
627                " point.  For example, the X@tie{}reference point of a staccato"
628                " dot usually is the note head that it applies to.  When the"
629                " note head is moved, the staccato dot moves along"
630                " automatically.\n"
631                "\n"
632                "A grob is often associated with a symbol, but some grobs do"
633                " not print any symbols.  They take care of grouping objects."
634                " For example, there is a separate grob that stacks staves"
635                " vertically.  The @ref{NoteCollision} object is also an"
636                " abstract grob: It only moves around chords, but doesn't print"
637                " anything.\n"
638                "\n"
639                "Grobs have properties (Scheme variables) that can be read and"
640                " set.  Two types of them exist: immutable and mutable."
641                "  Immutable variables define the default style and behavior."
642                "  They are shared between many objects.  They can be changed"
643                " using @code{\\override} and @code{\\revert}.  Mutable"
644                " properties are variables that are specific to one grob."
645                "  Typically, lists of other objects, or results from"
646                " computations are stored in mutable properties.  In"
647                " particular, every call to @code{ly:grob-set-property!}"
648                " (or its C++ equivalent) sets a mutable property.\n"
649                "\n"
650                "The properties @code{after-line-breaking} and"
651                " @code{before-line-breaking} are dummies that are not"
652                " user-serviceable.",
653
654                /* properties */
655                "X-extent "
656                "X-offset "
657                "Y-extent "
658                "Y-offset "
659                "after-line-breaking "
660                "avoid-slur "
661                "axis-group-parent-X "
662                "axis-group-parent-Y "
663                "before-line-breaking "
664                "cause "
665                "color "
666                "cross-staff "
667                "extra-X-extent "
668                "extra-Y-extent "
669                "extra-offset "
670                "interfaces "
671                "layer "
672                "meta "
673                "minimum-X-extent "
674                "minimum-Y-extent "
675                "outside-staff-horizontal-padding "
676                "outside-staff-padding "
677                "outside-staff-priority "
678                "pure-Y-offset-in-progress "
679                "rotation "
680                "springs-and-rods "
681                "staff-symbol "
682                "stencil "
683                "transparent "
684                "whiteout "
685               );
686
687 /****************************************************************
688   CALLBACKS
689 ****************************************************************/
690
691 static SCM
692 grob_stencil_extent (Grob *me, Axis a)
693 {
694   Stencil *m = me->get_stencil ();
695   Interval e;
696   if (m)
697     e = m->extent (a);
698   return ly_interval2scm (e);
699 }
700
701 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
702 SCM
703 Grob::stencil_height (SCM smob)
704 {
705   Grob *me = unsmob_grob (smob);
706   return grob_stencil_extent (me, Y_AXIS);
707 }
708
709 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
710 SCM
711 Grob::y_parent_positioning (SCM smob)
712 {
713   Grob *me = unsmob_grob (smob);
714   Grob *par = me->get_parent (Y_AXIS);
715   if (par)
716     (void) par->get_property ("positioning-done");
717
718   return scm_from_double (0.0);
719 }
720
721 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
722 SCM
723 Grob::x_parent_positioning (SCM smob)
724 {
725   Grob *me = unsmob_grob (smob);
726
727   Grob *par = me->get_parent (X_AXIS);
728   if (par)
729     (void) par->get_property ("positioning-done");
730
731   return scm_from_double (0.0);
732 }
733
734 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
735 SCM
736 Grob::stencil_width (SCM smob)
737 {
738   Grob *me = unsmob_grob (smob);
739   return grob_stencil_extent (me, X_AXIS);
740 }
741
742 Grob *
743 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
744 {
745   for (; scm_is_pair (elist); elist = scm_cdr (elist))
746     if (Grob *s = unsmob_grob (scm_car (elist)))
747       {
748         if (common)
749           common = common->common_refpoint (s, a);
750         else
751           common = s;
752       }
753
754   return common;
755 }
756
757 Grob *
758 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
759 {
760   for (vsize i = 0; i < arr.size (); i++)
761     if (common)
762       common = common->common_refpoint (arr[i], a);
763     else
764       common = arr[i];
765
766   return common;
767 }
768
769 Interval
770 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
771 {
772   Interval ext = me->extent (refpoint, a);
773   if (ext.is_empty ())
774     ext.add_point (me->relative_coordinate (refpoint, a));
775
776   return ext;
777 }
778
779 // Checks whether there is a vertical alignment in the chain of
780 // parents between this and commony.
781 bool
782 Grob::check_cross_staff (Grob *commony)
783 {
784   if (Align_interface::has_interface (commony))
785     return true;
786
787   for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
788     if (Align_interface::has_interface (g))
789       return true;
790
791   return false;
792 }
793