]> git.donarmstrong.com Git - lilypond.git/blob - lily/text-spanner.cc
release: 1.5.41
[lilypond.git] / lily / text-spanner.cc
1 /*
2
3   text-spanner.cc -- implement Text_spanner
4
5   source file of the GNU LilyPond music typesetter
6
7   (c) 2000--2002 Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 #include "molecule.hh"
11 #include "text-item.hh"
12 #include "text-spanner.hh"
13 #include "line-spanner.hh"
14 #include "spanner.hh"
15 #include "font-interface.hh"
16 #include "dimensions.hh"
17 #include "paper-def.hh"
18 #include "debug.hh"
19 #include "paper-column.hh"
20 #include "staff-symbol-referencer.hh"
21
22 /*
23   TODO:
24     - vertical start / vertical end (fixme-name) |
25     - contination types (vert. star, vert. end)  |-> eat volta-spanner
26     - more styles
27     - more texts/positions
28  */
29
30 MAKE_SCHEME_CALLBACK (Text_spanner, brew_molecule, 1);
31
32 SCM
33 Text_spanner::brew_molecule (SCM smob) 
34 {
35   Grob *me= unsmob_grob (smob);
36   Spanner *spanner = dynamic_cast<Spanner*> (me);
37
38   if (spanner->has_interface (ly_symbol2scm ("piano-pedal-interface")) ) 
39     {
40       setup_pedal_bracket(spanner);
41     }
42
43
44   /* Ugh, must be same as Hairpin::brew_molecule.  */
45   Real padding = gh_scm2double (me->get_grob_property ("if-text-padding"));
46   Real broken_left =  spanner->get_broken_left_end_align ();
47   Real width = spanner->spanner_length ();
48   width -= broken_left;
49
50   Drul_array<bool> broken;
51   Drul_array<Real> extra_off;
52   Direction d = LEFT;
53   do
54     {
55       extra_off [d]=0;
56       Item *b = spanner->get_bound (d);
57       broken[d] = b->break_status_dir () != CENTER;
58
59       if (!broken [d])
60         {
61
62           Interval e = b->extent (b, X_AXIS);
63           Real r = 0.0;
64           if (!e.empty_b ())
65             r = e[-d] + padding;
66           /* Text spanners such as ottava, should span from outer limits of
67            noteheads, iso (de)cresc. spanners that span the inner space */
68           if (me->get_grob_property ("outer") != SCM_EOL)
69             // r *= -1; // huh?
70             {
71               width -= d * r;
72             }
73           else
74             {
75               width += d * r;
76               extra_off[d] = r;
77             }
78         }
79     }
80   while (flip (&d) != LEFT);
81
82   // FIXME: ecs tells us -- only for (de)cresc. spanners
83   width += gh_scm2double (me->get_grob_property ("width-correct"));
84   /* /Ugh */
85
86
87   SCM properties = Font_interface::font_alist_chain (me);
88
89   SCM edge_text = me->get_grob_property ("edge-text");
90   Drul_array<Molecule> edge;
91   if (gh_pair_p (edge_text))
92     {
93       Direction d = LEFT;
94       do
95         {
96           /*  Don't repeat edge text for broken end */
97           if (!broken[d])
98             {
99               SCM text = index_cell (edge_text, d);
100               edge[d] = Text_item::text2molecule (me, text, properties);
101               if (!edge[d].empty_b ())
102                 edge[d].align_to (Y_AXIS, CENTER);
103             }
104         }
105       while (flip (&d) != LEFT);
106     }
107   width -= edge[LEFT].extent (X_AXIS).length ()
108     + edge[RIGHT].extent (X_AXIS).length ();
109
110   Drul_array<Real> shorten;
111   shorten[LEFT] = 0;
112   shorten[RIGHT] = 0;
113
114   SCM s = me->get_grob_property ("shorten");
115   if (gh_pair_p (s))
116     {
117       shorten[LEFT] = gh_scm2double (ly_car (s));
118       shorten[RIGHT] = gh_scm2double (ly_cdr (s));
119     }
120
121   width -= shorten[LEFT] + shorten[RIGHT];
122   
123   if (width < 0)
124     {
125       me->warning (_ ("Text_spanner too small"));
126       width = 0;
127     }
128
129   /* ugh */
130   Real thick = me->paper_l ()->get_var ("stafflinethickness");  
131   
132   Molecule line = Line_spanner::line_molecule (me, width, 0);
133   
134   Drul_array<Molecule> edge_line;
135   s = me->get_grob_property ("edge-height");
136   SCM ew = me->get_grob_property ("edge-width");
137   if (gh_pair_p (s))
138     {
139       Direction d = LEFT;
140       int dir = to_dir (me->get_grob_property ("direction"));
141       do
142         {
143           Real dx = ( gh_pair_p (ew)  ? 
144                       gh_scm2double (index_cell (ew, d)) * - dir  :  
145                       0 );
146           Real dy = gh_scm2double (index_cell (s, d)) * - dir;
147           if (dy)
148             {
149               SCM list = Line_spanner::line_atom (me, dx, dy);
150               Box b (Interval (-thick, 0),
151                      dy > 0
152                      ? Interval (0, dy)
153                      : Interval (dy, 0));
154               edge_line[d] = Molecule (b, list);
155             }
156         }
157       while (flip (&d) != LEFT);
158     }
159   
160   Molecule m;
161   if (!edge[LEFT].empty_b ())
162     m = edge[LEFT];
163
164   if (!edge_line[LEFT].empty_b ())
165     m.add_at_edge (X_AXIS, RIGHT, edge_line[LEFT], 0);
166   if (!line.empty_b ())
167     m.add_at_edge (X_AXIS, RIGHT, line, 0);
168   if (!edge_line[RIGHT].empty_b ())
169     m.add_at_edge (X_AXIS, RIGHT, edge_line[RIGHT], 0);
170   if (!edge[RIGHT].empty_b ())
171     m.add_at_edge (X_AXIS, RIGHT, edge[RIGHT], 0);
172   m.translate_axis (broken_left + extra_off[LEFT] + shorten[LEFT], X_AXIS);
173
174   return m.smobbed_copy ();
175 }
176
177
178
179 /* 
180    Piano pedal brackets are a special case of a text spanner.
181    Pedal up-down (restart) indicated by the angled right and left edges 
182    of consecutive pedals touching exactly to form an __/\__
183    Chris Jackson <chris@fluffhouse.org.uk>
184
185    TODO: Pedal line extending to the end of the note
186 */
187
188 void 
189 Text_spanner::setup_pedal_bracket(Spanner *s)
190 {
191
192   Real thick = s->paper_l ()->get_var ("stafflinethickness");  
193   Real ss = Staff_symbol_referencer::staff_space (s);
194
195   Drul_array<bool> a, broken;
196   Drul_array<Real> height, width, shorten, r;
197
198   // Pedal has an angled left edge \__  or an angled right edge __/ 
199   a[LEFT] = a[RIGHT] = false;
200   SCM al = s->get_grob_property ("angle-left");
201   SCM ar = s->get_grob_property ("angle-right");
202   if (gh_boolean_p (al) )  
203     a[LEFT]   = to_boolean (al);
204   if (gh_boolean_p (ar) )  
205     a[RIGHT]  = to_boolean (ar);
206   
207   height[LEFT] = ( to_boolean (s->get_grob_property ("text-start")) ?
208                    0 :
209                    ss );
210   height[RIGHT] = ss;
211   
212   Direction d = LEFT;
213   Interval e;
214   Real padding = 0;
215   SCM pa = (s->get_grob_property ("if-text-padding"));
216   if (gh_number_p (pa) )
217     padding = gh_scm2double (pa);
218   do {
219     Item *b = s->get_bound (d);
220
221     e = b->extent (b, X_AXIS);
222     if (!e.empty_b ())
223       r[d] = d * (e[-d] + padding);
224
225     broken[d] = b->break_status_dir () != CENTER;
226     width[d]  = (a[d]  ? ss*d/2 :  0);
227     if (broken[d])
228       height[d] =  0;
229   }
230   while (flip (&d) != LEFT);
231   
232   shorten[RIGHT] =  shorten[LEFT]  =  0;
233   Real extra_short = 0;
234   // For 'Mixed' style pedals, i.e.  a bracket preceded by text:  Ped._____|
235   // need to shorten by the extent of the text grob
236   if ( to_boolean (s->get_grob_property ("text-start")) )
237     {
238       Grob * textbit = s->get_parent(Y_AXIS);
239       extra_short = padding;
240       if (textbit->has_interface(ly_symbol2scm("piano-pedal-interface")))
241         // for pretty Ped. scripts. 
242         {
243           e = textbit->extent(textbit, Y_AXIS);
244           extra_short += e.length();
245         }
246       if (textbit->has_interface(ly_symbol2scm("text-interface"))) 
247         // for plain text, e.g., Sost. Ped.
248         {
249           SCM text  =  textbit->get_grob_property("text"); 
250           if (gh_string_p (text)) {
251             SCM properties = Font_interface::font_alist_chain (s);
252             Molecule mol = Text_item::text2molecule (s, text, properties);
253             extra_short += mol.extent(X_AXIS).length() / 2;
254           }
255         }
256       shorten[RIGHT] -= thick;
257     }
258
259   // Shorten a \____ on the left so that it will touch an adjoining ___/ 
260   shorten[LEFT] += abs(width[LEFT]) * 2   +  extra_short ;
261   
262   if (broken[LEFT]) {
263     shorten[LEFT] -= s->get_broken_left_end_align () ;
264     shorten[RIGHT] -= r[RIGHT];
265   }
266   else 
267     // Shorten bracket on the right so it ends just before the spanned note.
268     shorten[RIGHT]  +=  thick  -  (r[LEFT]  +  r[RIGHT]);
269
270   // Hmm. TODO: This should be set in grob-description.scm, but side-positioning
271   // of consecutive brackets only seems to work if direction is +1 within the engraver. 
272   s->set_grob_property ("direction", gh_int2scm(-1)); 
273
274   s->set_grob_property ("edge-height", gh_cons ( gh_double2scm ( height[LEFT] ) , 
275                                                  gh_double2scm ( height[RIGHT]) ) );
276   s->set_grob_property ("edge-width",  gh_cons ( gh_double2scm ( width[LEFT]  ), 
277                                                  gh_double2scm ( width[RIGHT] ) ));
278   s->set_grob_property ("shorten", gh_cons ( gh_double2scm ( shorten[LEFT] ), 
279                                              gh_double2scm ( shorten[RIGHT] ) ));
280 }