+ SCM directive = me->get_property ("outside-staff-placement-directive");
+
+ if (scm_is_eq (directive, ly_symbol2scm ("left-to-right-greedy"))
+ || scm_is_eq (directive, ly_symbol2scm ("left-to-right-polite"))
+ || scm_is_eq (directive, ly_symbol2scm ("right-to-left-greedy"))
+ || scm_is_eq (directive, ly_symbol2scm ("right-to-left-polite")))
+ return directive;
+
+ me->warning (_f ("\"%s\" is not a valid outside-staff-placement-directive",
+ robust_symbol2string (directive, "").c_str ()));
+
+ return ly_symbol2scm ("left-to-right-polite");
+}
+
+// Shifts the grobs in elements to ensure that they (and any
+// connected riders) don't collide with the staff skylines
+// or anything in all_X_skylines. Afterwards, the skylines
+// of the grobs in elements will be added to all_v_skylines.
+static void
+add_grobs_of_one_priority (Grob *me,
+ Drul_array<vector<Skyline_pair> > *all_v_skylines,
+ Drul_array<vector<Real> > *all_paddings,
+ Drul_array<vector<Real> > *all_horizon_paddings,
+ vector<Grob *> elements,
+ Grob *x_common,
+ Grob *y_common,
+ multimap<Grob *, Grob *> const &riders)
+{
+
+ SCM directive
+ = valid_outside_staff_placement_directive (me);
+
+ bool l2r = (scm_is_eq (directive, ly_symbol2scm ("left-to-right-greedy"))
+ || scm_is_eq (directive, ly_symbol2scm ("left-to-right-polite")));
+
+ bool polite = (scm_is_eq (directive, ly_symbol2scm ("left-to-right-polite"))
+ || scm_is_eq (directive, ly_symbol2scm ("right-to-left-polite")));
+
+ vector<Box> boxes;
+ vector<Skyline_pair> skylines_to_merge;
+
+ // We want to avoid situations like this:
+ // still more text
+ // more text
+ // text
+ // -------------------
+ // staff
+ // -------------------
+
+ // The point is that "still more text" should be positioned under
+ // "more text". In order to achieve this, we place the grobs in several
+ // passes. We keep track of the right-most horizontal position that has been
+ // affected by the current pass so far (actually we keep track of 2
+ // positions, one for above the staff, one for below).
+
+ // In each pass, we loop through the unplaced grobs from left to right.
+ // If the grob doesn't overlap the right-most affected position, we place it
+ // (and then update the right-most affected position to point to the right
+ // edge of the just-placed grob). Otherwise, we skip it until the next pass.
+ while (!elements.empty ())
+ {
+ Drul_array<Real> last_end (-infinity_f, -infinity_f);
+ vector<Grob *> skipped_elements;
+ for (vsize i = l2r ? 0 : elements.size ();
+ l2r ? i < elements.size () : i--;
+ l2r ? i++ : 0)
+ {
+ Grob *elt = elements[i];
+ Real padding
+ = robust_scm2double (elt->get_property ("outside-staff-padding"),
+ Axis_group_interface
+ ::get_default_outside_staff_padding ());
+ Real horizon_padding
+ = robust_scm2double (elt->get_property ("outside-staff-horizontal-padding"), 0.0);
+ Interval x_extent = elt->extent (x_common, X_AXIS);
+ x_extent.widen (horizon_padding);
+
+ Direction dir = get_grob_direction (elt);
+ if (dir == CENTER)
+ {
+ warning (_ ("an outside-staff object should have a direction, defaulting to up"));
+ dir = UP;
+ }
+
+ if (x_extent[LEFT] <= last_end[dir] && polite)
+ {
+ skipped_elements.push_back (elt);
+ continue;
+ }
+ last_end[dir] = x_extent[RIGHT];
+
+ Skyline_pair *v_orig = unsmob<Skyline_pair> (elt->get_property ("vertical-skylines"));
+ if (!v_orig || v_orig->is_empty ())
+ continue;
+
+ // Find the riders associated with this grob, and merge their
+ // skylines with elt's skyline.
+ typedef multimap<Grob *, Grob *>::const_iterator GrobMapIterator;
+ pair<GrobMapIterator, GrobMapIterator> range = riders.equal_range (elt);
+ vector<Skyline_pair> rider_v_skylines;
+ for (GrobMapIterator j = range.first; j != range.second; j++)
+ {
+ Grob *rider = j->second;
+ Skyline_pair *v_rider = unsmob<Skyline_pair> (rider->get_property ("vertical-skylines"));
+ if (v_rider)
+ {
+ Skyline_pair copy (*v_rider);
+ copy.shift (rider->relative_coordinate (x_common, X_AXIS));
+ copy.raise (rider->relative_coordinate (y_common, Y_AXIS));
+ rider_v_skylines.push_back (copy);
+ }
+ }
+ Skyline_pair v_skylines (*v_orig);
+ v_skylines.shift (elt->relative_coordinate (x_common, X_AXIS));
+ v_skylines.raise (elt->relative_coordinate (y_common, Y_AXIS));
+ v_skylines.merge (Skyline_pair (rider_v_skylines));
+
+ avoid_outside_staff_collisions (elt,
+ &v_skylines,
+ padding,
+ horizon_padding,
+ (*all_v_skylines)[dir],
+ (*all_paddings)[dir],
+ (*all_horizon_paddings)[dir],
+ dir);
+
+ elt->set_property ("outside-staff-priority", SCM_BOOL_F);
+ (*all_v_skylines)[dir].push_back (v_skylines);
+ (*all_paddings)[dir].push_back (padding);
+ (*all_horizon_paddings)[dir].push_back (horizon_padding);
+ }
+ swap (elements, skipped_elements);
+ skipped_elements.clear ();
+ }
+}
+
+// If the Grob has a Y-ancestor with outside-staff-priority, return it.
+// Otherwise, return 0.
+Grob *
+Axis_group_interface::outside_staff_ancestor (Grob *me)
+{
+ Grob *parent = me->get_parent (Y_AXIS);
+ if (!parent)
+ return 0;
+
+ if (scm_is_number (parent->get_property ("outside-staff-priority")))
+ return parent;
+
+ return outside_staff_ancestor (parent);
+}
+
+// It is tricky to correctly handle skyline placement of cross-staff grobs.
+// For example, cross-staff beams cannot be formatted until the distance between
+// staves is known and therefore any grobs that depend on the beam cannot be placed
+// until the skylines are known. On the other hand, the distance between staves should
+// really depend on position of the cross-staff grobs that lie between them.
+// Currently, we just leave cross-staff grobs out of the
+// skyline altogether, but this could mean that staves are placed so close together
+// that there is no room for the cross-staff grob. It also means, of course, that
+// we don't get the benefits of skyline placement for cross-staff grobs.
+Skyline_pair
+Axis_group_interface::skyline_spacing (Grob *me)
+{
+ extract_grob_set (me, unsmob<Grob_array> (me->get_object ("vertical-skyline-elements")) ? "vertical-skyline-elements" : "elements", fakeelements);
+ vector<Grob *> elements (fakeelements);
+ for (vsize i = 0; i < elements.size (); i++)
+ /*
+ As a sanity check, we make sure that no grob with an outside staff priority
+ has a Y-parent that also has an outside staff priority, which would result
+ in two movings.
+ */
+ if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))
+ && outside_staff_ancestor (elements[i]))
+ {
+ elements[i]->warning ("Cannot set outside-staff-priority for element and elements' Y parent.");
+ elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
+ }
+
+ /* For grobs with an outside-staff-priority, the sorting function might
+ call extent and cause suicide. This breaks the contract that is required
+ for the STL sort function. To avoid this, we make sure that any suicides
+ are triggered beforehand.
+ */
+ for (vsize i = 0; i < elements.size (); i++)
+ if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
+ elements[i]->extent (elements[i], X_AXIS);
+