]> git.donarmstrong.com Git - lilypond.git/blob - lily/break-align-interface.cc
* flower/include/std-string.hh:
[lilypond.git] / lily / break-align-interface.cc
1 /*
2   break-align-interface.cc -- implement Break_align_interface
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9
10 #include "break-align-interface.hh"
11
12 #include "align-interface.hh"
13 #include "axis-group-interface.hh"
14 #include "dimensions.hh"
15 #include "international.hh"
16 #include "output-def.hh"
17 #include "paper-column.hh"
18 #include "pointer-group-interface.hh"
19 #include "self-alignment-interface.hh"
20 #include "side-position-interface.hh"
21 #include "warn.hh"
22
23
24 MAKE_SCHEME_CALLBACK (Break_align_interface, self_align_callback, 1);
25 SCM
26 Break_align_interface::self_align_callback (SCM smob)
27 {
28   Grob *me = unsmob_grob (smob);
29
30   Item *item = dynamic_cast<Item *> (me);
31   Direction bsd = item->break_status_dir ();
32   if (bsd == LEFT)
33     me->set_property ("self-alignment-X", scm_from_int (RIGHT));
34
35   /*
36     Force break alignment itself to be done first, in the case
37   */
38   return Self_alignment_interface::aligned_on_self (me, X_AXIS);
39 }
40
41 /*
42   This is tricky: we cannot modify 'elements, since callers are
43   iterating the same list. Reordering the list in-place, or resetting
44   'elements will skip elements in the loops of callers.
45
46   So we return the correct order as an array.
47 */
48 vector<Grob*>
49 Break_align_interface::ordered_elements (Grob *grob)
50 {
51   Item *me = dynamic_cast<Item *> (grob);
52   extract_grob_set (me, "elements", elts);
53
54   SCM order_vec = me->get_property ("break-align-orders");
55   if (!scm_is_vector (order_vec)
56       || scm_c_vector_length (order_vec) < 3)
57     return elts;
58
59   vector<Grob*> writable_elts (elts);
60   SCM order = scm_vector_ref (order_vec,
61                               scm_from_int (me->break_status_dir () + 1));
62
63   /*
64     Copy in order specified in BREAK-ALIGN-ORDER.
65   */
66   vector<Grob*> new_elts;
67   for (; scm_is_pair (order); order = scm_cdr (order))
68     {
69       SCM sym = scm_car (order);
70
71       for (vsize i = writable_elts.size (); i--;)
72         {
73           Grob *g = writable_elts[i];
74           if (g && sym == g->get_property ("break-align-symbol"))
75             {
76               new_elts.push_back (g);
77               writable_elts.erase (writable_elts.begin () + i);
78             }
79         }
80     }
81
82   return new_elts;
83 }
84
85 void
86 Break_align_interface::add_element (Grob *me, Grob *toadd)
87 {
88   Align_interface::add_element (me, toadd);
89 }
90
91 MAKE_SCHEME_CALLBACK(Break_align_interface, calc_positioning_done, 1)
92 SCM
93 Break_align_interface::calc_positioning_done (SCM smob)
94 {
95   Grob *grob = unsmob_grob (smob);  
96   Item *me = dynamic_cast<Item *> (grob);
97
98   vector<Grob*> elems = ordered_elements (me);
99   vector<Interval> extents;
100
101   int last_nonempty = -1;
102   for (vsize i = 0; i < elems.size (); i++)
103     {
104       Interval y = elems[i]->extent (elems[i], X_AXIS);
105       extents.push_back (y);
106       if (!y.is_empty ())
107         last_nonempty = i;
108     }
109
110   vsize idx = 0;
111   while (idx < extents.size () && extents[idx].is_empty ())
112     idx++;
113
114   vector<Real> offsets;
115   offsets.resize (elems.size ());
116   for (vsize i = 0; i < offsets.size ();i++)
117     offsets[i] = 0.0;
118
119   Real extra_right_space = 0.0;
120   vsize edge_idx = VPOS;
121   while (idx < elems.size ())
122     {
123       vsize next_idx = idx + 1;
124       while (next_idx < elems.size ()
125              && extents[next_idx].is_empty ())
126         next_idx++;
127
128       Grob *l = elems[idx];
129       Grob *r = 0;
130
131       if (next_idx < elems.size ())
132         r = elems[next_idx];
133
134       SCM alist = SCM_EOL;
135
136       /*
137         Find the first grob with a space-alist entry.
138       */
139       extract_grob_set (l, "elements", elts);
140
141       for (vsize i = elts.size (); i--;)
142         {
143           Grob *elt = elts[i];
144
145           if (edge_idx == VPOS
146               && elt->get_property ("break-align-symbol")
147               == ly_symbol2scm ("left-edge"))
148             edge_idx = idx;
149
150           SCM l = elt->get_property ("space-alist");
151           if (scm_is_pair (l))
152             {
153               alist = l;
154               break;
155             }
156         }
157
158       SCM rsym = r ? SCM_EOL : ly_symbol2scm ("right-edge");
159
160       /*
161         We used to use #'cause to find out the symbol and the spacing
162         table, but that gets icky when that grob is suicided for some
163         reason.
164       */
165       if (r)
166         {
167           extract_grob_set (r, "elements", elts);
168           for (vsize i = elts.size ();
169                !scm_is_symbol (rsym) && i--;)
170             {
171               Grob *elt = elts[i];
172               rsym = elt->get_property ("break-align-symbol");
173             }
174         }
175
176       if (rsym == ly_symbol2scm ("left-edge"))
177         edge_idx = next_idx;
178
179       SCM entry = SCM_EOL;
180       if (scm_is_symbol (rsym))
181         entry = scm_assq (rsym, alist);
182
183       bool entry_found = scm_is_pair (entry);
184       if (!entry_found)
185         {
186           string sym_string;
187           if (scm_is_symbol (rsym))
188             sym_string = ly_symbol2string (rsym);
189
190           string orig_string;
191           if (unsmob_grob (l->get_property ("cause")))
192             orig_string = unsmob_grob (l->get_property ("cause"))->name ();
193
194           programming_error (_f ("No spacing entry from %s to `%s'",
195                                  orig_string.c_str (),
196                                  sym_string.c_str ()));
197         }
198
199       Real distance = 1.0;
200       SCM type = ly_symbol2scm ("extra-space");
201
202       if (entry_found)
203         {
204           entry = scm_cdr (entry);
205
206           distance = scm_to_double (scm_cdr (entry));
207           type = scm_car (entry);
208         }
209
210       if (r)
211         {
212           if (type == ly_symbol2scm ("extra-space"))
213             offsets[next_idx] = extents[idx][RIGHT] + distance
214               - extents[next_idx][LEFT];
215           /* should probably junk minimum-space */
216           else if (type == ly_symbol2scm ("minimum-space"))
217             offsets[next_idx] = max (extents[idx][RIGHT], distance);
218         }
219       else
220         extra_right_space = distance;
221
222       idx = next_idx;
223     }
224
225   Real here = 0.0;
226   Interval total_extent;
227
228   Real alignment_off = 0.0;
229   for (vsize i = 0; i < offsets.size (); i++)
230     {
231       here += offsets[i];
232       if (i == edge_idx)
233         alignment_off = -here;
234       total_extent.unite (extents[i] + here);
235     }
236
237   if (total_extent.is_empty ())
238     return SCM_BOOL_T;
239
240   if (me->break_status_dir () == LEFT)
241     alignment_off = -total_extent[RIGHT] - extra_right_space;
242   else if (edge_idx == VPOS)
243     alignment_off = -total_extent[LEFT];
244
245   here = alignment_off;
246   for (vsize i = 0; i < offsets.size (); i++)
247     {
248       here += offsets[i];
249       elems[i]->translate_axis (here, X_AXIS);
250     }
251
252   return SCM_BOOL_T;
253 }
254
255 ADD_INTERFACE (Break_aligned_interface, "break-aligned-interface",
256                "Items that are aligned in prefatory matter.\n"
257                "\n"
258                "The spacing of these items is controlled by the @code{space-alist}\n"
259                "property. It contains a list @code{break-align-symbol}s with a specification\n"
260                "of the associated space. The space specification can be "
261                "@table @code\n"
262                "@item (minimum-space . @var{spc}))\n"
263                "  Pad space until the distance is @var{spc}\n"
264                "@item (fixed-space . @var{spc})\n"
265                "  Set a fixed space\n"
266                "@item (semi-fixed-space . @var{spc})\n"
267                "  Set a space. Half of it is fixed and half is stretchable. \n"
268                "(does not work at start of line. fixme)\n"
269                "@item (extra-space . @var{spc})\n"
270                "  Add @var{spc} amount of space.\n"
271                "@end table\n"
272                "\n"
273                "Special keys for the alist are @code{first-note} and @code{next-note}, signifying\n"
274                "the first note on a line, and the next note halfway a line.\n"
275                "\n"
276                "Rules for this spacing are much more complicated than this. \n"
277                "See [Wanske] page 126 -- 134, [Ross] pg 143 -- 147\n",
278
279                /* properties */ 
280                "break-align-symbol "
281                "space-alist "
282                );
283
284 ADD_INTERFACE (Break_align_interface, "break-alignment-interface",
285                "The object that performs break aligment. See @ref{break-aligned-interface}.",
286
287                /* properties */
288                "positioning-done "
289                "break-align-orders");
290