80d38a3cac6c97aa4dafe0f3de742a1cca4c9398
[ardour.git] / gtk2_ardour / region_view.cc
1 /*
2     Copyright (C) 2001-2006 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     $Id$
19 */
20
21 #include <cmath>
22 #include <cassert>
23 #include <algorithm>
24
25 #include <gtkmm.h>
26
27 #include <gtkmm2ext/gtk_ui.h>
28
29 #include <ardour/playlist.h>
30 #include <ardour/audioregion.h>
31 #include <ardour/audiosource.h>
32 #include <ardour/audio_diskstream.h>
33
34 #include "streamview.h"
35 #include "region_view.h"
36 #include "route_time_axis.h"
37 #include "simplerect.h"
38 #include "simpleline.h"
39 #include "waveview.h"
40 #include "public_editor.h"
41 #include "region_editor.h"
42 #include "ghostregion.h"
43 #include "route_time_axis.h"
44 #include "utils.h"
45 #include "rgb_macros.h"
46 #include "gui_thread.h"
47
48 #include "i18n.h"
49
50 using namespace sigc;
51 using namespace ARDOUR;
52 using namespace PBD;
53 using namespace Editing;
54 using namespace ArdourCanvas;
55
56 static const int32_t sync_mark_width = 9;
57
58 sigc::signal<void,RegionView*> RegionView::RegionViewGoingAway;
59
60 RegionView::RegionView (ArdourCanvas::Group* parent, 
61                         TimeAxisView&        tv,
62                         ARDOUR::Region&      r,
63                         double               spu,
64                         Gdk::Color&          basic_color)
65         : TimeAxisViewItem (r.name(), *parent, tv, spu, basic_color, r.position(), r.length(),
66                         TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
67                                                       TimeAxisViewItem::ShowNameHighlight|
68                                                       TimeAxisViewItem::ShowFrame))
69         , _region (r)
70 {
71 }
72
73 RegionView::RegionView (ArdourCanvas::Group*         parent, 
74                         TimeAxisView&                tv,
75                         ARDOUR::Region&              r,
76                         double                       spu,
77                         Gdk::Color&                  basic_color,
78                         TimeAxisViewItem::Visibility visibility)
79         : TimeAxisViewItem (r.name(), *parent, tv, spu, basic_color, r.position(), r.length(), visibility)
80         , _region (r)
81 {
82 }
83
84 void
85 RegionView::init (Gdk::Color& basic_color, bool wfd)
86 {
87         editor        = 0;
88         valid         = true;
89         in_destructor = false;
90         _height       = 0;
91         wait_for_data = wfd;
92
93         compute_colors (basic_color);
94
95         name_highlight->set_data ("regionview", this);
96         name_text->set_data ("regionview", this);
97
98         /* an equilateral triangle */
99     ArdourCanvas::Points shape;
100         shape.push_back (Gnome::Art::Point (-((sync_mark_width-1)/2), 1));
101         shape.push_back (Gnome::Art::Point ((sync_mark_width - 1)/2, 1));
102         shape.push_back (Gnome::Art::Point (0, sync_mark_width - 1));
103         shape.push_back (Gnome::Art::Point (-((sync_mark_width-1)/2), 1));
104
105         sync_mark =  new ArdourCanvas::Polygon (*group);
106         sync_mark->property_points() = shape;
107         sync_mark->property_fill_color_rgba() = fill_color;
108         sync_mark->hide();
109
110         reset_width_dependent_items ((double) _region.length() / samples_per_unit);
111
112         set_height (trackview.height);
113
114         region_muted ();
115         region_sync_changed ();
116         region_resized (BoundsChanged);
117         region_locked ();
118
119         _region.StateChanged.connect (mem_fun(*this, &RegionView::region_changed));
120
121         group->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_event), group, this));
122         name_highlight->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_name_highlight_event), name_highlight, this));
123
124         set_colors ();
125
126         ColorChanged.connect (mem_fun (*this, &RegionView::color_handler));
127
128         /* XXX sync mark drag? */
129 }
130
131 RegionView::~RegionView ()
132 {
133         in_destructor = true;
134
135         RegionViewGoingAway (this); /* EMIT_SIGNAL */
136
137         for (vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
138                 delete *g;
139         }
140
141         if (editor) {
142                 delete editor;
143         }
144 }
145
146 gint
147 RegionView::_lock_toggle (ArdourCanvas::Item* item, GdkEvent* ev, void* arg)
148 {
149         switch (ev->type) {
150         case GDK_BUTTON_RELEASE:
151                 static_cast<RegionView*>(arg)->lock_toggle ();
152                 return TRUE;
153                 break;
154         default:
155                 break;
156         } 
157         return FALSE;
158 }
159
160 void
161 RegionView::lock_toggle ()
162 {
163         _region.set_locked (!_region.locked());
164 }
165
166 void
167 RegionView::region_changed (Change what_changed)
168 {
169         ENSURE_GUI_THREAD (bind (mem_fun(*this, &RegionView::region_changed), what_changed));
170
171         if (what_changed & BoundsChanged) {
172                 region_resized (what_changed);
173                 region_sync_changed ();
174         }
175         if (what_changed & Region::MuteChanged) {
176                 region_muted ();
177         }
178         if (what_changed & Region::OpacityChanged) {
179                 region_opacity ();
180         }
181         if (what_changed & ARDOUR::NameChanged) {
182                 region_renamed ();
183         }
184         if (what_changed & Region::SyncOffsetChanged) {
185                 region_sync_changed ();
186         }
187         if (what_changed & Region::LayerChanged) {
188                 region_layered ();
189         }
190         if (what_changed & Region::LockChanged) {
191                 region_locked ();
192         }
193 }
194
195 void
196 RegionView::region_locked ()
197 {
198         /* name will show locked status */
199         region_renamed ();
200 }
201
202 void
203 RegionView::region_resized (Change what_changed)
204 {
205         double unit_length;
206
207         if (what_changed & ARDOUR::PositionChanged) {
208                 set_position (_region.position(), 0);
209         }
210
211         if (what_changed & Change (StartChanged|LengthChanged)) {
212
213                 set_duration (_region.length(), 0);
214
215                 unit_length = _region.length() / samples_per_unit;
216                 
217                 reset_width_dependent_items (unit_length);
218                 
219                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
220
221                         (*i)->set_duration (unit_length);
222
223                 }
224         }
225 }
226
227 void
228 RegionView::reset_width_dependent_items (double pixel_width)
229 {
230         TimeAxisViewItem::reset_width_dependent_items (pixel_width);
231         _pixel_width = pixel_width;
232 }
233
234 void
235 RegionView::region_layered ()
236 {
237         RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*>(&get_time_axis_view());
238         assert(rtv);
239         rtv->view()->region_layered (this);
240 }
241         
242 void
243 RegionView::region_muted ()
244 {
245         set_frame_color ();
246         region_renamed ();
247 }
248
249 void
250 RegionView::region_opacity ()
251 {
252         set_frame_color ();
253 }
254
255 void
256 RegionView::raise ()
257 {
258         _region.raise ();
259 }
260
261 void
262 RegionView::raise_to_top ()
263 {
264         _region.raise_to_top ();
265 }
266
267 void
268 RegionView::lower ()
269 {
270         _region.lower ();
271 }
272
273 void
274 RegionView::lower_to_bottom ()
275 {
276         _region.lower_to_bottom ();
277 }
278
279 bool
280 RegionView::set_position (jack_nframes_t pos, void* src, double* ignored)
281 {
282         double delta;
283         bool ret;
284
285         if (!(ret = TimeAxisViewItem::set_position (pos, this, &delta))) {
286                 return false;
287         }
288
289         if (ignored) {
290                 *ignored = delta;
291         }
292
293         if (delta) {
294                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
295                         (*i)->group->move (delta, 0.0);
296                 }
297         }
298
299         return ret;
300 }
301
302 void
303 RegionView::set_samples_per_unit (gdouble spu)
304 {
305         TimeAxisViewItem::set_samples_per_unit (spu);
306
307         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
308                 (*i)->set_samples_per_unit (spu);
309                 (*i)->set_duration (_region.length() / samples_per_unit);
310         }
311
312         region_sync_changed ();
313 }
314
315 bool
316 RegionView::set_duration (jack_nframes_t frames, void *src)
317 {
318         if (!TimeAxisViewItem::set_duration (frames, src)) {
319                 return false;
320         }
321         
322         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
323                 (*i)->set_duration (_region.length() / samples_per_unit);
324         }
325
326         return true;
327 }
328
329 void
330 RegionView::compute_colors (Gdk::Color& basic_color)
331 {
332         TimeAxisViewItem::compute_colors (basic_color);
333 }
334
335 void
336 RegionView::set_colors ()
337 {
338         TimeAxisViewItem::set_colors ();
339         
340         if (sync_mark) {
341                 sync_mark->property_fill_color_rgba() = fill_color;
342         }
343 }
344
345 void
346 RegionView::set_frame_color ()
347 {
348         if (_region.opaque()) {
349                 fill_opacity = 180;
350         } else {
351                 fill_opacity = 100;
352         }
353
354         TimeAxisViewItem::set_frame_color ();
355 }
356
357 void
358 RegionView::hide_region_editor()
359 {
360         if (editor) {
361                 editor->hide_all ();
362         }
363 }
364
365 void
366 RegionView::region_renamed ()
367 {
368         string str;
369
370         if (_region.locked()) {
371                 str += '>';
372                 str += _region.name();
373                 str += '<';
374         } else {
375                 str = _region.name();
376         }
377
378         if (_region.speed_mismatch (trackview.session().frame_rate())) {
379                 str = string ("*") + str;
380         }
381
382         if (_region.muted()) {
383                 str = string ("!") + str;
384         }
385
386         set_item_name (str, this);
387         set_name_text (str);
388 }
389
390 void
391 RegionView::region_sync_changed ()
392 {
393         if (sync_mark == 0) {
394                 return;
395         }
396
397         int sync_dir;
398         jack_nframes_t sync_offset;
399
400         sync_offset = _region.sync_offset (sync_dir);
401
402         /* this has to handle both a genuine change of position, a change of samples_per_unit,
403            and a change in the bounds of the _region.
404          */
405
406         if (sync_offset == 0) {
407
408                 /* no sync mark - its the start of the region */
409
410                 sync_mark->hide();
411
412         } else {
413
414                 if ((sync_dir < 0) || ((sync_dir > 0) && (sync_offset > _region.length()))) { 
415
416                         /* no sync mark - its out of the bounds of the region */
417
418                         sync_mark->hide();
419
420                 } else {
421
422                         /* lets do it */
423
424                         Points points;
425                         
426                         //points = sync_mark->property_points().get_value();
427                         
428                         double offset = sync_offset / samples_per_unit;
429                         points.push_back (Gnome::Art::Point (offset - ((sync_mark_width-1)/2), 1));
430                         points.push_back (Gnome::Art::Point (offset + ((sync_mark_width-1)/2), 1));
431                         points.push_back (Gnome::Art::Point (offset, sync_mark_width - 1));
432                         points.push_back (Gnome::Art::Point (offset - ((sync_mark_width-1)/2), 1));     
433                         sync_mark->property_points().set_value (points);
434                         sync_mark->show();
435
436                 }
437         }
438 }
439
440 void
441 RegionView::move (double x_delta, double y_delta)
442 {
443         if (_region.locked() || (x_delta == 0 && y_delta == 0)) {
444                 return;
445         }
446
447         get_canvas_group()->move (x_delta, y_delta);
448
449         /* note: ghosts never leave their tracks so y_delta for them is always zero */
450
451         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
452                 (*i)->group->move (x_delta, 0.0);
453         }
454 }
455
456 void
457 RegionView::remove_ghost (GhostRegion* ghost)
458 {
459         if (in_destructor) {
460                 return;
461         }
462
463         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
464                 if (*i == ghost) {
465                         ghosts.erase (i);
466                         break;
467                 }
468         }
469 }
470
471 uint32_t
472 RegionView::get_fill_color ()
473 {
474         return fill_color;
475 }
476