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