Merged with trunk R1612.
[ardour.git] / gtk2_ardour / tempo_dialog.cc
1 #include <cstdio> // for snprintf, grrr 
2
3 #include <gtkmm/stock.h>
4 #include <gtkmm2ext/utils.h>
5
6 #include "tempo_dialog.h"
7 #include "utils.h"
8
9 #include "i18n.h"
10
11 using namespace Gtk;
12 using namespace Gtkmm2ext;
13 using namespace ARDOUR;
14 using namespace PBD;
15
16 TempoDialog::TempoDialog (TempoMap& map, nframes_t frame, const string & action)
17         : ArdourDialog (_("edit tempo")),
18           bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 1.0),
19           bpm_spinner (bpm_adjustment),
20           bpm_frame (_("Beats per minute")),
21           ok_button (action),
22           cancel_button (_("Cancel")),
23           when_bar_label (_("Bar")),
24           when_beat_label (_("Beat")),
25           when_table (2, 2),
26           when_frame (_("Location"))
27 {
28         BBT_Time when;
29         Tempo tempo (map.tempo_at (frame));
30         map.bbt_time (frame, when);
31
32         init (when, tempo.beats_per_minute(), true);
33 }
34
35 TempoDialog::TempoDialog (TempoSection& section, const string & action)
36         : ArdourDialog ("tempo dialog"),
37           bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 1.0),
38           bpm_spinner (bpm_adjustment),
39           bpm_frame (_("Beats per minute")),
40           ok_button (action),
41           cancel_button (_("Cancel")),
42           when_bar_label (_("Bar")),
43           when_beat_label (_("Beat")),
44           when_table (2, 2),
45           when_frame (_("Location"))
46 {
47         init (section.start(), section.beats_per_minute(), section.movable());
48 }
49
50 void
51 TempoDialog::init (const BBT_Time& when, double bpm, bool movable)
52 {
53         bpm_spinner.set_numeric (true);
54         bpm_spinner.set_digits (1);
55         bpm_spinner.set_wrap (true);
56         bpm_spinner.set_value (bpm);
57
58         hspacer1.set_border_width (5);
59         hspacer1.pack_start (bpm_spinner, false, false);
60         vspacer1.set_border_width (5);
61         vspacer1.pack_start (hspacer1, false, false);
62
63         bpm_frame.add (vspacer1);
64
65         if (movable) {
66                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
67                 when_bar_entry.set_text (buf);
68                 snprintf (buf, sizeof (buf), "%" PRIu32, when.beats);
69                 when_beat_entry.set_text (buf);
70                 
71                 when_bar_entry.set_name ("MetricEntry");
72                 when_beat_entry.set_name ("MetricEntry");
73                 
74                 when_bar_label.set_name ("MetricLabel");
75                 when_beat_label.set_name ("MetricLabel");
76                 
77                 Gtkmm2ext::set_size_request_to_display_given_text (when_bar_entry, "999g", 5, 7);
78                 Gtkmm2ext::set_size_request_to_display_given_text (when_beat_entry, "999g", 5, 7);
79                 
80                 when_table.set_homogeneous (true);
81                 when_table.set_row_spacings (2);
82                 when_table.set_col_spacings (2);
83                 when_table.set_border_width (5);
84                 
85                 when_table.attach (when_bar_label, 0, 1, 0, 1, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
86                 when_table.attach (when_bar_entry, 0, 1, 1, 2, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
87                 
88                 when_table.attach (when_beat_label, 1, 2, 0, 1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
89                 when_table.attach (when_beat_entry, 1, 2, 1, 2, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
90                 
91                 when_frame.set_name ("MetricDialogFrame");
92                 when_frame.add (when_table);
93
94                 get_vbox()->pack_start (when_frame, false, false);
95         }
96
97         bpm_frame.set_name ("MetricDialogFrame");
98         bpm_spinner.set_name ("MetricEntry");
99
100         get_vbox()->pack_start (bpm_frame, false, false);
101         
102         add_button (Stock::CANCEL, RESPONSE_CANCEL);
103         add_button (Stock::APPLY, RESPONSE_ACCEPT);
104         set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
105         set_default_response (RESPONSE_ACCEPT);
106
107         get_vbox()->show_all();
108         bpm_spinner.show();
109
110         set_name ("MetricDialog");
111
112         bpm_spinner.signal_activate().connect (bind (mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
113         bpm_spinner.signal_button_press_event().connect (mem_fun (*this, &TempoDialog::bpm_button_press), false);
114         bpm_spinner.signal_button_release_event().connect (mem_fun (*this, &TempoDialog::bpm_button_release), false);
115 }
116
117 bool
118 TempoDialog::bpm_button_press (GdkEventButton* ev)
119 {
120         return false;
121 }
122
123 bool
124 TempoDialog::bpm_button_release (GdkEventButton* ev)
125 {       
126         /* the value has been modified, accept should work now */
127
128         set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
129         return false;
130 }
131
132 double 
133 TempoDialog::get_bpm ()
134 {
135         return bpm_spinner.get_value ();
136 }       
137
138 bool
139 TempoDialog::get_bbt_time (BBT_Time& requested)
140 {
141         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
142                 return false;
143         }
144         
145         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
146                 return false;
147         }
148
149         return true;
150 }
151
152
153 MeterDialog::MeterDialog (TempoMap& map, nframes_t frame, const string & action)
154         : ArdourDialog ("meter dialog"),
155           note_frame (_("Meter denominator")),
156           bpb_frame (_("Beats per bar")),
157           ok_button (action),
158           cancel_button (_("Cancel")),
159           when_bar_label (_("Bar")),
160           when_beat_label (_("Beat")),
161           when_frame (_("Location"))
162 {
163         BBT_Time when;
164         frame = map.round_to_bar(frame,0); 
165         Meter meter (map.meter_at(frame));
166
167         map.bbt_time (frame, when);
168         init (when, meter.beats_per_bar(), meter.note_divisor(), true);
169 }
170
171 MeterDialog::MeterDialog (MeterSection& section, const string & action)
172         : ArdourDialog ("meter dialog"),
173           note_frame (_("Meter denominator")),
174           bpb_frame (_("Beats per bar")),
175           ok_button (action),
176           cancel_button (_("Cancel")),
177           when_bar_label (_("Bar")),
178           when_beat_label (_("Beat")),
179           when_frame (_("Location"))
180 {
181         init (section.start(), section.beats_per_bar(), section.note_divisor(), section.movable());
182 }
183
184 void
185 MeterDialog::init (const BBT_Time& when, double bpb, double note_type, bool movable)
186 {
187         snprintf (buf, sizeof (buf), "%.2f", bpb);
188         bpb_entry.set_text (buf);
189         bpb_entry.select_region (0, -1);
190         Gtkmm2ext::set_size_request_to_display_given_text (bpb_entry, "999999g", 5, 5);
191
192         strings.push_back (_("whole (1)"));
193         strings.push_back (_("second (2)"));
194         strings.push_back (_("third (3)"));
195         strings.push_back (_("quarter (4)"));
196         strings.push_back (_("eighth (8)"));
197         strings.push_back (_("sixteenth (16)"));
198         strings.push_back (_("thirty-second (32)"));
199         
200         set_popdown_strings (note_types, strings);
201
202         if (note_type==1.0f)
203                 note_types.set_active_text (_("whole (1)"));
204         else if (note_type==2.0f)
205                 note_types.set_active_text (_("second (2)"));
206         else if (note_type==3.0f)
207                 note_types.set_active_text (_("third (3)"));
208         else if (note_type==4.0f)
209                 note_types.set_active_text (_("quarter (4)"));
210         else if (note_type==8.0f)
211                 note_types.set_active_text (_("eighth (8)"));
212         else if (note_type==16.0f)
213                 note_types.set_active_text (_("sixteenth (16)"));
214         else if (note_type==32.0f)
215                 note_types.set_active_text (_("thirty-second (32)"));
216         else
217                 note_types.set_active_text (_("quarter (4)"));
218                 
219         /* the string here needs to be the longest one to display */
220         const guint32 FUDGE = 20; // Combo's are stupid - they steal space from the entry for the button
221         Gtkmm2ext::set_size_request_to_display_given_text (note_types, "thirty-second (32)", 7+FUDGE, 7);
222
223         hspacer1.set_border_width (5);
224         hspacer1.pack_start (note_types, false, false);
225         vspacer1.set_border_width (5);
226         vspacer1.pack_start (hspacer1, false, false);
227
228         hspacer2.set_border_width (5);
229         hspacer2.pack_start (bpb_entry, false, false);
230         vspacer2.set_border_width (5);
231         vspacer2.pack_start (hspacer2, false, false);
232
233         note_frame.add (vspacer1);
234         bpb_frame.add (vspacer2);
235
236         if (movable) {
237                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
238                 when_bar_entry.set_text (buf);
239                 snprintf (buf, sizeof (buf), "%" PRIu32, when.beats);
240                 when_beat_entry.set_text (buf);
241                 
242                 when_bar_entry.set_name ("MetricEntry");
243                 when_beat_entry.set_name ("MetricEntry");
244                 
245                 when_bar_label.set_name ("MetricLabel");
246                 when_beat_label.set_name ("MetricLabel");
247                 
248                 Gtkmm2ext::set_size_request_to_display_given_text (when_bar_entry, "999g", 5, 7);
249                 Gtkmm2ext::set_size_request_to_display_given_text (when_beat_entry, "999g", 5, 7);
250                 
251                 when_table.set_homogeneous (true);
252                 when_table.set_row_spacings (2);
253                 when_table.set_col_spacings (2);
254                 when_table.set_border_width (5);
255                 
256                 when_table.attach (when_bar_label, 0, 1, 0, 1, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
257                 when_table.attach (when_bar_entry, 0, 1, 1, 2, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
258                 
259                 when_table.attach (when_beat_label, 1, 2, 0, 1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
260                 when_table.attach (when_beat_entry, 1, 2, 1, 2, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
261                 
262                 when_frame.set_name ("MetricDialogFrame");
263                 when_frame.add (when_table);
264                 
265                 get_vbox()->pack_start (when_frame, false, false);
266         }
267         get_vbox()->pack_start (bpb_frame, false, false);
268         get_vbox()->pack_start (note_frame, false, false);
269         
270         bpb_frame.set_name ("MetricDialogFrame");
271         note_frame.set_name ("MetricDialogFrame");
272         bpb_entry.set_name ("MetricEntry");
273
274         add_button (Stock::CANCEL, RESPONSE_CANCEL);
275         add_button (Stock::APPLY, RESPONSE_ACCEPT);
276         set_response_sensitive (RESPONSE_ACCEPT, false);
277         set_default_response (RESPONSE_ACCEPT);
278
279         get_vbox()->show_all ();
280         bpb_entry.show ();
281
282         set_name ("MetricDialog");
283         bpb_entry.signal_activate().connect (bind (mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
284         bpb_entry.signal_key_press_event().connect (mem_fun (*this, &MeterDialog::bpb_key_press), false);
285         bpb_entry.signal_key_release_event().connect (mem_fun (*this, &MeterDialog::bpb_key_release));
286         note_types.signal_changed().connect (mem_fun (*this, &MeterDialog::note_types_change));
287 }
288
289 bool
290 MeterDialog::bpb_key_press (GdkEventKey* ev)
291 {
292
293 switch (ev->keyval) { 
294
295  case GDK_0:
296  case GDK_1:
297  case GDK_2:
298  case GDK_3:
299  case GDK_4:
300  case GDK_5:
301  case GDK_6:
302  case GDK_7:
303  case GDK_8:
304  case GDK_9:
305  case GDK_KP_0:
306  case GDK_KP_1:
307  case GDK_KP_2:
308  case GDK_KP_3:
309  case GDK_KP_4:
310  case GDK_KP_5:
311  case GDK_KP_6:
312  case GDK_KP_7:
313  case GDK_KP_8:
314  case GDK_KP_9:
315  case GDK_period:
316  case GDK_comma:
317  case  GDK_KP_Delete:
318  case  GDK_KP_Enter:
319  case  GDK_Delete:
320  case  GDK_BackSpace:
321  case  GDK_Escape:
322  case  GDK_Return:
323  case  GDK_Home:
324  case  GDK_End:
325  case  GDK_Left:
326  case  GDK_Right:
327  case  GDK_Num_Lock:
328  case  GDK_Tab:
329     return FALSE;
330  default:
331       break;
332  }
333
334    return TRUE;
335 }
336
337 bool
338 MeterDialog::bpb_key_release (GdkEventKey* ev)
339 {
340         if (bpb_entry.get_text() != "") {
341                 set_response_sensitive (RESPONSE_ACCEPT, true);
342         } else {
343                 set_response_sensitive (RESPONSE_ACCEPT, false);
344         }
345         return false;
346 }
347
348 void
349 MeterDialog::note_types_change ()
350 {
351         set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
352 }
353
354 double
355 MeterDialog::get_bpb ()
356 {
357         double bpb = 0;
358         
359         if (sscanf (bpb_entry.get_text().c_str(), "%lf", &bpb) != 1) {
360                 return 0;
361         }
362
363         return bpb;
364 }
365         
366 double
367 MeterDialog::get_note_type ()
368 {
369         double note_type = 0;
370         vector<string>::iterator i;
371         string text = note_types.get_active_text();
372         
373         for (i = strings.begin(); i != strings.end(); ++i) {
374                 if (text == *i) {
375                         if (sscanf (text.c_str(), "%*[^0-9]%lf", &note_type) != 1) {
376                                 error << string_compose(_("garbaged note type entry (%1)"), text) << endmsg;
377                                 return 0;
378                         } else {
379                                 break;
380                         }
381                 }
382         } 
383         
384         if (i == strings.end()) {
385                 if (sscanf (text.c_str(), "%lf", &note_type) != 1) {
386                         error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
387                         return 0;
388                 }
389         }
390
391         return note_type;
392 }
393
394 bool
395 MeterDialog::get_bbt_time (BBT_Time& requested)
396 {
397         requested.ticks = 0;
398
399         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
400                 return false;
401         }
402         
403         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
404                 return false;
405         }
406
407         return true;
408 }