Only allow LV2 touch events for control inputs
[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, samplepos_t sample, 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_sample (sample));
52         Timecode::BBT_Time when (map.bbt_at_sample (sample));
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_sample (section.sample()));
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         tap_tempo_button.can_focus ();
250         tap_tempo_button.grab_focus ();
251
252         set_name ("MetricDialog");
253
254         bpm_spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
255         bpm_spinner.signal_button_press_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_press), false);
256         bpm_spinner.signal_button_release_event().connect (sigc::mem_fun (*this, &TempoDialog::bpm_button_release), false);
257         bpm_spinner.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::bpm_changed));
258         end_bpm_spinner.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::bpm_changed));
259         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
260         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
261         when_beat_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
262         when_beat_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &TempoDialog::entry_key_release), false);
263         pulse_selector.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::pulse_change));
264         tempo_type.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::tempo_type_change));
265         lock_style.signal_changed().connect (sigc::mem_fun (*this, &TempoDialog::lock_style_change));
266         tap_tempo_button.signal_button_press_event().connect (sigc::mem_fun (*this, &TempoDialog::tap_tempo_button_press), false);
267         tap_tempo_button.signal_key_press_event().connect (sigc::mem_fun (*this, &TempoDialog::tap_tempo_key_press), false);
268         tap_tempo_button.signal_focus_out_event().connect (sigc::mem_fun (*this, &TempoDialog::tap_tempo_focus_out));
269
270         tempo_type_change();
271
272         tapped = false;
273 }
274
275 bool
276 TempoDialog::is_user_input_valid() const
277 {
278         return (when_beat_entry.get_text() != "")
279                 && (when_bar_entry.get_text() != "")
280                 && (when_bar_entry.get_text() != "0");
281 }
282
283 void
284 TempoDialog::bpm_changed ()
285 {
286         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
287 }
288
289 bool
290 TempoDialog::bpm_button_press (GdkEventButton*)
291 {
292         return false;
293 }
294
295 bool
296 TempoDialog::bpm_button_release (GdkEventButton*)
297 {
298         /* the value has been modified, accept should work now */
299
300         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
301         return false;
302 }
303
304 bool
305 TempoDialog::entry_key_release (GdkEventKey*)
306 {
307         Timecode::BBT_Time bbt;
308         get_bbt_time (bbt);
309
310         if (_section && is_user_input_valid()) {
311                 set_response_sensitive (RESPONSE_ACCEPT, _map->can_solve_bbt (_section, bbt));
312         } else {
313                 set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
314         }
315
316         return false;
317 }
318
319 double
320 TempoDialog::get_bpm ()
321 {
322         return bpm_spinner.get_value ();
323 }
324
325 double
326 TempoDialog::get_end_bpm ()
327 {
328         if (get_tempo_type() == TempoSection::Constant) {
329                 return bpm_spinner.get_value ();
330         }
331
332         return end_bpm_spinner.get_value ();
333 }
334
335 bool
336 TempoDialog::get_bbt_time (Timecode::BBT_Time& requested)
337 {
338         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
339                 return false;
340         }
341
342         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
343                 return false;
344         }
345
346         requested.ticks = 0;
347
348         return true;
349 }
350
351 double
352 TempoDialog::get_note_type ()
353 {
354         NoteTypes::iterator x = note_types.find (pulse_selector.get_active_text());
355
356         if (x == note_types.end()) {
357                 error << string_compose(_("incomprehensible pulse note type (%1)"), pulse_selector.get_active_text()) << endmsg;
358                 return 0;
359         }
360
361         return x->second;
362 }
363
364 TempoSection::Type
365 TempoDialog::get_tempo_type ()
366 {
367         TempoTypes::iterator x = tempo_types.find (tempo_type.get_active_text());
368
369         if (x == tempo_types.end()) {
370                 error << string_compose(_("incomprehensible tempo type (%1)"), tempo_type.get_active_text()) << endmsg;
371                 return TempoSection::Constant;
372         }
373
374         return x->second;
375 }
376
377 PositionLockStyle
378 TempoDialog::get_lock_style ()
379 {
380         LockStyles::iterator x = lock_styles.find (lock_style.get_active_text());
381
382         if (x == lock_styles.end()) {
383                 error << string_compose(_("incomprehensible lock style (%1)"), lock_style.get_active_text()) << endmsg;
384                 return MusicTime;
385         }
386
387         return x->second;
388 }
389
390 void
391 TempoDialog::pulse_change ()
392 {
393         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
394 }
395
396 void
397 TempoDialog::tempo_type_change ()
398 {
399         if (get_tempo_type() == TempoSection::Constant) {
400                 end_bpm_spinner.hide ();
401                 _end_bpm_label.hide();
402         } else {
403                 end_bpm_spinner.show ();
404                 _end_bpm_label.show();
405         }
406
407         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
408 }
409
410 void
411 TempoDialog::lock_style_change ()
412 {
413         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
414 }
415
416 bool
417 TempoDialog::tap_tempo_key_press (GdkEventKey*)
418 {
419         tap_tempo ();
420         return false;
421 }
422
423 bool
424 TempoDialog::tap_tempo_button_press (GdkEventButton* ev)
425 {
426         if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
427                 return true;
428         }
429         if (ev->button != 1) {
430                 return true;
431         }
432         tap_tempo ();
433         return false; // grab focus
434 }
435
436 void
437 TempoDialog::tap_tempo ()
438 {
439         double t;
440
441         // Linear least-squares regression
442         if (tapped) {
443                 t = 1e-6 * (g_get_monotonic_time () - first_t); // Subtract first_t to avoid precision problems
444
445                 double n = tap_count;
446                 sum_y += t;
447                 sum_x += n;
448                 sum_xy += n * t;
449                 sum_xx += n * n;
450                 double T = (sum_xy/n - sum_x/n * sum_y/n) / (sum_xx/n - sum_x/n * sum_x/n);
451
452                 if (t - last_t < T / 1.2 || t - last_t > T * 1.2) {
453                         tapped = false;
454                 } else {
455                         bpm_spinner.set_value (60.0 / T);
456                 }
457         }
458         if (!tapped) {
459                 first_t = g_get_monotonic_time ();
460                 t = 0.0;
461                 sum_y = 0.0;
462                 sum_x = 1.0;
463                 sum_xy = 0.0;
464                 sum_xx = 1.0;
465                 tap_count = 1.0;
466
467                 tapped = true;
468         }
469         tap_count++;
470         last_t = t;
471 }
472
473 bool
474 TempoDialog::tap_tempo_focus_out (GdkEventFocus* )
475 {
476         tapped = false;
477         return false;
478 }
479
480 MeterDialog::MeterDialog (TempoMap& map, samplepos_t sample, const string&)
481         : ArdourDialog (_("New Meter"))
482 {
483         sample = map.round_to_bar(sample, RoundNearest).sample;
484         Timecode::BBT_Time when (map.bbt_at_sample (sample));
485         Meter meter (map.meter_at_sample (sample));
486
487         init (when, meter.divisions_per_bar(), meter.note_divisor(), false, MusicTime);
488 }
489
490 MeterDialog::MeterDialog (TempoMap& map, MeterSection& section, const string&)
491         : ArdourDialog (_("Edit Meter"))
492 {
493         Timecode::BBT_Time when (map.bbt_at_sample (section.sample()));
494
495         init (when, section.divisions_per_bar(), section.note_divisor(), section.initial(), section.position_lock_style());
496 }
497
498 void
499 MeterDialog::init (const Timecode::BBT_Time& when, double bpb, double divisor, bool initial, PositionLockStyle style)
500 {
501         char buf[64];
502         vector<string> strings;
503         NoteTypes::iterator x;
504
505         snprintf (buf, sizeof (buf), "%.2f", bpb);
506         bpb_entry.set_text (buf);
507         bpb_entry.select_region (0, -1);
508         bpb_entry.set_alignment (1.0);
509
510         note_types.insert (make_pair (_("whole"), 1.0));
511         strings.push_back (_("whole"));
512         note_types.insert (make_pair (_("second"), 2.0));
513         strings.push_back (_("second"));
514         note_types.insert (make_pair (_("third"), 3.0));
515         strings.push_back (_("third"));
516         note_types.insert (make_pair (_("quarter"), 4.0));
517         strings.push_back (_("quarter"));
518         note_types.insert (make_pair (_("eighth"), 8.0));
519         strings.push_back (_("eighth"));
520         note_types.insert (make_pair (_("sixteenth"), 16.0));
521         strings.push_back (_("sixteenth"));
522         note_types.insert (make_pair (_("thirty-second"), 32.0));
523         strings.push_back (_("thirty-second"));
524         note_types.insert (make_pair (_("sixty-fourth"), 64.0));
525         strings.push_back (_("sixty-fourth"));
526         note_types.insert (make_pair (_("one-hundred-twenty-eighth"), 128.0));
527         strings.push_back (_("one-hundred-twenty-eighth"));
528
529         set_popdown_strings (note_type, strings);
530
531         for (x = note_types.begin(); x != note_types.end(); ++x) {
532                 if (x->second == divisor) {
533                         note_type.set_active_text (x->first);
534                         break;
535                 }
536         }
537
538         if (x == note_types.end()) {
539                 note_type.set_active_text (strings[3]); // "quarter"
540         }
541
542         strings.clear();
543
544         lock_styles.insert (make_pair (_("music"), MusicTime));
545         strings.push_back (_("music"));
546         lock_styles.insert (make_pair (_("audio"), AudioTime));
547         strings.push_back (_("audio"));
548         set_popdown_strings (lock_style, strings);
549         LockStyles::iterator ls;
550         for (ls = lock_styles.begin(); ls != lock_styles.end(); ++ls) {
551                 if (ls->second == style) {
552                         lock_style.set_active_text (ls->first);
553                         break;
554                 }
555         }
556         if (ls == lock_styles.end()) {
557                 lock_style.set_active_text (strings[0]); // "music"
558         }
559
560         Label* note_label = manage (new Label (_("Note value:"), ALIGN_RIGHT, ALIGN_CENTER));
561         Label* lock_label = manage (new Label (_("Lock style:"), ALIGN_RIGHT, ALIGN_CENTER));
562         Label* bpb_label = manage (new Label (_("Beats per bar:"), ALIGN_RIGHT, ALIGN_CENTER));
563         Table* table = manage (new Table (3, 3));
564         table->set_spacings (6);
565
566         table->attach (*bpb_label, 0, 1, 0, 1, FILL|EXPAND, FILL|EXPAND);
567         table->attach (bpb_entry, 1, 2, 0, 1, FILL|EXPAND, FILL|EXPAND);
568         table->attach (*note_label, 0, 1, 1, 2, FILL|EXPAND, FILL|EXPAND);
569         table->attach (note_type, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND);
570
571         snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
572         when_bar_entry.set_text (buf);
573         when_bar_entry.set_alignment (1.0);
574
575         if (!initial) {
576                 Label* when_label = manage (new Label(_("Meter begins at bar:"), ALIGN_LEFT, ALIGN_CENTER));
577
578                 table->attach (*when_label, 0, 1, 2, 3, FILL | EXPAND, FILL | EXPAND);
579                 table->attach (when_bar_entry, 1, 2, 2, 3, FILL | EXPAND, FILL | EXPAND);
580
581                 table->attach (*lock_label, 0, 1, 3, 4, FILL|EXPAND, FILL|EXPAND);
582                 table->attach (lock_style, 1, 2, 3, 4, FILL|EXPAND, SHRINK);
583         }
584
585         get_vbox()->set_border_width (12);
586         get_vbox()->pack_start (*table, false, false);
587
588         add_button (Stock::CANCEL, RESPONSE_CANCEL);
589         add_button (Stock::APPLY, RESPONSE_ACCEPT);
590         set_response_sensitive (RESPONSE_ACCEPT, true);
591         set_default_response (RESPONSE_ACCEPT);
592
593         get_vbox()->show_all ();
594
595         set_name ("MetricDialog");
596         bpb_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
597         bpb_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
598         bpb_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
599         when_bar_entry.signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
600         when_bar_entry.signal_key_press_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_press), false);
601         when_bar_entry.signal_key_release_event().connect (sigc::mem_fun (*this, &MeterDialog::entry_key_release));
602         note_type.signal_changed().connect (sigc::mem_fun (*this, &MeterDialog::note_type_change));
603         lock_style.signal_changed().connect (sigc::mem_fun (*this, &MeterDialog::lock_style_change));
604
605 }
606
607 bool
608 MeterDialog::is_user_input_valid() const
609 {
610         return (when_bar_entry.get_text() != "")
611                 && (when_bar_entry.get_text() != "0")
612                 && (bpb_entry.get_text() != "");
613 }
614
615 bool
616 MeterDialog::entry_key_press (GdkEventKey* ev)
617 {
618
619         switch (ev->keyval) {
620
621         case GDK_0:
622         case GDK_1:
623         case GDK_2:
624         case GDK_3:
625         case GDK_4:
626         case GDK_5:
627         case GDK_6:
628         case GDK_7:
629         case GDK_8:
630         case GDK_9:
631         case GDK_KP_0:
632         case GDK_KP_1:
633         case GDK_KP_2:
634         case GDK_KP_3:
635         case GDK_KP_4:
636         case GDK_KP_5:
637         case GDK_KP_6:
638         case GDK_KP_7:
639         case GDK_KP_8:
640         case GDK_KP_9:
641         case GDK_period:
642         case GDK_comma:
643         case  GDK_KP_Delete:
644         case  GDK_KP_Enter:
645         case  GDK_Delete:
646         case  GDK_BackSpace:
647         case  GDK_Escape:
648         case  GDK_Return:
649         case  GDK_Home:
650         case  GDK_End:
651         case  GDK_Left:
652         case  GDK_Right:
653         case  GDK_Num_Lock:
654         case  GDK_Tab:
655                 return FALSE;
656         default:
657                 break;
658         }
659
660         return TRUE;
661 }
662
663 bool
664 MeterDialog::entry_key_release (GdkEventKey*)
665 {
666         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
667         return false;
668 }
669
670 void
671 MeterDialog::note_type_change ()
672 {
673         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
674 }
675
676 void
677 MeterDialog::lock_style_change ()
678 {
679         set_response_sensitive (RESPONSE_ACCEPT, is_user_input_valid());
680 }
681
682 double
683 MeterDialog::get_bpb ()
684 {
685         double bpb = 0;
686
687         if (sscanf (bpb_entry.get_text().c_str(), "%lf", &bpb) != 1) {
688                 return 0;
689         }
690
691         return bpb;
692 }
693
694 double
695 MeterDialog::get_note_type ()
696 {
697         NoteTypes::iterator x = note_types.find (note_type.get_active_text());
698
699         if (x == note_types.end()) {
700                 error << string_compose(_("incomprehensible meter note type (%1)"), note_type.get_active_text()) << endmsg;
701                 return 0;
702         }
703
704         return x->second;
705 }
706
707 PositionLockStyle
708 MeterDialog::get_lock_style ()
709 {
710         LockStyles::iterator x = lock_styles.find (lock_style.get_active_text());
711
712         if (x == lock_styles.end()) {
713                 error << string_compose(_("incomprehensible meter lock style (%1)"), lock_style.get_active_text()) << endmsg;
714                 return MusicTime;
715         }
716
717         return x->second;
718 }
719
720 bool
721 MeterDialog::get_bbt_time (Timecode::BBT_Time& requested)
722 {
723         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
724                 return false;
725         }
726
727         requested.beats = 1;
728         requested.ticks = 0;
729
730         return true;
731 }