Pack toolbar combo boxes with expand=false and remove size kludges.
[ardour.git] / gtk2_ardour / tempo_dialog.cc
1 /*
2     Copyright (C) 2000-2007 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cstdio> // for snprintf, grrr
21
22 #include <gtkmm/stock.h>
23 #include <gtkmm2ext/utils.h>
24
25 #include "tempo_dialog.h"
26 #include "utils.h"
27
28 #include "i18n.h"
29
30 using namespace std;
31 using namespace Gtk;
32 using namespace Gtkmm2ext;
33 using namespace ARDOUR;
34 using namespace PBD;
35
36 TempoDialog::TempoDialog (TempoMap& map, framepos_t frame, const string & action)
37         : ArdourDialog (_("New Tempo")),
38           bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0),
39           bpm_spinner (bpm_adjustment),
40           ok_button (action),
41           cancel_button (_("Cancel")),
42           when_bar_label (_("bar:"), ALIGN_LEFT, ALIGN_CENTER),
43           when_beat_label (_("beat:"), ALIGN_LEFT, ALIGN_CENTER)
44 {
45         Timecode::BBT_Time when;
46         Tempo tempo (map.tempo_at (frame));
47         map.bbt_time (frame, when);
48
49         init (when, tempo.beats_per_minute(), tempo.note_type(), true);
50 }
51
52 TempoDialog::TempoDialog (TempoSection& section, const string & action)
53         : ArdourDialog ("Edit Tempo"),
54           bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0),
55           bpm_spinner (bpm_adjustment),
56           ok_button (action),
57           cancel_button (_("Cancel")),
58           when_bar_label (_("bar:"), ALIGN_LEFT, ALIGN_CENTER),
59           when_beat_label (_("beat:"), ALIGN_LEFT, ALIGN_CENTER)
60 {
61         init (section.start(), section.beats_per_minute(), section.note_type(), section.movable());
62 }
63
64 void
65 TempoDialog::init (const Timecode::BBT_Time& when, double bpm, double note_type, bool movable)
66 {
67         bpm_spinner.set_numeric (true);
68         bpm_spinner.set_digits (2);
69         bpm_spinner.set_wrap (true);
70         bpm_spinner.set_value (bpm);
71
72         strings.push_back (_("whole (1)"));
73         strings.push_back (_("second (2)"));
74         strings.push_back (_("third (3)"));
75         strings.push_back (_("quarter (4)"));
76         strings.push_back (_("eighth (8)"));
77         strings.push_back (_("sixteenth (16)"));
78         strings.push_back (_("thirty-second (32)"));
79
80         set_popdown_strings (note_types, strings);
81
82         if (note_type == 1.0f) {
83                 note_types.set_active_text (_("whole (1)"));
84         } else if (note_type == 2.0f) {
85                 note_types.set_active_text (_("second (2)"));
86         } else if (note_type == 3.0f) {
87                 note_types.set_active_text (_("third (3)"));
88         } else if (note_type == 4.0f) {
89                 note_types.set_active_text (_("quarter (4)"));
90         } else if (note_type == 8.0f) {
91                 note_types.set_active_text (_("eighth (8)"));
92         } else if (note_type == 16.0f) {
93                 note_types.set_active_text (_("sixteenth (16)"));
94         } else if (note_type == 32.0f) {
95                 note_types.set_active_text (_("thirty-second (32)"));
96         } else {
97                 note_types.set_active_text (_("quarter (4)"));
98         }
99
100         Table* table = manage (new Table (3, 3));
101         table->set_spacings (6);
102
103         Label* bpm_label = manage (new Label(_("Beats per minute:"), ALIGN_LEFT, ALIGN_CENTER));
104         table->attach (*bpm_label, 0, 2, 0, 1);
105         table->attach (bpm_spinner, 2, 3, 0, 1);
106
107         if (movable) {
108                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
109                 when_bar_entry.set_text (buf);
110                 snprintf (buf, sizeof (buf), "%" PRIu32, when.beats);
111                 when_beat_entry.set_text (buf);
112
113                 when_bar_entry.set_name ("MetricEntry");
114                 when_beat_entry.set_name ("MetricEntry");
115
116                 when_bar_label.set_name ("MetricLabel");
117                 when_beat_label.set_name ("MetricLabel");
118
119                 table->attach (when_bar_label, 1, 2, 2, 3);
120                 table->attach (when_bar_entry, 2, 3, 2, 3);
121
122                 table->attach (when_beat_label, 1, 2, 1, 2);
123                 table->attach (when_beat_entry, 2, 3, 1, 2);
124
125                 Label* when_label = manage (new Label(_("Tempo begins at"), ALIGN_LEFT, ALIGN_CENTER));
126                 table->attach (*when_label, 0, 1, 1, 2);
127         }
128
129         get_vbox()->set_border_width (12);
130         get_vbox()->pack_end (*table);
131         table->show_all ();
132
133         add_button (Stock::CANCEL, RESPONSE_CANCEL);
134         add_button (Stock::APPLY, RESPONSE_ACCEPT);
135         set_response_sensitive (RESPONSE_ACCEPT, false);
136         set_default_response (RESPONSE_ACCEPT);
137
138         bpm_spinner.show ();
139
140         set_name ("MetricDialog");
141
142         bpm_spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
143         bpm_spinner.signal_button_press_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_press), false);
144         bpm_spinner.signal_button_release_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_release), false);
145         bpm_spinner.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::bpm_changed));
146         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
147         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
148         when_beat_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
149         when_beat_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
150         note_types.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::note_types_change));
151 }
152
153 void
154 TempoDialog::bpm_changed ()
155 {
156         set_response_sensitive (RESPONSE_ACCEPT, true);
157 }
158
159 bool
160 TempoDialog::bpm_button_press (GdkEventButton*)
161 {
162         return false;
163 }
164
165 bool
166 TempoDialog::bpm_button_release (GdkEventButton*)
167 {
168         /* the value has been modified, accept should work now */
169
170         set_response_sensitive (RESPONSE_ACCEPT, true);
171         return false;
172 }
173
174 bool
175 TempoDialog::entry_key_release (GdkEventKey*)
176 {
177         if (when_beat_entry.get_text() != "" && when_bar_entry.get_text() != "") {
178                 set_response_sensitive (RESPONSE_ACCEPT, true);
179         } else {
180                 set_response_sensitive (RESPONSE_ACCEPT, false);
181         }
182         return false;
183 }
184
185 double
186 TempoDialog::get_bpm ()
187 {
188         return bpm_spinner.get_value ();
189 }
190
191 bool
192 TempoDialog::get_bbt_time (Timecode::BBT_Time& requested)
193 {
194         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
195                 return false;
196         }
197
198         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
199                 return false;
200         }
201
202         requested.ticks = 0;
203
204         return true;
205 }
206
207 double
208 TempoDialog::get_note_type ()
209 {
210         double note_type = 0;
211         vector<string>::iterator i;
212         string text = note_types.get_active_text();
213
214         for (i = strings.begin(); i != strings.end(); ++i) {
215                 if (text == *i) {
216                         if (sscanf (text.c_str(), "%*[^0-9]%lf", &note_type) != 1) {
217                                 error << string_compose(_("garbaged note type entry (%1)"), text) << endmsg;
218                                 return 0;
219                         } else {
220                                 break;
221                         }
222                 }
223         }
224
225         if (i == strings.end()) {
226                 if (sscanf (text.c_str(), "%lf", &note_type) != 1) {
227                         error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
228                         return 0;
229                 }
230         }
231
232         return note_type;
233 }
234
235 void
236 TempoDialog::note_types_change ()
237 {
238         set_response_sensitive (RESPONSE_ACCEPT, true);
239 }
240
241
242 MeterDialog::MeterDialog (TempoMap& map, framepos_t frame, const string & action)
243         : ArdourDialog ("New Meter"),
244           ok_button (action),
245           cancel_button (_("Cancel"))
246 {
247         Timecode::BBT_Time when;
248         frame = map.round_to_bar(frame,0);
249         Meter meter (map.meter_at(frame));
250
251         map.bbt_time (frame, when);
252         init (when, meter.beats_per_bar(), meter.note_divisor(), true);
253 }
254
255 MeterDialog::MeterDialog (MeterSection& section, const string & action)
256         : ArdourDialog ("Edit Meter"),
257           ok_button (action),
258           cancel_button (_("Cancel"))
259 {
260         init (section.start(), section.beats_per_bar(), section.note_divisor(), section.movable());
261 }
262
263 void
264 MeterDialog::init (const Timecode::BBT_Time& when, double bpb, double note_type, bool movable)
265 {
266         snprintf (buf, sizeof (buf), "%.2f", bpb);
267         bpb_entry.set_text (buf);
268         bpb_entry.select_region (0, -1);
269
270         strings.push_back (_("whole (1)"));
271         strings.push_back (_("second (2)"));
272         strings.push_back (_("third (3)"));
273         strings.push_back (_("quarter (4)"));
274         strings.push_back (_("eighth (8)"));
275         strings.push_back (_("sixteenth (16)"));
276         strings.push_back (_("thirty-second (32)"));
277
278         set_popdown_strings (note_types, strings);
279
280         if (note_type == 1.0f) {
281                 note_types.set_active_text (_("whole (1)"));
282         } else if (note_type == 2.0f) {
283                 note_types.set_active_text (_("second (2)"));
284         } else if (note_type == 3.0f) {
285                 note_types.set_active_text (_("third (3)"));
286         } else if (note_type == 4.0f) {
287                 note_types.set_active_text (_("quarter (4)"));
288         } else if (note_type == 8.0f) {
289                 note_types.set_active_text (_("eighth (8)"));
290         } else if (note_type == 16.0f) {
291                 note_types.set_active_text (_("sixteenth (16)"));
292         } else if (note_type == 32.0f) {
293                 note_types.set_active_text (_("thirty-second (32)"));
294         } else {
295                 note_types.set_active_text (_("quarter (4)"));
296         }
297
298         Label* note_label = manage (new Label (_("Note value:"), ALIGN_LEFT, ALIGN_CENTER));
299         Label* bpb_label = manage (new Label (_("Beats per bar:"), ALIGN_LEFT, ALIGN_CENTER));
300         Table* table = manage (new Table (3, 2));
301         table->set_spacings (6);
302
303         table->attach (*bpb_label, 0, 1, 0, 1, FILL|EXPAND, FILL|EXPAND);
304         table->attach (bpb_entry, 1, 2, 0, 1, FILL|EXPAND, FILL|EXPAND);
305         table->attach (*note_label, 0, 1, 1, 2, FILL|EXPAND, FILL|EXPAND);
306         table->attach (note_types, 1, 2, 1, 2, FILL|EXPAND, SHRINK);
307
308         if (movable) {
309                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
310                 when_bar_entry.set_text (buf);
311                 when_bar_entry.set_name ("MetricEntry");
312
313                 Label* when_label = manage (new Label(_("Meter begins at bar:"), ALIGN_LEFT, ALIGN_CENTER));
314
315                 table->attach (*when_label, 0, 1, 2, 3, FILL | EXPAND, FILL | EXPAND);
316                 table->attach (when_bar_entry, 1, 2, 2, 3, FILL | EXPAND, FILL | EXPAND);
317         } else {
318                 when_bar_entry.set_text ("0");
319         }
320
321         get_vbox()->set_border_width (12);
322         get_vbox()->pack_start (*table, false, false);
323
324         bpb_entry.set_name ("MetricEntry");
325
326         add_button (Stock::CANCEL, RESPONSE_CANCEL);
327         add_button (Stock::APPLY, RESPONSE_ACCEPT);
328         set_response_sensitive (RESPONSE_ACCEPT, false);
329         set_default_response (RESPONSE_ACCEPT);
330
331         get_vbox()->show_all ();
332
333         set_name ("MetricDialog");
334         bpb_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
335         bpb_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
336         bpb_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
337         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
338         when_bar_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
339         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
340
341         note_types.signal_changed().connect (sigc::mem_fun (*this, &MeterDialog::note_types_change));
342 }
343
344 bool
345 MeterDialog::entry_key_press (GdkEventKey* ev)
346 {
347
348         switch (ev->keyval) {
349
350         case GDK_0:
351         case GDK_1:
352         case GDK_2:
353         case GDK_3:
354         case GDK_4:
355         case GDK_5:
356         case GDK_6:
357         case GDK_7:
358         case GDK_8:
359         case GDK_9:
360         case GDK_KP_0:
361         case GDK_KP_1:
362         case GDK_KP_2:
363         case GDK_KP_3:
364         case GDK_KP_4:
365         case GDK_KP_5:
366         case GDK_KP_6:
367         case GDK_KP_7:
368         case GDK_KP_8:
369         case GDK_KP_9:
370         case GDK_period:
371         case GDK_comma:
372         case  GDK_KP_Delete:
373         case  GDK_KP_Enter:
374         case  GDK_Delete:
375         case  GDK_BackSpace:
376         case  GDK_Escape:
377         case  GDK_Return:
378         case  GDK_Home:
379         case  GDK_End:
380         case  GDK_Left:
381         case  GDK_Right:
382         case  GDK_Num_Lock:
383         case  GDK_Tab:
384                 return FALSE;
385         default:
386                 break;
387         }
388
389         return TRUE;
390 }
391
392 bool
393 MeterDialog::entry_key_release (GdkEventKey*)
394 {
395         if (when_bar_entry.get_text() != "" && bpb_entry.get_text() != "") {
396                 set_response_sensitive (RESPONSE_ACCEPT, true);
397         } else {
398                 set_response_sensitive (RESPONSE_ACCEPT, false);
399         }
400         return false;
401 }
402
403 void
404 MeterDialog::note_types_change ()
405 {
406         set_response_sensitive (RESPONSE_ACCEPT, true);
407 }
408
409 double
410 MeterDialog::get_bpb ()
411 {
412         double bpb = 0;
413
414         if (sscanf (bpb_entry.get_text().c_str(), "%lf", &bpb) != 1) {
415                 return 0;
416         }
417
418         return bpb;
419 }
420
421 double
422 MeterDialog::get_note_type ()
423 {
424         double note_type = 0;
425         vector<string>::iterator i;
426         string text = note_types.get_active_text();
427
428         for (i = strings.begin(); i != strings.end(); ++i) {
429                 if (text == *i) {
430                         if (sscanf (text.c_str(), "%*[^0-9]%lf", &note_type) != 1) {
431                                 error << string_compose(_("garbaged note type entry (%1)"), text) << endmsg;
432                                 return 0;
433                         } else {
434                                 break;
435                         }
436                 }
437         }
438
439         if (i == strings.end()) {
440                 if (sscanf (text.c_str(), "%lf", &note_type) != 1) {
441                         error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
442                         return 0;
443                 }
444         }
445
446         return note_type;
447 }
448
449 bool
450 MeterDialog::get_bbt_time (Timecode::BBT_Time& requested)
451 {
452         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
453                 return false;
454         }
455
456         requested.beats = 1;
457
458         requested.ticks = 0;
459
460         return true;
461 }