a5717c5110768f383c20266ca53a5157572986f0
[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         , when_bar_label (_("bar:"), ALIGN_LEFT, ALIGN_CENTER)
41         , when_beat_label (_("beat:"), ALIGN_LEFT, ALIGN_CENTER)
42         , pulse_selector_label (_("Pulse note"), ALIGN_LEFT, ALIGN_CENTER)
43 {
44         Timecode::BBT_Time when;
45         Tempo tempo (map.tempo_at (frame));
46         map.bbt_time (frame, when);
47
48         init (when, tempo.beats_per_minute(), tempo.note_type(), true);
49 }
50
51 TempoDialog::TempoDialog (TempoSection& section, const string & action)
52         : ArdourDialog ("Edit Tempo")
53         , bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0)
54         , bpm_spinner (bpm_adjustment)
55         , when_bar_label (_("bar:"), ALIGN_LEFT, ALIGN_CENTER)
56         , when_beat_label (_("beat:"), ALIGN_LEFT, ALIGN_CENTER)
57         , pulse_selector_label (_("Pulse note"), ALIGN_LEFT, ALIGN_CENTER)
58 {
59         init (section.start(), section.beats_per_minute(), section.note_type(), section.movable());
60 }
61
62 void
63 TempoDialog::init (const Timecode::BBT_Time& when, double bpm, double note_type, bool movable)
64 {
65         vector<string> strings;
66         NoteTypes::iterator x;
67
68         bpm_spinner.set_numeric (true);
69         bpm_spinner.set_digits (2);
70         bpm_spinner.set_wrap (true);
71         bpm_spinner.set_value (bpm);
72
73         note_types.insert (make_pair (_("whole"), 1.0));
74         strings.push_back (_("whole"));
75         note_types.insert (make_pair (_("second"), 2.0));
76         strings.push_back (_("second"));
77         note_types.insert (make_pair (_("third"), 3.0));
78         strings.push_back (_("third"));
79         note_types.insert (make_pair (_("quarter"), 4.0));
80         strings.push_back (_("quarter"));
81         note_types.insert (make_pair (_("eighth"), 8.0));
82         strings.push_back (_("eighth"));
83         note_types.insert (make_pair (_("thirty-second"), 32.0));
84         strings.push_back (_("thirty-second"));
85         note_types.insert (make_pair (_("sixty-fourth"), 64.0));
86         strings.push_back (_("sixty-fourth"));
87         note_types.insert (make_pair (_("one-hundred-twenty-eighth"), 128.0));
88         strings.push_back (_("one-hundred-twenty-eighth"));
89
90         set_popdown_strings (pulse_selector, strings);
91
92         for (x = note_types.begin(); x != note_types.end(); ++x) {
93                 if (x->second == note_type) {
94                         pulse_selector.set_active_text (x->first);
95                         break;
96                 }
97         }
98
99         if (x == note_types.end()) {
100                 pulse_selector.set_active_text (strings[3]); // "quarter"
101         }
102         
103         Table* table = manage (new Table (5, 5));
104         table->set_spacings (6);
105         table->set_homogeneous (false);
106
107         Label* bpm_label = manage (new Label(_("Beats per minute:"), ALIGN_LEFT, ALIGN_CENTER));
108         table->attach (*bpm_label, 0, 1, 0, 1);
109         table->attach (bpm_spinner, 1, 5, 0, 1);
110
111         table->attach (pulse_selector_label, 0, 1, 1, 2);
112         table->attach (pulse_selector, 1, 5, 1, 2);
113
114         if (movable) {
115                 char buf[64];
116
117                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
118                 when_bar_entry.set_text (buf);
119                 snprintf (buf, sizeof (buf), "%" PRIu32, when.beats);
120                 when_beat_entry.set_text (buf);
121
122                 when_bar_entry.set_name ("MetricEntry");
123                 when_beat_entry.set_name ("MetricEntry");
124
125                 when_bar_entry.set_width_chars(4);
126                 when_beat_entry.set_width_chars (4);
127
128                 when_bar_label.set_name ("MetricLabel");
129                 when_beat_label.set_name ("MetricLabel");
130
131                 table->attach (when_bar_label, 1, 2, 2, 3, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
132                 table->attach (when_bar_entry, 2, 3, 2, 3, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
133
134                 table->attach (when_beat_label, 3, 4, 2, 3, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
135                 table->attach (when_beat_entry, 4, 5, 2, 3, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
136
137                 Label* when_label = manage (new Label(_("Tempo begins at"), ALIGN_LEFT, ALIGN_CENTER));
138                 table->attach (*when_label, 0, 1, 2, 3);
139         }
140
141         get_vbox()->set_border_width (12);
142         get_vbox()->pack_end (*table);
143         table->show_all ();
144
145         add_button (Stock::CANCEL, RESPONSE_CANCEL);
146         add_button (Stock::APPLY, RESPONSE_ACCEPT);
147         set_response_sensitive (RESPONSE_ACCEPT, false);
148         set_default_response (RESPONSE_ACCEPT);
149
150         bpm_spinner.show ();
151
152         set_name ("MetricDialog");
153
154         bpm_spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
155         bpm_spinner.signal_button_press_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_press), false);
156         bpm_spinner.signal_button_release_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_release), false);
157         bpm_spinner.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::bpm_changed));
158         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
159         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
160         when_beat_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
161         when_beat_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
162         pulse_selector.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::pulse_change));
163 }
164
165 void
166 TempoDialog::bpm_changed ()
167 {
168         set_response_sensitive (RESPONSE_ACCEPT, true);
169 }
170
171 bool
172 TempoDialog::bpm_button_press (GdkEventButton*)
173 {
174         return false;
175 }
176
177 bool
178 TempoDialog::bpm_button_release (GdkEventButton*)
179 {
180         /* the value has been modified, accept should work now */
181
182         set_response_sensitive (RESPONSE_ACCEPT, true);
183         return false;
184 }
185
186 bool
187 TempoDialog::entry_key_release (GdkEventKey*)
188 {
189         if (when_beat_entry.get_text() != "" && when_bar_entry.get_text() != "") {
190                 set_response_sensitive (RESPONSE_ACCEPT, true);
191         } else {
192                 set_response_sensitive (RESPONSE_ACCEPT, false);
193         }
194         return false;
195 }
196
197 double
198 TempoDialog::get_bpm ()
199 {
200         return bpm_spinner.get_value ();
201 }
202
203 bool
204 TempoDialog::get_bbt_time (Timecode::BBT_Time& requested)
205 {
206         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
207                 return false;
208         }
209
210         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
211                 return false;
212         }
213
214         requested.ticks = 0;
215
216         return true;
217 }
218
219 double
220 TempoDialog::get_note_type ()
221 {
222         NoteTypes::iterator x = note_types.find (pulse_selector.get_active_text());
223         
224         if (x == note_types.end()) {
225                 error << string_compose(_("incomprehensible pulse note type (%1)"), pulse_selector.get_active_text()) << endmsg;
226                 return 0;
227         }
228
229         return x->second;
230 }
231
232 void
233 TempoDialog::pulse_change ()
234 {
235         set_response_sensitive (RESPONSE_ACCEPT, true);
236 }
237
238
239 MeterDialog::MeterDialog (TempoMap& map, framepos_t frame, const string & action)
240         : ArdourDialog ("New Meter")
241 {
242         Timecode::BBT_Time when;
243         frame = map.round_to_bar(frame,0);
244         Meter meter (map.meter_at(frame));
245
246         map.bbt_time (frame, when);
247         init (when, meter.divisions_per_bar(), meter.note_divisor(), true);
248 }
249
250 MeterDialog::MeterDialog (MeterSection& section, const string & action)
251         : ArdourDialog ("Edit Meter")
252 {
253         init (section.start(), section.divisions_per_bar(), section.note_divisor(), section.movable());
254 }
255
256 void
257 MeterDialog::init (const Timecode::BBT_Time& when, double bpb, double divisor, bool movable)
258 {
259         char buf[64];
260         vector<string> strings;
261         NoteTypes::iterator x;
262
263         snprintf (buf, sizeof (buf), "%.2f", bpb);
264         bpb_entry.set_text (buf);
265         bpb_entry.select_region (0, -1);
266
267         note_types.insert (make_pair (_("whole"), 1.0));
268         strings.push_back (_("whole"));
269         note_types.insert (make_pair (_("second"), 2.0));
270         strings.push_back (_("second"));
271         note_types.insert (make_pair (_("third"), 3.0));
272         strings.push_back (_("third"));
273         note_types.insert (make_pair (_("quarter"), 4.0));
274         strings.push_back (_("quarter"));
275         note_types.insert (make_pair (_("eighth"), 8.0));
276         strings.push_back (_("eighth"));
277         note_types.insert (make_pair (_("thirty-second"), 32.0));
278         strings.push_back (_("thirty-second"));
279         note_types.insert (make_pair (_("sixty-fourth"), 64.0));
280         strings.push_back (_("sixty-fourth"));
281         note_types.insert (make_pair (_("one-hundred-twenty-eighth"), 128.0));
282         strings.push_back (_("one-hundred-twenty-eighth"));
283
284         set_popdown_strings (note_type, strings);
285
286         for (x = note_types.begin(); x != note_types.end(); ++x) {
287                 if (x->second == divisor) {
288                         note_type.set_active_text (x->first);
289                         break;
290                 }
291         }
292         
293         if (x == note_types.end()) {
294                 note_type.set_active_text (strings[3]); // "quarter"
295         }
296
297         Label* note_label = manage (new Label (_("Note value:"), ALIGN_LEFT, ALIGN_CENTER));
298         Label* bpb_label = manage (new Label (_("Beats per bar:"), ALIGN_LEFT, ALIGN_CENTER));
299         Table* table = manage (new Table (3, 2));
300         table->set_spacings (6);
301
302         table->attach (*bpb_label, 0, 1, 0, 1, FILL|EXPAND, FILL|EXPAND);
303         table->attach (bpb_entry, 1, 2, 0, 1, FILL|EXPAND, FILL|EXPAND);
304         table->attach (*note_label, 0, 1, 1, 2, FILL|EXPAND, FILL|EXPAND);
305         table->attach (note_type, 1, 2, 1, 2, FILL|EXPAND, SHRINK);
306
307         if (movable) {
308                 char buf[64];
309
310                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
311                 when_bar_entry.set_text (buf);
312                 when_bar_entry.set_name ("MetricEntry");
313
314                 Label* when_label = manage (new Label(_("Meter begins at bar:"), ALIGN_LEFT, ALIGN_CENTER));
315
316                 table->attach (*when_label, 0, 1, 2, 3, FILL | EXPAND, FILL | EXPAND);
317                 table->attach (when_bar_entry, 1, 2, 2, 3, FILL | EXPAND, FILL | EXPAND);
318         } else {
319                 when_bar_entry.set_text ("0");
320         }
321
322         get_vbox()->set_border_width (12);
323         get_vbox()->pack_start (*table, false, false);
324
325         bpb_entry.set_name ("MetricEntry");
326
327         add_button (Stock::CANCEL, RESPONSE_CANCEL);
328         add_button (Stock::APPLY, RESPONSE_ACCEPT);
329         set_response_sensitive (RESPONSE_ACCEPT, false);
330         set_default_response (RESPONSE_ACCEPT);
331
332         get_vbox()->show_all ();
333
334         set_name ("MetricDialog");
335         bpb_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
336         bpb_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
337         bpb_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
338         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
339         when_bar_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
340         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
341         note_type.signal_changed().connect (sigc::mem_fun (*this, &MeterDialog::note_type_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_type_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         NoteTypes::iterator x = note_types.find (note_type.get_active_text());
425         
426         if (x == note_types.end()) {
427                 error << string_compose(_("incomprehensible meter note type (%1)"), note_type.get_active_text()) << endmsg;
428                 return 0;
429         }
430
431         return x->second;
432 }
433
434 bool
435 MeterDialog::get_bbt_time (Timecode::BBT_Time& requested)
436 {
437         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
438                 return false;
439         }
440
441         requested.beats = 1;
442
443         requested.ticks = 0;
444
445         return true;
446 }