Apply patch from mantis 2766 to fix apply sensitivity when editing the meter marker...
[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, nframes_t frame, const string & action)
37         : ArdourDialog (_("edit tempo")),
38           bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 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         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 ("tempo dialog"),
54           bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 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 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, true);
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 (bind (mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
143         bpm_spinner.signal_button_press_event().connect (mem_fun (*this, &TempoDialog::bpm_button_press), false);
144         bpm_spinner.signal_button_release_event().connect (mem_fun (*this, &TempoDialog::bpm_button_release), false);
145         when_bar_entry.signal_activate().connect (bind (mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
146         when_bar_entry.signal_key_release_event().connect (mem_fun (*this, &TempoDialog::entry_key_release), false);
147         when_beat_entry.signal_activate().connect (bind (mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
148         when_beat_entry.signal_key_release_event().connect (mem_fun (*this, &TempoDialog::entry_key_release), false);
149         note_types.signal_changed().connect (mem_fun (*this, &TempoDialog::note_types_change));
150 }
151
152 bool
153 TempoDialog::bpm_button_press (GdkEventButton*)
154 {
155         return false;
156 }
157
158 bool
159 TempoDialog::bpm_button_release (GdkEventButton*)
160 {       
161         /* the value has been modified, accept should work now */
162
163         set_response_sensitive (RESPONSE_ACCEPT, true);
164         return false;
165 }
166
167 bool
168 TempoDialog::entry_key_release (GdkEventKey*)
169 {       
170         if (when_beat_entry.get_text() != "" && when_bar_entry.get_text() != "") {
171                 set_response_sensitive (RESPONSE_ACCEPT, true);
172         } else {
173                 set_response_sensitive (RESPONSE_ACCEPT, false);
174         }
175         return false;
176 }
177
178 double 
179 TempoDialog::get_bpm ()
180 {
181         return bpm_spinner.get_value ();
182 }       
183
184 bool
185 TempoDialog::get_bbt_time (BBT_Time& requested)
186 {
187         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
188                 return false;
189         }
190         
191         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
192                 return false;
193         }
194
195         requested.ticks = 0;
196
197         return true;
198 }
199
200 double
201 TempoDialog::get_note_type ()
202 {
203         double note_type = 0;
204         vector<string>::iterator i;
205         string text = note_types.get_active_text();
206         
207         for (i = strings.begin(); i != strings.end(); ++i) {
208                 if (text == *i) {
209                         if (sscanf (text.c_str(), "%*[^0-9]%lf", &note_type) != 1) {
210                                 error << string_compose(_("garbaged note type entry (%1)"), text) << endmsg;
211                                 return 0;
212                         } else {
213                                 break;
214                         }
215                 }
216         } 
217         
218         if (i == strings.end()) {
219                 if (sscanf (text.c_str(), "%lf", &note_type) != 1) {
220                         error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
221                         return 0;
222                 }
223         }
224
225         return note_type;
226 }
227
228 void
229 TempoDialog::note_types_change ()
230 {
231         set_response_sensitive (RESPONSE_ACCEPT, true);
232 }
233
234
235 MeterDialog::MeterDialog (TempoMap& map, nframes_t frame, const string & action)
236         : ArdourDialog ("meter dialog"),
237           ok_button (action),
238           cancel_button (_("Cancel"))
239 {
240         BBT_Time when;
241         frame = map.round_to_bar(frame,0); 
242         Meter meter (map.meter_at(frame));
243
244         map.bbt_time (frame, when);
245         init (when, meter.beats_per_bar(), meter.note_divisor(), true);
246 }
247
248 MeterDialog::MeterDialog (MeterSection& section, const string & action)
249         : ArdourDialog ("meter dialog"),
250           ok_button (action),
251           cancel_button (_("Cancel"))
252 {
253         init (section.start(), section.beats_per_bar(), section.note_divisor(), section.movable());
254 }
255
256 void
257 MeterDialog::init (const BBT_Time& when, double bpb, double note_type, bool movable)
258 {
259         snprintf (buf, sizeof (buf), "%.2f", bpb);
260         bpb_entry.set_text (buf);
261         bpb_entry.select_region (0, -1);
262
263         strings.push_back (_("whole (1)"));
264         strings.push_back (_("second (2)"));
265         strings.push_back (_("third (3)"));
266         strings.push_back (_("quarter (4)"));
267         strings.push_back (_("eighth (8)"));
268         strings.push_back (_("sixteenth (16)"));
269         strings.push_back (_("thirty-second (32)"));
270         
271         set_popdown_strings (note_types, strings, true);
272
273         if (note_type == 1.0f) {
274                 note_types.set_active_text (_("whole (1)"));
275         } else if (note_type == 2.0f) {
276                 note_types.set_active_text (_("second (2)"));
277         } else if (note_type == 3.0f) {
278                 note_types.set_active_text (_("third (3)"));
279         } else if (note_type == 4.0f) {
280                 note_types.set_active_text (_("quarter (4)"));
281         } else if (note_type == 8.0f) {
282                 note_types.set_active_text (_("eighth (8)"));
283         } else if (note_type == 16.0f) {
284                 note_types.set_active_text (_("sixteenth (16)"));
285         } else if (note_type == 32.0f) {
286                 note_types.set_active_text (_("thirty-second (32)"));
287         } else {
288                 note_types.set_active_text (_("quarter (4)"));
289         }
290
291         Label* note_label = manage (new Label (_("Note value:"), ALIGN_LEFT, ALIGN_CENTER));
292         Label* bpb_label = manage (new Label (_("Beats per bar:"), ALIGN_LEFT, ALIGN_CENTER));
293         Table* table = manage (new Table (3, 2));
294         table->set_spacings (6);
295
296         table->attach (*bpb_label, 0, 1, 0, 1, FILL|EXPAND, FILL|EXPAND);
297         table->attach (bpb_entry, 1, 2, 0, 1, FILL|EXPAND, FILL|EXPAND);
298         table->attach (*note_label, 0, 1, 1, 2, FILL|EXPAND, FILL|EXPAND);
299         table->attach (note_types, 1, 2, 1, 2, FILL|EXPAND, SHRINK);
300
301         if (movable) {
302                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
303                 when_bar_entry.set_text (buf);
304                 when_bar_entry.set_name ("MetricEntry");
305                 
306                 Label* when_label = manage (new Label(_("Meter begins at bar:"), ALIGN_LEFT, ALIGN_CENTER));
307
308                 table->attach (*when_label, 0, 1, 2, 3, FILL | EXPAND, FILL | EXPAND);
309                 table->attach (when_bar_entry, 1, 2, 2, 3, FILL | EXPAND, FILL | EXPAND);
310         } else {
311                 when_bar_entry.set_text ("0");
312         }
313
314         get_vbox()->set_border_width (12);
315         get_vbox()->pack_start (*table, false, false);
316
317         bpb_entry.set_name ("MetricEntry");
318
319         add_button (Stock::CANCEL, RESPONSE_CANCEL);
320         add_button (Stock::APPLY, RESPONSE_ACCEPT);
321         set_response_sensitive (RESPONSE_ACCEPT, false);
322         set_default_response (RESPONSE_ACCEPT);
323
324         get_vbox()->show_all ();
325
326         set_name ("MetricDialog");
327         bpb_entry.signal_activate().connect (bind (mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
328         bpb_entry.signal_key_press_event().connect (mem_fun (*this, &MeterDialog::entry_key_press), false);
329         bpb_entry.signal_key_release_event().connect (mem_fun (*this, &MeterDialog::entry_key_release));
330         when_bar_entry.signal_activate().connect (bind (mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
331         when_bar_entry.signal_key_press_event().connect (mem_fun (*this, &MeterDialog::entry_key_press), false);
332         when_bar_entry.signal_key_release_event().connect (mem_fun (*this, &MeterDialog::entry_key_release));
333
334         note_types.signal_changed().connect (mem_fun (*this, &MeterDialog::note_types_change));
335 }
336
337 bool
338 MeterDialog::entry_key_press (GdkEventKey* ev)
339 {
340
341         switch (ev->keyval) { 
342
343         case GDK_0:
344         case GDK_1:
345         case GDK_2:
346         case GDK_3:
347         case GDK_4:
348         case GDK_5:
349         case GDK_6:
350         case GDK_7:
351         case GDK_8:
352         case GDK_9:
353         case GDK_KP_0:
354         case GDK_KP_1:
355         case GDK_KP_2:
356         case GDK_KP_3:
357         case GDK_KP_4:
358         case GDK_KP_5:
359         case GDK_KP_6:
360         case GDK_KP_7:
361         case GDK_KP_8:
362         case GDK_KP_9:
363         case GDK_period:
364         case GDK_comma:
365         case  GDK_KP_Delete:
366         case  GDK_KP_Enter:
367         case  GDK_Delete:
368         case  GDK_BackSpace:
369         case  GDK_Escape:
370         case  GDK_Return:
371         case  GDK_Home:
372         case  GDK_End:
373         case  GDK_Left:
374         case  GDK_Right:
375         case  GDK_Num_Lock:
376         case  GDK_Tab:
377                 return FALSE;
378         default:
379                 break;
380         }
381
382         return TRUE;
383 }
384
385 bool
386 MeterDialog::entry_key_release (GdkEventKey*)
387 {
388         if (when_bar_entry.get_text() != "" && bpb_entry.get_text() != "") {
389                 set_response_sensitive (RESPONSE_ACCEPT, true);
390         } else {
391                 set_response_sensitive (RESPONSE_ACCEPT, false);
392         }
393         return false;
394 }
395
396 void
397 MeterDialog::note_types_change ()
398 {
399         set_response_sensitive (RESPONSE_ACCEPT, true);
400 }
401
402 double
403 MeterDialog::get_bpb ()
404 {
405         double bpb = 0;
406         
407         if (sscanf (bpb_entry.get_text().c_str(), "%lf", &bpb) != 1) {
408                 return 0;
409         }
410
411         return bpb;
412 }
413         
414 double
415 MeterDialog::get_note_type ()
416 {
417         double note_type = 0;
418         vector<string>::iterator i;
419         string text = note_types.get_active_text();
420         
421         for (i = strings.begin(); i != strings.end(); ++i) {
422                 if (text == *i) {
423                         if (sscanf (text.c_str(), "%*[^0-9]%lf", &note_type) != 1) {
424                                 error << string_compose(_("garbaged note type entry (%1)"), text) << endmsg;
425                                 return 0;
426                         } else {
427                                 break;
428                         }
429                 }
430         } 
431         
432         if (i == strings.end()) {
433                 if (sscanf (text.c_str(), "%lf", &note_type) != 1) {
434                         error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
435                         return 0;
436                 }
437         }
438
439         return note_type;
440 }
441
442 bool
443 MeterDialog::get_bbt_time (BBT_Time& requested)
444 {
445
446         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
447                 return false;
448         }
449         
450         requested.beats = 1;
451
452         requested.ticks = 0;
453
454         return true;
455 }