]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Merge branch 'lilypond/translation' of ssh://git.sv.gnu.org/srv/git/lilypond into...
[lilypond.git] / lily / grob.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2010 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
195 void
196 Grob::handle_broken_dependencies ()
197 {
198   Spanner *sp = dynamic_cast<Spanner *> (this);
199   if (original () && sp)
200     return;
201
202   if (sp)
203     /* THIS, SP is the original spanner.  We use a special function
204        because some Spanners have enormously long lists in their
205        properties, and a special function fixes FOO  */
206     {
207       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
208         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
209     }
210   System *system = get_system ();
211
212   if (is_live ()
213       && system
214       && common_refpoint (system, X_AXIS)
215       && common_refpoint (system, Y_AXIS))
216     substitute_object_links (system->self_scm (), object_alist_);
217   else if (dynamic_cast<System *> (this))
218     substitute_object_links (SCM_UNDEFINED, object_alist_);
219   else
220     /* THIS element is `invalid'; it has been removed from all
221        dependencies, so let's junk the element itself.
222
223        Do not do this for System, since that would remove references
224        to the originals of score-grobs, which get then GC'd (a bad
225        thing).  */
226     suicide ();
227 }
228
229 /* Note that we still want references to this element to be
230    rearranged, and not silently thrown away, so we keep pointers like
231    {broken_into_{drul, array}, original}
232 */
233 void
234 Grob::suicide ()
235 {
236   if (!is_live ())
237     return;
238
239   for (int a = X_AXIS; a < NO_AXES; a++)
240     dim_cache_[a].clear ();
241
242   mutable_property_alist_ = SCM_EOL;
243   object_alist_ = SCM_EOL;
244   immutable_property_alist_ = SCM_EOL;
245   interfaces_ = SCM_EOL;
246 }
247
248 void
249 Grob::handle_prebroken_dependencies ()
250 {
251   /* Don't do this in the derived method, since we want to keep access to
252      object_alist_ centralized.  */
253   if (original ())
254     {
255       Item *it = dynamic_cast<Item *> (this);
256       substitute_object_links (scm_from_int (it->break_status_dir ()),
257                                original ()->object_alist_);
258     }
259 }
260
261 Grob *
262 Grob::find_broken_piece (System *) const
263 {
264   return 0;
265 }
266
267 /****************************************************************
268    OFFSETS
269 ****************************************************************/
270
271 void
272 Grob::translate_axis (Real y, Axis a)
273 {
274   if (isinf (y) || isnan (y))
275     {
276       programming_error (_ ("Infinity or NaN encountered"));
277       return ;
278     }
279   
280   if (!dim_cache_[a].offset_)
281     dim_cache_[a].offset_ = new Real (y);
282   else
283     *dim_cache_[a].offset_ += y;  
284 }
285
286 /* Find the offset relative to D.  If D equals THIS, then it is 0.
287    Otherwise, it recursively defd as
288
289    OFFSET_ + PARENT_L_->relative_coordinate (D) */
290 Real
291 Grob::relative_coordinate (Grob const *refp, Axis a) const
292 {
293   /* eaa - hmmm, should we do a programming_error() here? */
294   if ((this == NULL) || (refp == this))
295     return 0.0;
296
297   /* We catch PARENT_L_ == nil case with this, but we crash if we did
298      not ask for the absolute coordinate (ie. REFP == nil.)  */
299   Real off = get_offset (a);
300   if (refp == dim_cache_[a].parent_)
301     return off;
302   
303   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
304
305   return off;
306 }
307
308 Real
309 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
310 {
311   if (refp == this)
312     return 0.0;
313
314   Real off = 0;
315
316   if (dim_cache_[Y_AXIS].offset_)
317     {
318       if (to_boolean (get_property ("pure-Y-offset-in-progress")))
319         programming_error ("cyclic chain in pure-Y-offset callbacks");
320
321       off = *dim_cache_[Y_AXIS].offset_;
322     }
323   else
324     {
325       SCM proc = get_property_data ("Y-offset");
326
327       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
328       set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
329       off = robust_scm2double (call_pure_function (proc,
330                                                    scm_list_1 (self_scm ()),
331                                                    start, end),
332                                0.0);
333       del_property ("pure-Y-offset-in-progress");
334       delete dim_cache_[Y_AXIS].offset_;
335       dim_cache_[Y_AXIS].offset_ = 0;
336     }
337
338   /* we simulate positioning-done if we are the child of a VerticalAlignment,
339      but only if we don't have a cached offset. If we do have a cached offset,
340      it probably means that the Alignment was fixed and it has already been
341      calculated.
342   */
343   if (Grob *p = get_parent (Y_AXIS))
344     {
345       Real trans = 0;
346       if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
347         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
348
349       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
350     }
351   return off;
352 }
353
354 /* Invoke callbacks to get offset relative to parent.  */
355 Real
356 Grob::get_offset (Axis a) const
357 {
358   if (dim_cache_[a].offset_)
359     return *dim_cache_[a].offset_;
360
361   Grob *me = (Grob *) this;
362
363   SCM sym = axis_offset_symbol (a);
364   me->dim_cache_[a].offset_ = new Real (0.0);
365
366   /*
367     UGH: can't fold next 2 statements together. Apparently GCC thinks
368     dim_cache_[a].offset_ is unaliased.
369   */
370   Real off = robust_scm2double (internal_get_property (sym), 0.0);
371   if (me->dim_cache_[a].offset_)
372     {
373       *me->dim_cache_[a].offset_ += off;
374       me->del_property (sym);
375       return *me->dim_cache_[a].offset_;
376     }
377   else
378     return 0.0;
379 }
380
381 Real
382 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
383 {
384   if (pure && a != Y_AXIS)
385     programming_error ("tried to get pure X-offset");
386   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
387     : relative_coordinate (refp, a);
388 }
389
390 /****************************************************************
391   extents
392 ****************************************************************/
393
394 void
395 Grob::flush_extent_cache (Axis axis)
396 {
397   if (dim_cache_[axis].extent_)
398     {
399       /*
400         Ugh, this is not accurate; will flush property, causing
401         callback to be called if.
402        */
403       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
404       delete dim_cache_[axis].extent_;
405       dim_cache_[axis].extent_ = 0;
406       if (get_parent (axis))
407         get_parent (axis)->flush_extent_cache (axis);
408     }
409 }
410
411
412 Interval
413 Grob::extent (Grob *refp, Axis a) const
414 {
415   Real offset = relative_coordinate (refp, a);
416   Interval real_ext;
417   if (dim_cache_[a].extent_)
418     {
419       real_ext = *dim_cache_[a].extent_;
420     }
421   else
422     {
423       /*
424         Order is significant: ?-extent may trigger suicide.
425        */
426       SCM ext_sym =
427         (a == X_AXIS)
428         ? ly_symbol2scm ("X-extent")
429         : ly_symbol2scm ("Y-extent");
430         
431       SCM ext = internal_get_property (ext_sym);
432       if (is_number_pair (ext))
433         real_ext.unite (ly_scm2interval (ext));
434
435       SCM min_ext_sym =
436         (a == X_AXIS)
437         ? ly_symbol2scm ("minimum-X-extent")
438         : ly_symbol2scm ("minimum-Y-extent");
439       SCM min_ext = internal_get_property (min_ext_sym);
440       if (is_number_pair (min_ext))
441         real_ext.unite (ly_scm2interval (min_ext));
442
443       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
444     }
445   
446   real_ext.translate (offset);
447   
448   return real_ext;
449 }
450
451 Interval
452 Grob::pure_height (Grob *refp, int start, int end)
453 {
454   SCM iv_scm = get_pure_property ("Y-extent", start, end);
455   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
456   Real offset = pure_relative_y_coordinate (refp, start, end);
457
458   SCM min_ext = get_property ("minimum-Y-extent");
459
460   /* we don't add minimum-Y-extent if the extent is empty. This solves
461      a problem with Hara-kiri spanners. They would request_suicide and
462      return empty extents, but we would force them here to be large. */
463   if (!iv.is_empty () && is_number_pair (min_ext))
464     iv.unite (ly_scm2interval (min_ext));
465
466   if (!iv.is_empty ())
467     iv.translate (offset);
468   return iv;
469 }
470
471 Interval
472 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
473 {
474   if (pure && a != Y_AXIS)
475     programming_error ("tried to get pure width");
476   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
477 }
478
479 Interval_t<int>
480 Grob::spanned_rank_interval () const
481 {
482   return Interval_t<int> (-1, 0);
483 }
484
485 bool
486 Grob::pure_is_visible (int start, int end) const
487 {
488   return true;
489 }
490
491 /* Sort grobs according to their starting column. */
492 bool
493 Grob::less (Grob *g1, Grob *g2)
494 {
495   return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
496 }
497
498 /****************************************************************
499   REFPOINTS
500 ****************************************************************/
501
502 /* Find the group-element which has both #this# and #s#  */
503 Grob *
504 Grob::common_refpoint (Grob const *s, Axis a) const
505 {
506   /* I don't like the quadratic aspect of this code, but I see no
507      other way.  The largest chain of parents might be 10 high or so,
508      so it shouldn't be a real issue.  */
509   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
510     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
511       if (d == c)
512         return (Grob *) d;
513
514   return 0;
515 }
516
517 void
518 Grob::set_parent (Grob *g, Axis a)
519 {
520   dim_cache_[a].parent_ = g;
521 }
522
523 Grob *
524 Grob::get_parent (Axis a) const
525 {
526   return dim_cache_[a].parent_;
527 }
528
529
530 void
531 Grob::fixup_refpoint ()
532 {
533   for (int a = X_AXIS; a < NO_AXES; a++)
534     {
535       Axis ax = (Axis)a;
536       Grob *parent = get_parent (ax);
537
538       if (!parent)
539         continue;
540
541       if (parent->get_system () != get_system () && get_system ())
542         {
543           Grob *newparent = parent->find_broken_piece (get_system ());
544           set_parent (newparent, ax);
545         }
546
547       if (Item *i = dynamic_cast<Item *> (this))
548         {
549           Item *parenti = dynamic_cast<Item *> (parent);
550
551           if (parenti && i)
552             {
553               Direction my_dir = i->break_status_dir ();
554               if (my_dir != parenti->break_status_dir ())
555                 {
556                   Item *newparent = parenti->find_prebroken_piece (my_dir);
557                   set_parent (newparent, ax);
558                 }
559             }
560         }
561     }
562 }
563
564
565 /****************************************************************
566   MESSAGES
567 ****************************************************************/
568 void
569 Grob::warning (string s) const
570 {
571   if (get_program_option ("warning-as-error"))
572     error (s);
573
574   SCM cause = self_scm ();
575   while (Grob *g = unsmob_grob (cause))
576     cause = g->get_property ("cause");
577
578   /* ES TODO: cause can't be Music*/
579   if (Music *m = unsmob_music (cause))
580     m->origin ()->warning (s);
581   else if (Stream_event *ev = unsmob_stream_event (cause))
582     ev->origin ()->warning (s);
583   else
584     ::warning (s);
585 }
586
587
588 string
589 Grob::name () const
590 {
591   SCM meta = get_property ("meta");
592   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
593   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
594   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
595 }
596
597 void
598 Grob::programming_error (string s) const
599 {
600   if (get_program_option ("warning-as-error"))
601     error (s);
602
603   SCM cause = self_scm ();
604   while (Grob *g = unsmob_grob (cause))
605     cause = g->get_property ("cause");
606
607   s = _f ("programming error: %s", s);
608
609   /* ES TODO: cause can't be Music*/
610   if (Music *m = unsmob_music (cause))
611     m->origin ()->message (s);
612   else if (Stream_event *ev = unsmob_stream_event (cause))
613     ev->origin ()->message (s);
614   else
615     ::message (s);
616 }
617
618
619 ADD_INTERFACE (Grob,
620                "A grob represents a piece of music notation.\n"
621                "\n"
622                "All grobs have an X and Y@tie{}position on the page.  These"
623                " X and Y@tie{}positions are stored in a relative format, thus"
624                " they can easily be combined by stacking them, hanging one"
625                " grob to the side of another, or coupling them into grouping"
626                " objects.\n"
627                "\n"
628                "Each grob has a reference point (a.k.a.@: parent): The"
629                " position of a grob is stored relative to that reference"
630                " point.  For example, the X@tie{}reference point of a staccato"
631                " dot usually is the note head that it applies to.  When the"
632                " note head is moved, the staccato dot moves along"
633                " automatically.\n"
634                "\n"
635                "A grob is often associated with a symbol, but some grobs do"
636                " not print any symbols.  They take care of grouping objects."
637                " For example, there is a separate grob that stacks staves"
638                " vertically.  The @ref{NoteCollision} object is also an"
639                " abstract grob: It only moves around chords, but doesn't print"
640                " anything.\n"
641                "\n"
642                "Grobs have properties (Scheme variables) that can be read and"
643                " set.  Two types of them exist: immutable and mutable."
644                "  Immutable variables define the default style and behavior."
645                "  They are shared between many objects.  They can be changed"
646                " using @code{\\override} and @code{\\revert}.  Mutable"
647                " properties are variables that are specific to one grob."
648                "  Typically, lists of other objects, or results from"
649                " computations are stored in mutable properties.  In"
650                " particular, every call to @code{ly:grob-set-property!}"
651                " (or its C++ equivalent) sets a mutable property.\n"
652                "\n"
653                "The properties @code{after-line-breaking} and"
654                " @code{before-line-breaking} are dummies that are not"
655                " user-serviceable.",
656
657                /* properties */
658                "X-extent "
659                "X-offset "
660                "Y-extent "
661                "Y-offset "
662                "after-line-breaking "
663                "avoid-slur "
664                "axis-group-parent-X "
665                "axis-group-parent-Y "
666                "before-line-breaking "
667                "cause "
668                "color "
669                "cross-staff "
670                "extra-X-extent "
671                "extra-Y-extent "
672                "extra-offset "
673                "interfaces "
674                "layer "
675                "meta "
676                "minimum-X-extent "
677                "minimum-Y-extent "
678                "outside-staff-horizontal-padding "
679                "outside-staff-padding "
680                "outside-staff-priority "
681                "pure-Y-offset-in-progress "
682                "rotation "
683                "springs-and-rods "
684                "staff-symbol "
685                "stencil "
686                "transparent "
687                "whiteout "
688                );
689
690 /****************************************************************
691   CALLBACKS
692 ****************************************************************/
693
694 static SCM
695 grob_stencil_extent (Grob *me, Axis a)
696 {
697   Stencil *m = me->get_stencil ();
698   Interval e;
699   if (m)
700     e = m->extent (a);
701   return ly_interval2scm (e);
702 }
703
704
705 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
706 SCM
707 Grob::stencil_height (SCM smob)
708 {
709   Grob *me = unsmob_grob (smob);
710   return grob_stencil_extent (me, Y_AXIS);
711 }
712
713 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
714 SCM
715 Grob::y_parent_positioning (SCM smob)
716 {
717   Grob *me = unsmob_grob (smob);
718   Grob *par = me->get_parent (Y_AXIS);
719   if (par)
720     (void) par->get_property ("positioning-done");
721
722   return scm_from_double (0.0);
723 }
724
725
726 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
727 SCM
728 Grob::x_parent_positioning (SCM smob)
729 {
730   Grob *me = unsmob_grob (smob);
731   
732   Grob *par = me->get_parent (X_AXIS);
733   if (par)
734     (void) par->get_property ("positioning-done");
735
736   return scm_from_double (0.0);
737 }
738
739 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
740 SCM
741 Grob::stencil_width (SCM smob)
742 {
743   Grob *me = unsmob_grob (smob);
744   return grob_stencil_extent (me, X_AXIS);
745 }
746
747
748 Grob *
749 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
750 {
751   for (; scm_is_pair (elist); elist = scm_cdr (elist))
752     if (Grob *s = unsmob_grob (scm_car (elist)))
753       {
754         if (common)
755           common = common->common_refpoint (s, a);
756         else
757           common = s;
758       }
759
760   return common;
761 }
762
763 Grob *
764 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
765 {
766   for (vsize i = 0; i < arr.size (); i++)
767     if (common)
768       common = common->common_refpoint (arr[i], a);
769     else
770       common = arr[i];
771
772   return common;
773 }
774
775 Interval
776 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
777 {
778   Interval ext = me->extent (refpoint, a);
779   if (ext.is_empty ())
780     ext.add_point (me->relative_coordinate (refpoint, a));
781
782   return ext;
783 }
784
785 // Checks whether there is a vertical alignment in the chain of
786 // parents between this and commony.
787 bool
788 Grob::check_cross_staff (Grob *commony)
789 {
790   if (Align_interface::has_interface (commony))
791     return true;
792
793   for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
794     if (Align_interface::has_interface (g))
795       return true;
796
797   return false;
798 }
799