+bool
+typecheck_grob (SCM symbol, SCM value)
+{
+ if (Unpure_pure_container *upc = unsmob<Unpure_pure_container> (value))
+ return typecheck_grob (symbol, upc->unpure_part ())
+ && typecheck_grob (symbol, upc->pure_part ());
+ return ly_is_procedure (value)
+ || type_check_assignment (symbol, value, ly_symbol2scm ("backend-type?"));
+}
+
+class Grob_properties : public Simple_smob<Grob_properties>
+{
+public:
+ SCM mark_smob () const;
+ static const char * const type_p_name_;
+private:
+ friend class Grob_property_info;
+ friend SCM ly_make_grob_properties (SCM);
+ // alist_ may contain unexpanded nested overrides
+ SCM alist_;
+ // based_on_ is the cooked_ value from the next higher context that
+ // alist_ is based on
+ SCM based_on_;
+ // cooked_ is a version of alist_ where nested overrides have been
+ // expanded
+ SCM cooked_;
+ // cooked_from_ is the value of alist_ from which the expansion has
+ // been done
+ SCM cooked_from_;
+ // nested_ is a count of nested overrides in alist_ Or rather: of
+ // entries that must not appear in the cooked list and are
+ // identified by having a "key" that is not a symbol. Temporary
+ // overrides and reverts also meet that description and have a
+ // nominal key of #t/#f and a value of the original cons cell.
+ int nested_;
+
+ Grob_properties (SCM alist, SCM based_on) :
+ alist_ (alist), based_on_ (based_on),
+ // if the constructor was called with lists possibly containing
+ // partial overrides, we would need to initialize with based_on in
+ // order to trigger an initial update. But this should never
+ // happen, so we initialize straight with alist.
+ cooked_ (alist), cooked_from_ (alist), nested_ (0) { }
+};
+
+const char * const Grob_properties::type_p_name_ = "ly:grob-properties?";
+
+SCM
+Grob_properties::mark_smob () const
+{
+ scm_gc_mark (alist_);
+ scm_gc_mark (based_on_);
+ scm_gc_mark (cooked_);
+ return cooked_from_;
+}
+
+LY_DEFINE (ly_make_grob_properties, "ly:make-grob-properties",
+ 1, 0, 0, (SCM alist),
+ "This packages the given property list @var{alist} in"
+ " a grob property container stored in a context property"
+ " with the name of a grob.")
+{
+ LY_ASSERT_TYPE (ly_is_list, alist, 1);
+ return Grob_properties (alist, SCM_EOL).smobbed_copy ();
+}
+
+
+Grob_property_info
+Grob_property_info::find ()
+{
+ if (props_)
+ return *this;
+ SCM res = SCM_UNDEFINED;
+ if (Context *c = context_->where_defined (symbol_, &res))
+ if (c != context_)
+ return Grob_property_info (c, symbol_, unsmob<Grob_properties> (res));
+ props_ = unsmob<Grob_properties> (res);
+ return *this;
+}
+
+bool
+Grob_property_info::check ()
+{
+ if (props_)
+ return true;
+ SCM res = SCM_UNDEFINED;
+ if (context_->here_defined (symbol_, &res))
+ props_ = unsmob<Grob_properties> (res);
+ return props_;
+}
+
+bool
+Grob_property_info::create ()
+{
+ // Using scm_hashq_create_handle_x would seem like the one-lookup
+ // way to create a handle if it does not exist yet. However, we
+ // need to check that there is a corresponding grob in this
+ // particular output first, and we have to do this in the global
+ // context. By far the most frequent case will be that a
+ // Grob_properties for this context already exists, so we optimize
+ // for that and only check the global handle when the local
+ // context is pristine.
+ if (check ())
+ return true;
+ SCM current_context_val = SCM_EOL;
+ Context *g = context_->get_global_context ();
+ if (!g)
+ return false; // Context is probably dead
+
+ /*
+ Don't mess with MIDI.
+ */
+ if (g == context_
+ || !g->here_defined (symbol_, ¤t_context_val))
+ return false;
+
+ Grob_properties *def = unsmob<Grob_properties> (current_context_val);
+
+ if (!def)
+ {
+ programming_error ("Grob definition expected");
+ return false;
+ }
+
+ // We create the new Grob_properties from the default definition
+ // since this is what we have available right now. It may or may
+ // not be accurate since we don't take into account any
+ // prospective overrides in intermediate contexts. If there are
+ // any, they will be factored in when `updated' is being called.
+ SCM props = Grob_properties (def->alist_, def->alist_).smobbed_copy ();
+ context_->set_property (symbol_, props);
+ props_ = unsmob<Grob_properties> (props);
+ return props_;
+}