+ vector<Grob *> parent_stems;
+ Real ss = 0;
+
+ Grob *commonx = me;
+ for (vsize i = 0; i < dots.size (); i++)
+ {
+ Grob *n = dots[i]->get_parent (Y_AXIS);
+ commonx = n->common_refpoint (commonx, X_AXIS);
+
+ if (Grob *stem = Grob::unsmob (n->get_object ("stem")))
+ {
+ commonx = stem->common_refpoint (commonx, X_AXIS);
+
+ if (Stem::first_head (stem) == n)
+ parent_stems.push_back (stem);
+ }
+ }
+
+ vector<Box> boxes;
+ set<Grob *> stems;
+
+ extract_grob_set (me, "side-support-elements", support);
+
+ Interval base_x;
+ for (vsize i = 0; i < parent_stems.size (); i++)
+ base_x.unite (Stem::first_head (parent_stems[i])->extent (commonx, X_AXIS));
+
+ for (vsize i = 0; i < support.size (); i++)
+ {
+ Grob *s = support[i];
+ if (!ss)
+ ss = Staff_symbol_referencer::staff_space (s);
+
+ /* can't inspect Y extent of rest.
+
+ Rest collisions should wait after line breaking.
+ */
+ Interval y;
+ if (Rest::has_interface (s))
+ {
+ base_x.unite (s->extent (commonx, X_AXIS));
+ continue;
+ }
+ else if (Stem::has_interface (s))
+ {
+ Real y1 = Stem::head_positions (s)[-get_grob_direction (s)];
+ Real y2 = y1 + get_grob_direction (s) * 7;
+
+ y.add_point (y1);
+ y.add_point (y2);
+
+ stems.insert (s);
+ }
+ else if (Note_head::has_interface (s))
+ y = Interval (-1.1, 1.1);
+ else
+ {
+ programming_error ("unknown grob in dot col support");
+ continue;
+ }
+
+ y += Staff_symbol_referencer::get_position (s);
+
+ Box b (s->extent (commonx, X_AXIS), y);
+ boxes.push_back (b);
+
+ if (Grob *stem = Grob::unsmob (s->get_object ("stem")))
+ stems.insert (stem);
+ }
+
+ for (set<Grob *>::const_iterator i (stems.begin ());
+ i != stems.end (); i++)
+ {
+ Grob *stem = (*i);
+ Grob *flag = Stem::flag (stem);
+ if (flag)
+ {
+ Grob *commony = stem->common_refpoint (flag, Y_AXIS);
+ Interval y = flag->extent (commony, Y_AXIS) * (2 / ss);
+ Interval x = flag->extent (commonx, X_AXIS);
+
+ boxes.push_back (Box (x, y));
+ }
+ }
+
+ /*
+ The use of pure_position_less and pure_get_rounded_position below
+ are due to the fact that this callback is called before line breaking
+ occurs. Because dots' actual Y posiitons may be linked to that of
+ beams (dots are attached to rests, which are shifted to avoid beams),
+ we instead must use their pure Y positions.
+ */
+ vector_sort (dots, pure_position_less);
+
+ SCM chord_dots_limit = me->get_property ("chord-dots-limit");
+ if (scm_is_number (chord_dots_limit))
+ {
+ // Sort dots by stem, then check for dots above the limit for each stem
+ vector <vector <Grob *> > dots_each_stem (parent_stems.size ());
+ for (vsize i = 0; i < dots.size (); i++)
+ if (Grob *stem = Grob::unsmob (dots[i]->get_parent (Y_AXIS)
+ -> get_object ("stem")))
+ for (vsize j = 0; j < parent_stems.size (); j++)
+ if (stem == parent_stems[j])
+ {
+ dots_each_stem[j].push_back (dots[i]);
+ break;
+ }
+ for (vsize j = 0; j < parent_stems.size (); j++)
+ {
+ Interval chord = Stem::head_positions (parent_stems[j]);
+ int total_room = ((int) chord.length () + 2
+ + scm_to_int (chord_dots_limit)) / 2;
+ int total_dots = dots_each_stem[j].size ();
+ // remove excessive dots from the ends of the stem
+ for (int first_dot = 0; total_dots > total_room; total_dots--)
+ if (0 == (total_dots - total_room) % 2)
+ dots_each_stem[j][first_dot++]->suicide ();
+ else
+ dots_each_stem[j][first_dot + total_dots - 1]->suicide ();
+ }
+ }
+