2 Copyright (C) 2002 Paul Davis
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.
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.
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.
20 #include <cstdio> // for sprintf, grrr
26 #include <libgnomecanvasmm.h>
28 #include <pbd/error.h>
29 #include <pbd/memento_command.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/gtk_ui.h>
34 #include <ardour/session.h>
35 #include <ardour/tempo.h>
36 #include <gtkmm2ext/doi.h>
37 #include <gtkmm2ext/utils.h>
41 #include "simpleline.h"
42 #include "tempo_dialog.h"
43 #include "rgb_macros.h"
44 #include "gui_thread.h"
45 #include "ardour_ui.h"
46 #include "time_axis_view.h"
47 #include "tempo_lines.h"
53 using namespace ARDOUR
;
56 using namespace Gtkmm2ext
;
57 using namespace Editing
;
60 Editor::remove_metric_marks ()
62 /* don't delete these while handling events, just punt till the GUI is idle */
64 for (Marks::iterator x
= metric_marks
.begin(); x
!= metric_marks
.end(); ++x
) {
65 delete_when_idle (*x
);
67 metric_marks
.clear ();
71 Editor::draw_metric_marks (const Metrics
& metrics
)
74 const MeterSection
*ms
;
75 const TempoSection
*ts
;
78 remove_metric_marks ();
80 for (Metrics::const_iterator i
= metrics
.begin(); i
!= metrics
.end(); ++i
) {
82 if ((ms
= dynamic_cast<const MeterSection
*>(*i
)) != 0) {
83 snprintf (buf
, sizeof(buf
), "%g/%g", ms
->beats_per_bar(), ms
->note_divisor ());
84 metric_marks
.push_back (new MeterMarker (*this, *meter_group
, ARDOUR_UI::config()->canvasvar_MeterMarker
.get(), buf
,
85 *(const_cast<MeterSection
*>(ms
))));
86 } else if ((ts
= dynamic_cast<const TempoSection
*>(*i
)) != 0) {
87 snprintf (buf
, sizeof (buf
), "%.2f", ts
->beats_per_minute());
88 metric_marks
.push_back (new TempoMarker (*this, *tempo_group
, ARDOUR_UI::config()->canvasvar_TempoMarker
.get(), buf
,
89 *(const_cast<TempoSection
*>(ts
))));
97 Editor::tempo_map_changed (Change ignored
)
103 ENSURE_GUI_THREAD(bind (mem_fun (*this, &Editor::tempo_map_changed
), ignored
));
106 tempo_lines
->tempo_map_changed();
109 compute_current_bbt_points(leftmost_frame
, leftmost_frame
+ current_page_frames());
110 session
->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks
); // redraw metric markers
112 update_tempo_based_rulers ();
116 * This code was originally in tempo_map_changed, but this is called every time the canvas scrolls horizontally.
117 * That's why this is moved in here. The new tempo_map_changed is called when the ARDOUR::TempoMap actually changed.
120 Editor::redisplay_tempo (bool immediate_redraw
)
126 compute_current_bbt_points (leftmost_frame
, leftmost_frame
+ current_page_frames());
127 if (immediate_redraw
) {
133 Glib::signal_idle().connect (mem_fun (*this, &Editor::redraw_measures
));
136 update_tempo_based_rulers (); // redraw rulers and measures
140 Editor::compute_current_bbt_points (nframes_t leftmost
, nframes_t rightmost
)
146 BBT_Time previous_beat
, next_beat
; // the beats previous to the leftmost frame and after the rightmost frame
148 session
->bbt_time(leftmost
, previous_beat
);
149 session
->bbt_time(rightmost
, next_beat
);
151 if (previous_beat
.beats
> 1) {
152 previous_beat
.beats
-= 1;
153 } else if (previous_beat
.bars
> 1) {
154 previous_beat
.bars
--;
155 previous_beat
.beats
+= 1;
157 previous_beat
.ticks
= 0;
159 if (session
->tempo_map().meter_at(rightmost
).beats_per_bar () > next_beat
.beats
+ 1) {
160 next_beat
.beats
+= 1;
167 if (current_bbt_points
) {
168 delete current_bbt_points
;
169 current_bbt_points
= 0;
172 current_bbt_points
= session
->tempo_map().get_points (session
->tempo_map().frame_time (previous_beat
), session
->tempo_map().frame_time (next_beat
) + 1);
176 Editor::hide_measures ()
184 Editor::redraw_measures ()
191 Editor::draw_measures ()
193 if (session
== 0 || _show_measures
== false ||
194 !current_bbt_points
|| current_bbt_points
->empty()) {
198 if (tempo_lines
== 0) {
199 tempo_lines
= new TempoLines(*track_canvas
, time_line_group
, physical_screen_height
);
202 tempo_lines
->draw(*current_bbt_points
, frames_per_unit
);
206 Editor::mouse_add_new_tempo_event (nframes64_t frame
)
212 TempoMap
& map(session
->tempo_map());
213 TempoDialog
tempo_dialog (map
, frame
, _("add"));
215 tempo_dialog
.set_position (Gtk::WIN_POS_MOUSE
);
216 //this causes compiz to display no border.
217 //tempo_dialog.signal_realize().connect (bind (sigc::ptr_fun (set_decoration), &tempo_dialog, Gdk::WMDecoration (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
219 ensure_float (tempo_dialog
);
221 switch (tempo_dialog
.run()) {
222 case RESPONSE_ACCEPT
:
231 bpm
= tempo_dialog
.get_bpm ();
232 double nt
= tempo_dialog
.get_note_type();
233 bpm
= max (0.01, bpm
);
235 tempo_dialog
.get_bbt_time (requested
);
237 begin_reversible_command (_("add tempo mark"));
238 XMLNode
&before
= map
.get_state();
239 map
.add_tempo (Tempo (bpm
,nt
), requested
);
240 XMLNode
&after
= map
.get_state();
241 session
->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &after
));
242 commit_reversible_command ();
248 Editor::mouse_add_new_meter_event (nframes64_t frame
)
255 TempoMap
& map(session
->tempo_map());
256 MeterDialog
meter_dialog (map
, frame
, _("add"));
258 meter_dialog
.set_position (Gtk::WIN_POS_MOUSE
);
260 //this causes compiz to display no border..
261 //meter_dialog.signal_realize().connect (bind (sigc::ptr_fun (set_decoration), &meter_dialog, Gdk::WMDecoration (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
263 ensure_float (meter_dialog
);
265 switch (meter_dialog
.run ()) {
266 case RESPONSE_ACCEPT
:
272 double bpb
= meter_dialog
.get_bpb ();
273 bpb
= max (1.0, bpb
); // XXX is this a reasonable limit?
275 double note_type
= meter_dialog
.get_note_type ();
278 meter_dialog
.get_bbt_time (requested
);
280 begin_reversible_command (_("add meter mark"));
281 XMLNode
&before
= map
.get_state();
282 map
.add_meter (Meter (bpb
, note_type
), requested
);
283 session
->add_command(new MementoCommand
<TempoMap
>(map
, &before
, &map
.get_state()));
284 commit_reversible_command ();
290 Editor::remove_tempo_marker (ArdourCanvas::Item
* item
)
293 TempoMarker
* tempo_marker
;
295 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
296 fatal
<< _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg
;
300 if ((tempo_marker
= dynamic_cast<TempoMarker
*> (marker
)) == 0) {
301 fatal
<< _("programming error: marker for tempo is not a tempo marker!") << endmsg
;
305 if (tempo_marker
->tempo().movable()) {
306 Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::real_remove_tempo_marker
), &tempo_marker
->tempo()));
311 Editor::edit_meter_section (MeterSection
* section
)
313 MeterDialog
meter_dialog (*section
, _("done"));
315 meter_dialog
.set_position (Gtk::WIN_POS_MOUSE
);
317 ensure_float (meter_dialog
);
319 switch (meter_dialog
.run()) {
320 case RESPONSE_ACCEPT
:
326 double bpb
= meter_dialog
.get_bpb ();
327 bpb
= max (1.0, bpb
); // XXX is this a reasonable limit?
329 double note_type
= meter_dialog
.get_note_type ();
331 begin_reversible_command (_("replace tempo mark"));
332 XMLNode
&before
= session
->tempo_map().get_state();
333 session
->tempo_map().replace_meter (*section
, Meter (bpb
, note_type
));
334 XMLNode
&after
= session
->tempo_map().get_state();
335 session
->add_command(new MementoCommand
<TempoMap
>(session
->tempo_map(), &before
, &after
));
336 commit_reversible_command ();
340 Editor::edit_tempo_section (TempoSection
* section
)
342 TempoDialog
tempo_dialog (*section
, _("done"));
344 tempo_dialog
.set_position (Gtk::WIN_POS_MOUSE
);
346 ensure_float (tempo_dialog
);
348 switch (tempo_dialog
.run ()) {
349 case RESPONSE_ACCEPT
:
355 double bpm
= tempo_dialog
.get_bpm ();
356 double nt
= tempo_dialog
.get_note_type ();
358 tempo_dialog
.get_bbt_time(when
);
359 bpm
= max (0.01, bpm
);
361 cerr
<< "Editing tempo section to be at " << when
<< endl
;
362 session
->tempo_map().dump (cerr
);
363 begin_reversible_command (_("replace tempo mark"));
364 XMLNode
&before
= session
->tempo_map().get_state();
365 session
->tempo_map().replace_tempo (*section
, Tempo (bpm
,nt
));
366 session
->tempo_map().dump (cerr
);
367 session
->tempo_map().move_tempo (*section
, when
);
368 session
->tempo_map().dump (cerr
);
369 XMLNode
&after
= session
->tempo_map().get_state();
370 session
->add_command (new MementoCommand
<TempoMap
>(session
->tempo_map(), &before
, &after
));
371 commit_reversible_command ();
375 Editor::edit_tempo_marker (ArdourCanvas::Item
*item
)
378 TempoMarker
* tempo_marker
;
380 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
381 fatal
<< _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg
;
385 if ((tempo_marker
= dynamic_cast<TempoMarker
*> (marker
)) == 0) {
386 fatal
<< _("programming error: marker for tempo is not a tempo marker!") << endmsg
;
390 edit_tempo_section (&tempo_marker
->tempo());
394 Editor::edit_meter_marker (ArdourCanvas::Item
*item
)
397 MeterMarker
* meter_marker
;
399 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
400 fatal
<< _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg
;
404 if ((meter_marker
= dynamic_cast<MeterMarker
*> (marker
)) == 0) {
405 fatal
<< _("programming error: marker for meter is not a meter marker!") << endmsg
;
409 edit_meter_section (&meter_marker
->meter());
413 Editor::real_remove_tempo_marker (TempoSection
*section
)
415 begin_reversible_command (_("remove tempo mark"));
416 XMLNode
&before
= session
->tempo_map().get_state();
417 session
->tempo_map().remove_tempo (*section
);
418 XMLNode
&after
= session
->tempo_map().get_state();
419 session
->add_command(new MementoCommand
<TempoMap
>(session
->tempo_map(), &before
, &after
));
420 commit_reversible_command ();
426 Editor::remove_meter_marker (ArdourCanvas::Item
* item
)
429 MeterMarker
* meter_marker
;
431 if ((marker
= reinterpret_cast<Marker
*> (item
->get_data ("marker"))) == 0) {
432 fatal
<< _("programming error: meter marker canvas item has no marker object pointer!") << endmsg
;
436 if ((meter_marker
= dynamic_cast<MeterMarker
*> (marker
)) == 0) {
437 fatal
<< _("programming error: marker for meter is not a meter marker!") << endmsg
;
441 if (meter_marker
->meter().movable()) {
442 Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::real_remove_meter_marker
), &meter_marker
->meter()));
447 Editor::real_remove_meter_marker (MeterSection
*section
)
449 begin_reversible_command (_("remove tempo mark"));
450 XMLNode
&before
= session
->tempo_map().get_state();
451 session
->tempo_map().remove_meter (*section
);
452 XMLNode
&after
= session
->tempo_map().get_state();
453 session
->add_command(new MementoCommand
<TempoMap
>(session
->tempo_map(), &before
, &after
));
454 commit_reversible_command ();