]> git.donarmstrong.com Git - lilypond.git/blob - lily/text-spanner.cc
release: 1.5.43
[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-pair");
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
186 void 
187 Text_spanner::setup_pedal_bracket(Spanner *s)
188 {
189
190   Real thick = s->paper_l ()->get_var ("stafflinethickness");  
191
192   Drul_array<bool> w, broken;
193   Drul_array<Real> height, width, shorten, r;
194
195   SCM pa = s->get_grob_property ("if-text-padding");
196   SCM ew = s->get_grob_property ("edge-width");
197   SCM eh = s->get_grob_property ("edge-height");
198   SCM sp = s->get_grob_property ("shorten-pair");
199   SCM wl = s->get_grob_property ("left-widen");
200   SCM wr = s->get_grob_property ("right-widen");
201
202   // Pedal has an angled left edge \__  or an angled right edge __/ 
203   w[LEFT] = w[RIGHT] = false;
204   if (gh_boolean_p (wl) )  
205     w[LEFT]   = to_boolean (wl);
206   if (gh_boolean_p (wr) )  
207     w[RIGHT]  = to_boolean (wr);
208   
209   Direction d = LEFT;
210   Interval e;
211   Real padding = 0;
212
213   if (gh_number_p (pa) )
214     padding = gh_scm2double (pa);
215
216   do {
217     Item *b = s->get_bound (d);
218
219     e = b->extent (b, X_AXIS);
220     if (!e.empty_b ())
221       r[d] = d * (e[-d] + padding);
222
223     broken[d] = b->break_status_dir () != CENTER;
224     width[d]  =  0;
225     height[d] =  0;
226     shorten[d] = 0;
227     if ( w[d] && gh_pair_p (ew) )
228       width[d] +=  gh_scm2double (index_cell (ew, d)) * d;
229     if ( !broken[d] && (gh_pair_p (eh) ) )
230       height[d] = gh_scm2double (index_cell (eh, d));
231     if ( gh_pair_p (sp) )
232       shorten[d] =  gh_scm2double (index_cell (sp, d));
233   }
234   while (flip (&d) != LEFT);
235   
236   Real extra_short = 0;
237   // For 'Mixed' style pedals, i.e.  a bracket preceded by text:  Ped._____|
238   // need to shorten by the extent of the text grob
239   if ( to_boolean (s->get_grob_property ("text-start")) )
240     {
241       height[LEFT] = 0;
242       Grob * textbit = s->get_parent(Y_AXIS);
243       extra_short = padding;
244       if (textbit->has_interface(ly_symbol2scm("piano-pedal-interface")))
245         // for pretty Ped. scripts. 
246         {
247           e = textbit->extent(textbit, Y_AXIS);
248           extra_short += e.length();
249         }
250       if (textbit->has_interface(ly_symbol2scm("text-interface"))) 
251         // for plain text, e.g., Sost. Ped.
252         {
253           SCM text  =  textbit->get_grob_property("text"); 
254           if (gh_string_p (text)) {
255             SCM properties = Font_interface::font_alist_chain (s);
256             Molecule mol = Text_item::text2molecule (s, text, properties);
257             extra_short += mol.extent(X_AXIS).length() / 2;
258           }
259         }
260       shorten[RIGHT] -= thick;
261     }
262
263   // Shorten a \____ on the left so that it will touch an adjoining ___/ 
264   shorten[LEFT] += abs(width[LEFT]) * 2   +  extra_short ;
265   
266   if (broken[LEFT]) {
267     shorten[LEFT] -= s->get_broken_left_end_align () ;
268     shorten[RIGHT] -= r[RIGHT];
269   }
270   else 
271     // Shorten bracket on the right so it ends just before the spanned note.
272     shorten[RIGHT]  +=  thick  -  (r[LEFT]  +  r[RIGHT]);
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-pair", gh_cons ( gh_double2scm ( shorten[LEFT] ), 
279                                              gh_double2scm ( shorten[RIGHT] ) ));
280 }