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