+Real
+Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
+{
+ if (refp == this)
+ return 0.0;
+
+ Real off = 0;
+
+ if (dim_cache_[Y_AXIS].offset_)
+ {
+ if (to_boolean (get_property ("pure-Y-offset-in-progress")))
+ programming_error ("cyclic chain in pure-Y-offset callbacks");
+
+ off = *dim_cache_[Y_AXIS].offset_;
+ }
+ else
+ {
+ SCM proc = get_property_data ("Y-offset");
+
+ dim_cache_[Y_AXIS].offset_ = new Real (0.0);
+ set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
+ off = robust_scm2double (call_pure_function (proc,
+ scm_list_1 (self_scm ()),
+ start, end),
+ 0.0);
+ del_property ("pure-Y-offset-in-progress");
+ delete dim_cache_[Y_AXIS].offset_;
+ dim_cache_[Y_AXIS].offset_ = 0;
+ }
+
+ /* we simulate positioning-done if we are the child of a VerticalAlignment,
+ but only if we don't have a cached offset. If we do have a cached offset,
+ it probably means that the Alignment was fixed and it has already been
+ calculated.
+ */
+ if (Grob *p = get_parent (Y_AXIS))
+ {
+ Real trans = 0;
+ if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
+ trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
+
+ return off + trans + p->pure_relative_y_coordinate (refp, start, end);
+ }
+ return off;
+}
+