Merged with trunk R1719.
[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 Gtk;
31 using namespace Gtkmm2ext;
32 using namespace ARDOUR;
33 using namespace PBD;
34
35 TempoDialog::TempoDialog (TempoMap& map, nframes_t frame, const string & action)
36         : ArdourDialog (_("edit tempo")),
37           bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 1.0),
38           bpm_spinner (bpm_adjustment),
39           bpm_frame (_("Beats per minute")),
40           ok_button (action),
41           cancel_button (_("Cancel")),
42           when_bar_label (_("Bar")),
43           when_beat_label (_("Beat")),
44           when_table (2, 2),
45           when_frame (_("Location"))
46 {
47         BBT_Time when;
48         Tempo tempo (map.tempo_at (frame));
49         map.bbt_time (frame, when);
50
51         init (when, tempo.beats_per_minute(), true);
52 }
53
54 TempoDialog::TempoDialog (TempoSection& section, const string & action)
55         : ArdourDialog ("tempo dialog"),
56           bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 1.0),
57           bpm_spinner (bpm_adjustment),
58           bpm_frame (_("Beats per minute")),
59           ok_button (action),
60           cancel_button (_("Cancel")),
61           when_bar_label (_("Bar")),
62           when_beat_label (_("Beat")),
63           when_table (2, 2),
64           when_frame (_("Location"))
65 {
66         init (section.start(), section.beats_per_minute(), section.movable());
67 }
68
69 void
70 TempoDialog::init (const BBT_Time& when, double bpm, bool movable)
71 {
72         bpm_spinner.set_numeric (true);
73         bpm_spinner.set_digits (1);
74         bpm_spinner.set_wrap (true);
75         bpm_spinner.set_value (bpm);
76
77         hspacer1.set_border_width (5);
78         hspacer1.pack_start (bpm_spinner, false, false);
79         vspacer1.set_border_width (5);
80         vspacer1.pack_start (hspacer1, false, false);
81
82         bpm_frame.add (vspacer1);
83
84         if (movable) {
85                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
86                 when_bar_entry.set_text (buf);
87                 snprintf (buf, sizeof (buf), "%" PRIu32, when.beats);
88                 when_beat_entry.set_text (buf);
89                 
90                 when_bar_entry.set_name ("MetricEntry");
91                 when_beat_entry.set_name ("MetricEntry");
92                 
93                 when_bar_label.set_name ("MetricLabel");
94                 when_beat_label.set_name ("MetricLabel");
95                 
96                 Gtkmm2ext::set_size_request_to_display_given_text (when_bar_entry, "999g", 5, 7);
97                 Gtkmm2ext::set_size_request_to_display_given_text (when_beat_entry, "999g", 5, 7);
98                 
99                 when_table.set_homogeneous (true);
100                 when_table.set_row_spacings (2);
101                 when_table.set_col_spacings (2);
102                 when_table.set_border_width (5);
103                 
104                 when_table.attach (when_bar_label, 0, 1, 0, 1, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
105                 when_table.attach (when_bar_entry, 0, 1, 1, 2, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
106                 
107                 when_table.attach (when_beat_label, 1, 2, 0, 1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
108                 when_table.attach (when_beat_entry, 1, 2, 1, 2, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
109                 
110                 when_frame.set_name ("MetricDialogFrame");
111                 when_frame.add (when_table);
112
113                 get_vbox()->pack_start (when_frame, false, false);
114         }
115
116         bpm_frame.set_name ("MetricDialogFrame");
117         bpm_spinner.set_name ("MetricEntry");
118
119         get_vbox()->pack_start (bpm_frame, false, false);
120         
121         add_button (Stock::CANCEL, RESPONSE_CANCEL);
122         add_button (Stock::APPLY, RESPONSE_ACCEPT);
123         set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
124         set_default_response (RESPONSE_ACCEPT);
125
126         get_vbox()->show_all();
127         bpm_spinner.show();
128
129         set_name ("MetricDialog");
130
131         bpm_spinner.signal_activate().connect (bind (mem_fun (*this, &TempoDialog::response), RESPONSE_ACCEPT));
132         bpm_spinner.signal_button_press_event().connect (mem_fun (*this, &TempoDialog::bpm_button_press), false);
133         bpm_spinner.signal_button_release_event().connect (mem_fun (*this, &TempoDialog::bpm_button_release), false);
134 }
135
136 bool
137 TempoDialog::bpm_button_press (GdkEventButton* ev)
138 {
139         return false;
140 }
141
142 bool
143 TempoDialog::bpm_button_release (GdkEventButton* ev)
144 {       
145         /* the value has been modified, accept should work now */
146
147         set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
148         return false;
149 }
150
151 double 
152 TempoDialog::get_bpm ()
153 {
154         return bpm_spinner.get_value ();
155 }       
156
157 bool
158 TempoDialog::get_bbt_time (BBT_Time& requested)
159 {
160         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
161                 return false;
162         }
163         
164         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
165                 return false;
166         }
167
168         return true;
169 }
170
171
172 MeterDialog::MeterDialog (TempoMap& map, nframes_t frame, const string & action)
173         : ArdourDialog ("meter dialog"),
174           note_frame (_("Meter denominator")),
175           bpb_frame (_("Beats per bar")),
176           ok_button (action),
177           cancel_button (_("Cancel")),
178           when_bar_label (_("Bar")),
179           when_beat_label (_("Beat")),
180           when_frame (_("Location"))
181 {
182         BBT_Time when;
183         frame = map.round_to_bar(frame,0); 
184         Meter meter (map.meter_at(frame));
185
186         map.bbt_time (frame, when);
187         init (when, meter.beats_per_bar(), meter.note_divisor(), true);
188 }
189
190 MeterDialog::MeterDialog (MeterSection& section, const string & action)
191         : ArdourDialog ("meter dialog"),
192           note_frame (_("Meter denominator")),
193           bpb_frame (_("Beats per bar")),
194           ok_button (action),
195           cancel_button (_("Cancel")),
196           when_bar_label (_("Bar")),
197           when_beat_label (_("Beat")),
198           when_frame (_("Location"))
199 {
200         init (section.start(), section.beats_per_bar(), section.note_divisor(), section.movable());
201 }
202
203 void
204 MeterDialog::init (const BBT_Time& when, double bpb, double note_type, bool movable)
205 {
206         snprintf (buf, sizeof (buf), "%.2f", bpb);
207         bpb_entry.set_text (buf);
208         bpb_entry.select_region (0, -1);
209         Gtkmm2ext::set_size_request_to_display_given_text (bpb_entry, "999999g", 5, 5);
210
211         strings.push_back (_("whole (1)"));
212         strings.push_back (_("second (2)"));
213         strings.push_back (_("third (3)"));
214         strings.push_back (_("quarter (4)"));
215         strings.push_back (_("eighth (8)"));
216         strings.push_back (_("sixteenth (16)"));
217         strings.push_back (_("thirty-second (32)"));
218         
219         set_popdown_strings (note_types, strings);
220
221         if (note_type==1.0f)
222                 note_types.set_active_text (_("whole (1)"));
223         else if (note_type==2.0f)
224                 note_types.set_active_text (_("second (2)"));
225         else if (note_type==3.0f)
226                 note_types.set_active_text (_("third (3)"));
227         else if (note_type==4.0f)
228                 note_types.set_active_text (_("quarter (4)"));
229         else if (note_type==8.0f)
230                 note_types.set_active_text (_("eighth (8)"));
231         else if (note_type==16.0f)
232                 note_types.set_active_text (_("sixteenth (16)"));
233         else if (note_type==32.0f)
234                 note_types.set_active_text (_("thirty-second (32)"));
235         else
236                 note_types.set_active_text (_("quarter (4)"));
237                 
238         /* the string here needs to be the longest one to display */
239         const guint32 FUDGE = 20; // Combo's are stupid - they steal space from the entry for the button
240         Gtkmm2ext::set_size_request_to_display_given_text (note_types, "thirty-second (32)", 7+FUDGE, 7);
241
242         hspacer1.set_border_width (5);
243         hspacer1.pack_start (note_types, false, false);
244         vspacer1.set_border_width (5);
245         vspacer1.pack_start (hspacer1, false, false);
246
247         hspacer2.set_border_width (5);
248         hspacer2.pack_start (bpb_entry, false, false);
249         vspacer2.set_border_width (5);
250         vspacer2.pack_start (hspacer2, false, false);
251
252         note_frame.add (vspacer1);
253         bpb_frame.add (vspacer2);
254
255         if (movable) {
256                 snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
257                 when_bar_entry.set_text (buf);
258                 snprintf (buf, sizeof (buf), "%" PRIu32, when.beats);
259                 when_beat_entry.set_text (buf);
260                 
261                 when_bar_entry.set_name ("MetricEntry");
262                 when_beat_entry.set_name ("MetricEntry");
263                 
264                 when_bar_label.set_name ("MetricLabel");
265                 when_beat_label.set_name ("MetricLabel");
266                 
267                 Gtkmm2ext::set_size_request_to_display_given_text (when_bar_entry, "999g", 5, 7);
268                 Gtkmm2ext::set_size_request_to_display_given_text (when_beat_entry, "999g", 5, 7);
269                 
270                 when_table.set_homogeneous (true);
271                 when_table.set_row_spacings (2);
272                 when_table.set_col_spacings (2);
273                 when_table.set_border_width (5);
274                 
275                 when_table.attach (when_bar_label, 0, 1, 0, 1, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
276                 when_table.attach (when_bar_entry, 0, 1, 1, 2, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
277                 
278                 when_table.attach (when_beat_label, 1, 2, 0, 1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
279                 when_table.attach (when_beat_entry, 1, 2, 1, 2, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
280                 
281                 when_frame.set_name ("MetricDialogFrame");
282                 when_frame.add (when_table);
283                 
284                 get_vbox()->pack_start (when_frame, false, false);
285         }
286         get_vbox()->pack_start (bpb_frame, false, false);
287         get_vbox()->pack_start (note_frame, false, false);
288         
289         bpb_frame.set_name ("MetricDialogFrame");
290         note_frame.set_name ("MetricDialogFrame");
291         bpb_entry.set_name ("MetricEntry");
292
293         add_button (Stock::CANCEL, RESPONSE_CANCEL);
294         add_button (Stock::APPLY, RESPONSE_ACCEPT);
295         set_response_sensitive (RESPONSE_ACCEPT, false);
296         set_default_response (RESPONSE_ACCEPT);
297
298         get_vbox()->show_all ();
299         bpb_entry.show ();
300
301         set_name ("MetricDialog");
302         bpb_entry.signal_activate().connect (bind (mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
303         bpb_entry.signal_key_press_event().connect (mem_fun (*this, &MeterDialog::bpb_key_press), false);
304         bpb_entry.signal_key_release_event().connect (mem_fun (*this, &MeterDialog::bpb_key_release));
305         note_types.signal_changed().connect (mem_fun (*this, &MeterDialog::note_types_change));
306 }
307
308 bool
309 MeterDialog::bpb_key_press (GdkEventKey* ev)
310 {
311
312 switch (ev->keyval) { 
313
314  case GDK_0:
315  case GDK_1:
316  case GDK_2:
317  case GDK_3:
318  case GDK_4:
319  case GDK_5:
320  case GDK_6:
321  case GDK_7:
322  case GDK_8:
323  case GDK_9:
324  case GDK_KP_0:
325  case GDK_KP_1:
326  case GDK_KP_2:
327  case GDK_KP_3:
328  case GDK_KP_4:
329  case GDK_KP_5:
330  case GDK_KP_6:
331  case GDK_KP_7:
332  case GDK_KP_8:
333  case GDK_KP_9:
334  case GDK_period:
335  case GDK_comma:
336  case  GDK_KP_Delete:
337  case  GDK_KP_Enter:
338  case  GDK_Delete:
339  case  GDK_BackSpace:
340  case  GDK_Escape:
341  case  GDK_Return:
342  case  GDK_Home:
343  case  GDK_End:
344  case  GDK_Left:
345  case  GDK_Right:
346  case  GDK_Num_Lock:
347  case  GDK_Tab:
348     return FALSE;
349  default:
350       break;
351  }
352
353    return TRUE;
354 }
355
356 bool
357 MeterDialog::bpb_key_release (GdkEventKey* ev)
358 {
359         if (bpb_entry.get_text() != "") {
360                 set_response_sensitive (RESPONSE_ACCEPT, true);
361         } else {
362                 set_response_sensitive (RESPONSE_ACCEPT, false);
363         }
364         return false;
365 }
366
367 void
368 MeterDialog::note_types_change ()
369 {
370         set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
371 }
372
373 double
374 MeterDialog::get_bpb ()
375 {
376         double bpb = 0;
377         
378         if (sscanf (bpb_entry.get_text().c_str(), "%lf", &bpb) != 1) {
379                 return 0;
380         }
381
382         return bpb;
383 }
384         
385 double
386 MeterDialog::get_note_type ()
387 {
388         double note_type = 0;
389         vector<string>::iterator i;
390         string text = note_types.get_active_text();
391         
392         for (i = strings.begin(); i != strings.end(); ++i) {
393                 if (text == *i) {
394                         if (sscanf (text.c_str(), "%*[^0-9]%lf", &note_type) != 1) {
395                                 error << string_compose(_("garbaged note type entry (%1)"), text) << endmsg;
396                                 return 0;
397                         } else {
398                                 break;
399                         }
400                 }
401         } 
402         
403         if (i == strings.end()) {
404                 if (sscanf (text.c_str(), "%lf", &note_type) != 1) {
405                         error << string_compose(_("incomprehensible note type entry (%1)"), text) << endmsg;
406                         return 0;
407                 }
408         }
409
410         return note_type;
411 }
412
413 bool
414 MeterDialog::get_bbt_time (BBT_Time& requested)
415 {
416         requested.ticks = 0;
417
418         if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
419                 return false;
420         }
421         
422         if (sscanf (when_beat_entry.get_text().c_str(), "%" PRIu32, &requested.beats) != 1) {
423                 return false;
424         }
425
426         return true;
427 }