new design for region dragging; make add route dialog float over the correct window...
[ardour.git] / gtk2_ardour / audio_region_editor.cc
1 /*
2     Copyright (C) 2001 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 <pbd/memento_command.h>
21
22 #include <ardour/audioregion.h>
23 #include <ardour/playlist.h>
24 #include <ardour/utils.h>
25 #include <gtkmm2ext/utils.h>
26 #include <gtkmm2ext/stop_signal.h>
27 #include <cmath>
28
29 #include "audio_region_editor.h"
30 #include "audio_region_view.h"
31 #include "ardour_ui.h"
32 #include "utils.h"
33 #include "gui_thread.h"
34
35 #include "i18n.h"
36
37 using namespace ARDOUR;
38 using namespace PBD;
39 using namespace sigc;
40 using namespace std;
41
42 AudioRegionEditor::AudioRegionEditor (Session& s, boost::shared_ptr<AudioRegion> r, AudioRegionView& rv)
43         : RegionEditor (s),
44           _region (r),
45           _region_view (rv),
46           name_label (_("NAME:")),
47           audition_button (_("play")),
48           time_table (3, 2),
49           start_clock (X_("regionstart"), true, X_("AudioRegionEditorClock"), true),
50           end_clock (X_("regionend"), true, X_("AudioRegionEditorClock"), true),
51           length_clock (X_("regionlength"), true, X_("AudioRegionEditorClock"), true, true),
52           sync_offset_clock (X_("regionsyncoffset"), true, X_("AudioRegionEditorClock"), true, true)
53
54 {
55         start_clock.set_session (&_session);
56         end_clock.set_session (&_session);
57         length_clock.set_session (&_session);
58
59         name_entry.set_name ("AudioRegionEditorEntry");
60         name_label.set_name ("AudioRegionEditorLabel");
61
62         name_hbox.set_spacing (5);
63         name_hbox.pack_start (name_label, false, false);
64         name_hbox.pack_start (name_entry, false, false);
65
66         ARDOUR_UI::instance()->tooltips().set_tip (audition_button, _("audition this region"));
67
68         audition_button.unset_flags (Gtk::CAN_FOCUS);
69         
70         audition_button.set_events (audition_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
71
72         top_row_button_hbox.set_border_width (5);
73         top_row_button_hbox.set_spacing (5);
74         top_row_button_hbox.set_homogeneous (false);
75         top_row_button_hbox.pack_end (audition_button, false, false);
76         
77         top_row_hbox.pack_start (name_hbox, true, true);
78         top_row_hbox.pack_end (top_row_button_hbox, true, true);
79
80         start_label.set_name ("AudioRegionEditorLabel");
81         start_label.set_text (_("START:"));
82         end_label.set_name ("AudioRegionEditorLabel");
83         end_label.set_text (_("END:"));
84         length_label.set_name ("AudioRegionEditorLabel");
85         length_label.set_text (_("LENGTH:"));
86         
87         time_table.set_col_spacings (2);
88         time_table.set_row_spacings (5);
89         time_table.set_border_width (5);
90
91         start_alignment.set (1.0, 0.5);
92         end_alignment.set (1.0, 0.5);
93         length_alignment.set (1.0, 0.5);
94
95         start_alignment.add (start_label);
96         end_alignment.add (end_label);
97         length_alignment.add (length_label);
98
99         time_table.attach (start_alignment, 0, 1, 0, 1, Gtk::FILL, Gtk::FILL);
100         time_table.attach (start_clock, 1, 2, 0, 1, Gtk::FILL, Gtk::FILL);
101
102         time_table.attach (end_alignment, 0, 1, 1, 2, Gtk::FILL, Gtk::FILL);
103         time_table.attach (end_clock, 1, 2, 1, 2, Gtk::FILL, Gtk::FILL);
104
105         time_table.attach (length_alignment, 0, 1, 2, 3, Gtk::FILL, Gtk::FILL);
106         time_table.attach (length_clock, 1, 2, 2, 3, Gtk::FILL, Gtk::FILL);
107
108         lower_hbox.pack_start (time_table, true, true);
109         lower_hbox.pack_start (sep1, false, false);
110         lower_hbox.pack_start (sep2, false, false);
111
112         get_vbox()->pack_start (top_row_hbox, true, true);
113         get_vbox()->pack_start (sep3, false, false);
114         get_vbox()->pack_start (lower_hbox, true, true);
115
116         set_name ("AudioRegionEditorWindow");
117         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
118
119         signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (this)));
120
121         string title = _("ardour: region ");
122         title += _region->name();
123         set_title (title);
124
125         show_all();
126
127         name_changed ();
128         bounds_changed (Change (StartChanged|LengthChanged|PositionChanged));
129
130         _region->StateChanged.connect (mem_fun(*this, &AudioRegionEditor::region_changed));
131         
132         spin_arrow_grab = false;
133         
134         connect_editor_events ();
135 }
136
137 AudioRegionEditor::~AudioRegionEditor ()
138 {
139 }
140
141 void
142 AudioRegionEditor::region_changed (Change what_changed)
143 {
144         if (what_changed & NameChanged) {
145                 name_changed ();
146         }
147         if (what_changed & BoundsChanged) {
148                 bounds_changed (what_changed);
149         }
150 }
151
152 gint 
153 AudioRegionEditor::bpressed (GdkEventButton* ev, Gtk::SpinButton* but, void (AudioRegionEditor::*pmf)())
154 {
155         switch (ev->button) {
156         case 1:
157         case 2:
158         case 3:
159                 if (ev->type == GDK_BUTTON_PRESS) { /* no double clicks here */
160                         if (!spin_arrow_grab) {
161                                 // GTK2FIX probably nuke the region editor
162                                 // if ((ev->window == but->gobj()->panel)) {
163                                 // spin_arrow_grab = true;
164                                 // (this->*pmf)();
165                                 // } 
166                         } 
167                 } 
168                 break;
169         default:
170                 break;
171         }
172         return FALSE;
173 }
174
175 gint 
176 AudioRegionEditor::breleased (GdkEventButton* ev, Gtk::SpinButton* but, void (AudioRegionEditor::*pmf)())
177 {
178         if (spin_arrow_grab) {
179                 (this->*pmf)();
180                 spin_arrow_grab = false;
181         }
182         return FALSE;
183 }
184
185 void
186 AudioRegionEditor::connect_editor_events ()
187 {
188         name_entry.signal_changed().connect (mem_fun(*this, &AudioRegionEditor::name_entry_changed));
189
190         start_clock.ValueChanged.connect (mem_fun(*this, &AudioRegionEditor::start_clock_changed));
191         end_clock.ValueChanged.connect (mem_fun(*this, &AudioRegionEditor::end_clock_changed));
192         length_clock.ValueChanged.connect (mem_fun(*this, &AudioRegionEditor::length_clock_changed));
193
194         audition_button.signal_toggled().connect (mem_fun(*this, &AudioRegionEditor::audition_button_toggled));
195         _session.AuditionActive.connect (mem_fun(*this, &AudioRegionEditor::audition_state_changed));
196 }
197
198 void
199 AudioRegionEditor::start_clock_changed ()
200 {
201         _session.begin_reversible_command (_("change region start position"));
202
203         boost::shared_ptr<Playlist> pl = _region->playlist();
204
205         if (pl) {
206                 XMLNode &before = pl->get_state();
207                 _region->set_position (start_clock.current_time(), this);
208                 XMLNode &after = pl->get_state();
209                 _session.add_command(new MementoCommand<Playlist>(*pl, &before, &after));
210         }
211
212         _session.commit_reversible_command ();
213 }
214
215 void
216 AudioRegionEditor::end_clock_changed ()
217 {
218         _session.begin_reversible_command (_("change region end position"));
219
220         boost::shared_ptr<Playlist> pl = _region->playlist();
221         
222         if (pl) {
223                 XMLNode &before = pl->get_state();
224                 _region->trim_end (end_clock.current_time(), this);
225                 XMLNode &after = pl->get_state();
226                 _session.add_command(new MementoCommand<Playlist>(*pl, &before, &after));
227         }
228
229         _session.commit_reversible_command ();
230
231         end_clock.set (_region->position() + _region->length(), true);
232 }
233
234 void
235 AudioRegionEditor::length_clock_changed ()
236 {
237         nframes_t frames = length_clock.current_time();
238         
239         _session.begin_reversible_command (_("change region length"));
240         
241         boost::shared_ptr<Playlist> pl = _region->playlist();
242
243         if (pl) {
244                 XMLNode &before = pl->get_state();
245                 _region->trim_end (_region->position() + frames, this);
246                 XMLNode &after = pl->get_state();
247                 _session.add_command(new MementoCommand<Playlist>(*pl, &before, &after));
248         }
249
250         _session.commit_reversible_command ();
251
252         length_clock.set (_region->length());
253 }
254
255 void
256 AudioRegionEditor::audition_button_toggled ()
257 {
258         if (audition_button.get_active()) {
259                 _session.audition_region (_region);
260         } else {
261                 _session.cancel_audition ();
262         }
263 }
264
265 void
266 AudioRegionEditor::name_changed ()
267 {
268         if (name_entry.get_text() != _region->name()) {
269                 name_entry.set_text (_region->name());
270         }
271 }
272
273 void
274 AudioRegionEditor::bounds_changed (Change what_changed)
275 {
276         if (what_changed & Change ((PositionChanged|LengthChanged))) {
277                 start_clock.set (_region->position(), true);
278                 end_clock.set (_region->position() + _region->length(), true);
279                 length_clock.set (_region->length(), true);
280         }
281 }
282
283 void
284 AudioRegionEditor::activation ()
285 {
286         
287 }       
288
289 void
290 AudioRegionEditor::name_entry_changed ()
291 {
292         if (name_entry.get_text() != _region->name()) {
293                 _region->set_name (name_entry.get_text());
294         }
295 }
296
297 void
298 AudioRegionEditor::audition_state_changed (bool yn)
299 {
300         ENSURE_GUI_THREAD (bind (mem_fun(*this, &AudioRegionEditor::audition_state_changed), yn));
301
302         if (!yn) {
303                 audition_button.set_active (false);
304         }
305 }
306