rework tempo dialog formatting, add end bpm entry.
[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
24 #include "gtkmm2ext/utils.h"
25
26 #include "tempo_dialog.h"
27 #include "ui_config.h"
28
29 #include "pbd/i18n.h"
30
31 using namespace std;
32 using namespace Gtk;
33 using namespace Gtkmm2ext;
34 using namespace ARDOUR;
35 using namespace PBD;
36
37 TempoDialog::TempoDialog (TempoMap& map, framepos_t frame, const string&)
38         : ArdourDialog (_("New Tempo"))
39         , _map (&map)
40         , _section (0)
41         , bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0)
42         , bpm_spinner (bpm_adjustment)
43         , end_bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0)
44         , end_bpm_spinner (end_bpm_adjustment)
45         , _end_bpm_label (_("End Beats per Minute:"), ALIGN_LEFT, ALIGN_CENTER)
46         , when_bar_label (_("bar:"), ALIGN_RIGHT, ALIGN_CENTER)
47         , when_beat_label (_("beat:"), ALIGN_RIGHT, ALIGN_CENTER)
48         , pulse_selector_label (_("Pulse:"), ALIGN_LEFT, ALIGN_CENTER)
49         , tap_tempo_button (_("Tap tempo"))
50 {
51         Tempo tempo (map.tempo_at_frame (frame));
52         Timecode::BBT_Time when (map.bbt_at_frame (frame));
53
54         init (when, tempo.note_types_per_minute(), tempo.end_note_types_per_minute(), tempo.note_type(), TempoSection::Constant, true, MusicTime);
55 }
56
57 TempoDialog::TempoDialog (TempoMap& map, TempoSection& section, const string&)
58         : ArdourDialog (_("Edit Tempo"))
59         , _map (&map)
60         , _section (&section)
61         , bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0)
62         , bpm_spinner (bpm_adjustment)
63         , end_bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0)
64         , end_bpm_spinner (end_bpm_adjustment)
65         , _end_bpm_label (_("End Beats per Minute:"), ALIGN_LEFT, ALIGN_CENTER)
66         , when_bar_label (_("bar:"), ALIGN_RIGHT, ALIGN_CENTER)
67         , when_beat_label (_("beat:"), ALIGN_RIGHT, ALIGN_CENTER)
68         , pulse_selector_label (_("Pulse:"), ALIGN_LEFT, ALIGN_CENTER)
69         , tap_tempo_button (_("Tap tempo"))
70 {
71         Timecode::BBT_Time when (map.bbt_at_frame (section.frame()));
72         init (when, section.note_types_per_minute(), section.end_note_types_per_minute(), section.note_type(), section.type()
73               , section.initial() || section.locked_to_meter(), section.position_lock_style());
74 }
75
76 void
77 TempoDialog::init (const Timecode::BBT_Time& when, double bpm, double end_bpm, double note_type, TempoSection::Type type, bool initial, PositionLockStyle style)
78 {
79         vector<string> strings;
80         NoteTypes::iterator x;
81
82         bpm_spinner.set_numeric (true);
83         bpm_spinner.set_digits (3);
84         bpm_spinner.set_wrap (true);
85         bpm_spinner.set_value (bpm);
86         bpm_spinner.set_alignment (1.0);
87
88         end_bpm_spinner.set_numeric (true);
89         end_bpm_spinner.set_digits (3);
90         end_bpm_spinner.set_wrap (true);
91         end_bpm_spinner.set_value (end_bpm);
92         end_bpm_spinner.set_alignment (1.0);
93
94         Gtkmm2ext::set_size_request_to_display_given_text (pulse_selector, _("one-hundred-twenty-eighth"), 3, 6);
95
96         note_types.insert (make_pair (_("whole"), 1.0));
97         strings.push_back (_("whole"));
98         note_types.insert (make_pair (_("second"), 2.0));
99         strings.push_back (_("second"));
100         note_types.insert (make_pair (_("third"), 3.0));
101         strings.push_back (_("third"));
102         note_types.insert (make_pair (_("quarter"), 4.0));
103         strings.push_back (_("quarter"));
104         note_types.insert (make_pair (_("eighth"), 8.0));
105         strings.push_back (_("eighth"));
106         note_types.insert (make_pair (_("sixteenth"), 16.0));
107         strings.push_back (_("sixteenth"));
108         note_types.insert (make_pair (_("thirty-second"), 32.0));
109         strings.push_back (_("thirty-second"));
110         note_types.insert (make_pair (_("sixty-fourth"), 64.0));
111         strings.push_back (_("sixty-fourth"));
112         note_types.insert (make_pair (_("one-hundred-twenty-eighth"), 128.0));
113         strings.push_back (_("one-hundred-twenty-eighth"));
114
115         set_popdown_strings (pulse_selector, strings);
116
117         for (x = note_types.begin(); x != note_types.end(); ++x) {
118                 if (x->second == note_type) {
119                         pulse_selector.set_active_text (x->first);
120                         break;
121                 }
122         }
123
124         if (x == note_types.end()) {
125                 pulse_selector.set_active_text (strings[3]); // "quarter"
126         }
127
128         strings.clear();
129
130         tempo_types.insert (make_pair (_("ramped"), TempoSection::Ramp));
131         strings.push_back (_("ramped"));
132         tempo_types.insert (make_pair (_("constant"), TempoSection::Constant));
133         strings.push_back (_("constant"));
134         set_popdown_strings (tempo_type, strings);
135         TempoTypes::iterator tt;
136         for (tt = tempo_types.begin(); tt != tempo_types.end(); ++tt) {
137                 if (tt->second == type) {
138                         tempo_type.set_active_text (tt->first);
139                         break;
140                 }
141         }
142         if (tt == tempo_types.end()) {
143                 tempo_type.set_active_text (strings[1]); // "constant"
144         }
145
146         strings.clear();
147
148         lock_styles.insert (make_pair (_("music"), MusicTime));
149         strings.push_back (_("music"));
150         lock_styles.insert (make_pair (_("audio"), AudioTime));
151         strings.push_back (_("audio"));
152         set_popdown_strings (lock_style, strings);
153         LockStyles::iterator ls;
154         for (ls = lock_styles.begin(); ls != lock_styles.end(); ++ls) {
155                 if (ls->second == style) {
156                         lock_style.set_active_text (ls->first);
157                         break;
158                 }
159         }
160         if (ls == lock_styles.end()) {
161                 lock_style.set_active_text (strings[0]); // "music"
162         }
163
164         Table* table;
165
166         if (UIConfiguration::instance().get_allow_non_quarter_pulse()) {
167                 table = manage (new Table (5, 7));
168         } else {
169                 table = manage (new Table (5, 6));
170         }
171
172         table->set_spacings (6);
173         table->set_homogeneous (false);
174
175         int row = 0;
176
177         if (UIConfiguration::instance().get_allow_non_quarter_pulse()) {
178                 table->attach (pulse_selector_label, 0, 1, row, row + 1);
179                 table->attach (pulse_selector, 1, 5, row, row + 1);
180
181                 ++row;
182         }
183
184         Label* bpm_label = manage (new Label(_("Start Beats per Minute:"), ALIGN_LEFT, ALIGN_CENTER));
185         table->attach (*bpm_label, 0, 1, row, row + 1);
186         table->attach (bpm_spinner, 1, 5, row, row + 1);
187         ++row;
188
189         table->attach (_end_bpm_label, 0, 1, row, row + 1);
190         table->attach (end_bpm_spinner, 1, 5, row, row + 1);
191         ++row;
192
193         Label* tempo_type_label = manage (new Label(_("Tempo Type:"), ALIGN_LEFT, ALIGN_CENTER));
194         table->attach (*tempo_type_label, 0, 1, row, row + 1);
195         table->attach (tempo_type, 1, 5, row, row + 1);
196
197         ++row;
198
199         char buf[64];
200
201         snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
202         when_bar_entry.set_text (buf);
203         snprintf (buf, sizeof (buf), "%" PRIu32, when.beats);
204         when_beat_entry.set_text (buf);
205
206         if (!initial) {
207                 when_bar_entry.set_width_chars(4);
208                 when_beat_entry.set_width_chars (4);
209                 when_bar_entry.set_alignment (1.0);
210                 when_beat_entry.set_alignment (1.0);
211
212                 when_bar_label.set_name ("MetricLabel");
213                 when_beat_label.set_name ("MetricLabel");
214
215                 table->attach (when_bar_label, 1, 2, row, row+1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
216                 table->attach (when_bar_entry, 2, 3, row, row+1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
217
218                 table->attach (when_beat_label, 3, 4, row, row+1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
219                 table->attach (when_beat_entry, 4, 5, row, row+1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
220
221                 Label* when_label = manage (new Label(_("Tempo begins at"), ALIGN_LEFT, ALIGN_CENTER));
222                 table->attach (*when_label, 0, 1, row, row+1);
223
224                 ++row;
225                 ++row;
226
227                 Label* lock_style_label = manage (new Label(_("Lock Style:"), ALIGN_LEFT, ALIGN_CENTER));
228                 table->attach (*lock_style_label, 0, 1, row, row + 1);
229                 table->attach (lock_style, 1, 5, row, row + 1);
230
231                 --row;
232         }
233
234         get_vbox()->set_border_width (12);
235         get_vbox()->pack_end (*table);
236
237         table->show_all ();
238
239         add_button (Stock::CANCEL, RESPONSE_CANCEL);
240         add_button (Stock::APPLY, RESPONSE_ACCEPT);
241         set_response_sensitive (RESPONSE_ACCEPT, true);
242         set_default_response (RESPONSE_ACCEPT);
243
244         bpm_spinner.show ();
245         end_bpm_spinner.show ();
246         tap_tempo_button.show ();
247         get_vbox()->set_spacing (6);
248         get_vbox()->pack_end (tap_tempo_button);
249         bpm_spinner.grab_focus ();
250
251         set_name ("MetricDialog");
252
253         bpm_spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
254         bpm_spinner.signal_button_press_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_press), false);
255         bpm_spinner.signal_button_release_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_release), false);
256         bpm_spinner.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::bpm_changed));
257         end_bpm_spinner.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::bpm_changed));
258         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
259         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
260         when_beat_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
261         when_beat_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
262         pulse_selector.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::pulse_change));
263         tempo_type.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::tempo_type_change));
264         lock_style.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::lock_style_change));
265         tap_tempo_button.signal_button_press_event().connect (sigc::mem_fun (*this, &TempoDialog::tap_tempo_button_press), false);
266         tap_tempo_button.signal_focus_out_event().connect (sigc::mem_fun (*this, &TempoDialog::tap_tempo_focus_out));
267
268         tempo_type_change();
269
270         tapped = false;
271 }
272
273 bool
274 TempoDialog::is_user_input_valid() const
275 {
276         return (when_beat_entry.get_text() != "")
277                 && (when_bar_entry.get_text() != "")
278                 && (when_bar_entry.get_text() != "0");
279 }
280
281 void
282 TempoDialog::bpm_changed ()
283 {
284         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
285 }
286
287 bool
288 TempoDialog::bpm_button_press (GdkEventButton*)
289 {
290         return false;
291 }
292
293 bool
294 TempoDialog::bpm_button_release (GdkEventButton*)
295 {
296         /* the value has been modified, accept should work now */
297
298         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
299         return false;
300 }
301
302 bool
303 TempoDialog::entry_key_release (GdkEventKey*)
304 {
305         Timecode::BBT_Time bbt;
306         get_bbt_time (bbt);
307
308         if (_section && is_user_input_valid()) {
309                 set_response_sensitive (RESPONSE_ACCEPT, _map->can_solve_bbt (_section, bbt));
310         } else {
311                 set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
312         }
313
314         return false;
315 }
316
317 double
318 TempoDialog::get_bpm ()
319 {
320         return bpm_spinner.get_value ();
321 }
322
323 double
324 TempoDialog::get_end_bpm ()
325 {
326         if (get_tempo_type() == TempoSection::Constant) {
327                 return bpm_spinner.get_value ();
328         }
329
330         return end_bpm_spinner.get_value ();
331 }
332
333 bool
334 TempoDialog::get_bbt_time (Timecode::BBT_Time& requested)
335 {
336         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
337                 return false;
338         }
339
340         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
341                 return false;
342         }
343
344         requested.ticks = 0;
345
346         return true;
347 }
348
349 double
350 TempoDialog::get_note_type ()
351 {
352         NoteTypes::iterator x = note_types.find (pulse_selector.get_active_text());
353
354         if (x == note_types.end()) {
355                 error << string_compose(_("incomprehensible pulse note type (%1)"), pulse_selector.get_active_text()) << endmsg;
356                 return 0;
357         }
358
359         return x->second;
360 }
361
362 TempoSection::Type
363 TempoDialog::get_tempo_type ()
364 {
365         TempoTypes::iterator x = tempo_types.find (tempo_type.get_active_text());
366
367         if (x == tempo_types.end()) {
368                 error << string_compose(_("incomprehensible tempo type (%1)"), tempo_type.get_active_text()) << endmsg;
369                 return TempoSection::Constant;
370         }
371
372         return x->second;
373 }
374
375 PositionLockStyle
376 TempoDialog::get_lock_style ()
377 {
378         LockStyles::iterator x = lock_styles.find (lock_style.get_active_text());
379
380         if (x == lock_styles.end()) {
381                 error << string_compose(_("incomprehensible lock style (%1)"), lock_style.get_active_text()) << endmsg;
382                 return MusicTime;
383         }
384
385         return x->second;
386 }
387
388 void
389 TempoDialog::pulse_change ()
390 {
391         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
392 }
393
394 void
395 TempoDialog::tempo_type_change ()
396 {
397         if (get_tempo_type() == TempoSection::Constant) {
398                 end_bpm_spinner.hide ();
399                 _end_bpm_label.hide();
400         } else {
401                 end_bpm_spinner.show ();
402                 _end_bpm_label.show();
403         }
404
405         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
406 }
407
408 void
409 TempoDialog::lock_style_change ()
410 {
411         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
412 }
413
414 bool
415 TempoDialog::tap_tempo_button_press (GdkEventButton *ev)
416 {
417         double t;
418
419         // Linear least-squares regression
420         if (tapped) {
421                 t = 1e-6 * (g_get_monotonic_time () - first_t); // Subtract first_t to avoid precision problems
422
423                 double n = tap_count;
424                 sum_y += t;
425                 sum_x += n;
426                 sum_xy += n * t;
427                 sum_xx += n * n;
428                 double T = (sum_xy/n - sum_x/n * sum_y/n) / (sum_xx/n - sum_x/n * sum_x/n);
429
430                 if (t - last_t < T / 1.2 || t - last_t > T * 1.2) {
431                         tapped = false;
432                 } else {
433                         bpm_spinner.set_value (60.0 / T);
434                 }
435         }
436         if (!tapped) {
437                 first_t = g_get_monotonic_time ();
438                 t = 0.0;
439                 sum_y = 0.0;
440                 sum_x = 1.0;
441                 sum_xy = 0.0;
442                 sum_xx = 1.0;
443                 tap_count = 1.0;
444
445                 tapped = true;
446         }
447         tap_count++;
448         last_t = t;
449
450         return true;
451 }
452
453 bool
454 TempoDialog::tap_tempo_focus_out (GdkEventFocus* )
455 {
456         tapped = false;
457         return false;
458 }
459
460 MeterDialog::MeterDialog (TempoMap& map, framepos_t frame, const string&)
461         : ArdourDialog (_("New Meter"))
462 {
463         frame = map.round_to_bar(frame, RoundNearest).frame;
464         Timecode::BBT_Time when (map.bbt_at_frame (frame));
465         Meter meter (map.meter_at_frame (frame));
466
467         init (when, meter.divisions_per_bar(), meter.note_divisor(), false, MusicTime);
468 }
469
470 MeterDialog::MeterDialog (TempoMap& map, MeterSection& section, const string&)
471         : ArdourDialog (_("Edit Meter"))
472 {
473         Timecode::BBT_Time when (map.bbt_at_frame (section.frame()));
474
475         init (when, section.divisions_per_bar(), section.note_divisor(), section.initial(), section.position_lock_style());
476 }
477
478 void
479 MeterDialog::init (const Timecode::BBT_Time& when, double bpb, double divisor, bool initial, PositionLockStyle style)
480 {
481         char buf[64];
482         vector<string> strings;
483         NoteTypes::iterator x;
484
485         snprintf (buf, sizeof (buf), "%.2f", bpb);
486         bpb_entry.set_text (buf);
487         bpb_entry.select_region (0, -1);
488         bpb_entry.set_alignment (1.0);
489
490         note_types.insert (make_pair (_("whole"), 1.0));
491         strings.push_back (_("whole"));
492         note_types.insert (make_pair (_("second"), 2.0));
493         strings.push_back (_("second"));
494         note_types.insert (make_pair (_("third"), 3.0));
495         strings.push_back (_("third"));
496         note_types.insert (make_pair (_("quarter"), 4.0));
497         strings.push_back (_("quarter"));
498         note_types.insert (make_pair (_("eighth"), 8.0));
499         strings.push_back (_("eighth"));
500         note_types.insert (make_pair (_("sixteenth"), 16.0));
501         strings.push_back (_("sixteenth"));
502         note_types.insert (make_pair (_("thirty-second"), 32.0));
503         strings.push_back (_("thirty-second"));
504         note_types.insert (make_pair (_("sixty-fourth"), 64.0));
505         strings.push_back (_("sixty-fourth"));
506         note_types.insert (make_pair (_("one-hundred-twenty-eighth"), 128.0));
507         strings.push_back (_("one-hundred-twenty-eighth"));
508
509         set_popdown_strings (note_type, strings);
510
511         for (x = note_types.begin(); x != note_types.end(); ++x) {
512                 if (x->second == divisor) {
513                         note_type.set_active_text (x->first);
514                         break;
515                 }
516         }
517
518         if (x == note_types.end()) {
519                 note_type.set_active_text (strings[3]); // "quarter"
520         }
521
522         strings.clear();
523
524         lock_styles.insert (make_pair (_("music"), MusicTime));
525         strings.push_back (_("music"));
526         lock_styles.insert (make_pair (_("audio"), AudioTime));
527         strings.push_back (_("audio"));
528         set_popdown_strings (lock_style, strings);
529         LockStyles::iterator ls;
530         for (ls = lock_styles.begin(); ls != lock_styles.end(); ++ls) {
531                 if (ls->second == style) {
532                         lock_style.set_active_text (ls->first);
533                         break;
534                 }
535         }
536         if (ls == lock_styles.end()) {
537                 lock_style.set_active_text (strings[0]); // "music"
538         }
539
540         Label* note_label = manage (new Label (_("Note value:"), ALIGN_RIGHT, ALIGN_CENTER));
541         Label* lock_label = manage (new Label (_("Lock style:"), ALIGN_RIGHT, ALIGN_CENTER));
542         Label* bpb_label = manage (new Label (_("Beats per bar:"), ALIGN_RIGHT, ALIGN_CENTER));
543         Table* table = manage (new Table (3, 3));
544         table->set_spacings (6);
545
546         table->attach (*bpb_label, 0, 1, 0, 1, FILL|EXPAND, FILL|EXPAND);
547         table->attach (bpb_entry, 1, 2, 0, 1, FILL|EXPAND, FILL|EXPAND);
548         table->attach (*note_label, 0, 1, 1, 2, FILL|EXPAND, FILL|EXPAND);
549         table->attach (note_type, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND);
550
551         snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
552         when_bar_entry.set_text (buf);
553         when_bar_entry.set_alignment (1.0);
554
555         if (!initial) {
556                 Label* when_label = manage (new Label(_("Meter begins at bar:"), ALIGN_LEFT, ALIGN_CENTER));
557
558                 table->attach (*when_label, 0, 1, 2, 3, FILL | EXPAND, FILL | EXPAND);
559                 table->attach (when_bar_entry, 1, 2, 2, 3, FILL | EXPAND, FILL | EXPAND);
560
561                 table->attach (*lock_label, 0, 1, 3, 4, FILL|EXPAND, FILL|EXPAND);
562                 table->attach (lock_style, 1, 2, 3, 4, FILL|EXPAND, SHRINK);
563         }
564
565         get_vbox()->set_border_width (12);
566         get_vbox()->pack_start (*table, false, false);
567
568         add_button (Stock::CANCEL, RESPONSE_CANCEL);
569         add_button (Stock::APPLY, RESPONSE_ACCEPT);
570         set_response_sensitive (RESPONSE_ACCEPT, true);
571         set_default_response (RESPONSE_ACCEPT);
572
573         get_vbox()->show_all ();
574
575         set_name ("MetricDialog");
576         bpb_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
577         bpb_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
578         bpb_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
579         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
580         when_bar_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
581         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
582         note_type.signal_changed().connect (sigc::mem_fun (*this, &MeterDialog::note_type_change));
583         lock_style.signal_changed().connect (sigc::mem_fun (*this, &MeterDialog::lock_style_change));
584
585 }
586
587 bool
588 MeterDialog::is_user_input_valid() const
589 {
590         return (when_bar_entry.get_text() != "")
591                 && (when_bar_entry.get_text() != "0")
592                 && (bpb_entry.get_text() != "");
593 }
594
595 bool
596 MeterDialog::entry_key_press (GdkEventKey* ev)
597 {
598
599         switch (ev->keyval) {
600
601         case GDK_0:
602         case GDK_1:
603         case GDK_2:
604         case GDK_3:
605         case GDK_4:
606         case GDK_5:
607         case GDK_6:
608         case GDK_7:
609         case GDK_8:
610         case GDK_9:
611         case GDK_KP_0:
612         case GDK_KP_1:
613         case GDK_KP_2:
614         case GDK_KP_3:
615         case GDK_KP_4:
616         case GDK_KP_5:
617         case GDK_KP_6:
618         case GDK_KP_7:
619         case GDK_KP_8:
620         case GDK_KP_9:
621         case GDK_period:
622         case GDK_comma:
623         case  GDK_KP_Delete:
624         case  GDK_KP_Enter:
625         case  GDK_Delete:
626         case  GDK_BackSpace:
627         case  GDK_Escape:
628         case  GDK_Return:
629         case  GDK_Home:
630         case  GDK_End:
631         case  GDK_Left:
632         case  GDK_Right:
633         case  GDK_Num_Lock:
634         case  GDK_Tab:
635                 return FALSE;
636         default:
637                 break;
638         }
639
640         return TRUE;
641 }
642
643 bool
644 MeterDialog::entry_key_release (GdkEventKey*)
645 {
646         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
647         return false;
648 }
649
650 void
651 MeterDialog::note_type_change ()
652 {
653         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
654 }
655
656 void
657 MeterDialog::lock_style_change ()
658 {
659         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
660 }
661
662 double
663 MeterDialog::get_bpb ()
664 {
665         double bpb = 0;
666
667         if (sscanf (bpb_entry.get_text().c_str(), "%lf", &bpb) != 1) {
668                 return 0;
669         }
670
671         return bpb;
672 }
673
674 double
675 MeterDialog::get_note_type ()
676 {
677         NoteTypes::iterator x = note_types.find (note_type.get_active_text());
678
679         if (x == note_types.end()) {
680                 error << string_compose(_("incomprehensible meter note type (%1)"), note_type.get_active_text()) << endmsg;
681                 return 0;
682         }
683
684         return x->second;
685 }
686
687 PositionLockStyle
688 MeterDialog::get_lock_style ()
689 {
690         LockStyles::iterator x = lock_styles.find (lock_style.get_active_text());
691
692         if (x == lock_styles.end()) {
693                 error << string_compose(_("incomprehensible meter lock style (%1)"), lock_style.get_active_text()) << endmsg;
694                 return MusicTime;
695         }
696
697         return x->second;
698 }
699
700 bool
701 MeterDialog::get_bbt_time (Timecode::BBT_Time& requested)
702 {
703         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
704                 return false;
705         }
706
707         requested.beats = 1;
708         requested.ticks = 0;
709
710         return true;
711 }