aa7005e9c032883e23157ead544ccf3113944de4
[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 #include "pbd/stacktrace.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 "ardour_ui.h"
35 #include "streamview.h"
36 #include "region_view.h"
37 #include "automation_region_view.h"
38 #include "route_time_axis.h"
39 #include "simplerect.h"
40 #include "simpleline.h"
41 #include "waveview.h"
42 #include "public_editor.h"
43 #include "region_editor.h"
44 #include "ghostregion.h"
45 #include "route_time_axis.h"
46 #include "utils.h"
47 #include "rgb_macros.h"
48 #include "gui_thread.h"
49
50 #include "i18n.h"
51
52 using namespace std;
53 using namespace sigc;
54 using namespace ARDOUR;
55 using namespace PBD;
56 using namespace Editing;
57 using namespace ArdourCanvas;
58
59 static const int32_t sync_mark_width = 9;
60
61 sigc::signal<void,RegionView*> RegionView::RegionViewGoingAway;
62
63 RegionView::RegionView (ArdourCanvas::Group*              parent, 
64                         TimeAxisView&                     tv,
65                         boost::shared_ptr<ARDOUR::Region> r,
66                         double                            spu,
67                         Gdk::Color const &                basic_color)
68         : TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), false,
69                             TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
70                                                           TimeAxisViewItem::ShowNameHighlight| TimeAxisViewItem::ShowFrame))
71         , _region (r)
72         , sync_mark(0)
73         , sync_line(0)
74         , editor(0)
75         , current_visible_sync_position(0.0)
76         , valid(false)
77         , _enable_display(false)
78         , _pixel_width(1.0)
79         , in_destructor(false)
80         , wait_for_data(false)
81         , _time_converter(r->session(), r->position())
82 {
83 }
84
85 RegionView::RegionView (const RegionView& other)
86         : sigc::trackable(other)
87         , TimeAxisViewItem (other)
88         , _time_converter(other._time_converter)
89 {
90         /* derived concrete type will call init () */
91
92         _region = other._region;
93         current_visible_sync_position = other.current_visible_sync_position;
94         valid = false;
95         _pixel_width = other._pixel_width;
96         _height = other._height;
97 }
98
99 RegionView::RegionView (const RegionView& other, boost::shared_ptr<Region> other_region)
100         : sigc::trackable(other)
101         , TimeAxisViewItem (other)
102         , _time_converter(other._time_converter)
103 {
104         /* this is a pseudo-copy constructor used when dragging regions 
105            around on the canvas.
106         */
107
108         /* derived concrete type will call init () */
109
110         _region = other_region;
111         current_visible_sync_position = other.current_visible_sync_position;
112         valid = false;
113         _pixel_width = other._pixel_width;
114         _height = other._height;
115 }
116
117 RegionView::RegionView (ArdourCanvas::Group*         parent, 
118                         TimeAxisView&                tv,
119                         boost::shared_ptr<ARDOUR::Region> r,
120                         double                       spu,
121                         Gdk::Color const &           basic_color,
122                                                 bool recording,
123                         TimeAxisViewItem::Visibility visibility)
124         : TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), recording, visibility)
125         , _region (r)
126         , sync_mark(0)
127         , sync_line(0)
128         , editor(0)
129         , current_visible_sync_position(0.0)
130         , valid(false)
131         , _enable_display(false)
132         , _pixel_width(1.0)
133         , in_destructor(false)
134         , wait_for_data(false)
135         , _time_converter(r->session(), r->position())
136 {
137 }
138
139 void
140 RegionView::init (Gdk::Color const & basic_color, bool wfd)
141 {
142         editor        = 0;
143         valid         = true;
144         in_destructor = false;
145         _height       = 0;
146         wait_for_data = wfd;
147         sync_mark     = 0;
148         sync_line     = 0;
149         sync_mark     = 0;
150         sync_line     = 0;
151
152         compute_colors (basic_color);
153
154         if (name_highlight) {
155                 name_highlight->set_data ("regionview", this);
156                 name_highlight->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_name_highlight_event), name_highlight, this));
157                 
158                 frame_handle_start->set_data ("regionview", this);
159                 frame_handle_start->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_name_highlight_event), frame_handle_start, this));
160
161                 frame_handle_end->set_data ("regionview", this);
162                 frame_handle_end->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_name_highlight_event), frame_handle_end, this));
163
164                 frame_handle_start->raise_to_top();
165                 frame_handle_end->raise_to_top();
166         }
167
168         if (name_pixbuf) {
169                 name_pixbuf->set_data ("regionview", this);
170         }
171
172         if (wfd)
173                 _enable_display = true;
174
175         set_height (trackview.current_height());
176
177         _region->StateChanged.connect (mem_fun(*this, &RegionView::region_changed));
178
179         group->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_event), group, this));
180
181         set_colors ();
182
183         ColorsChanged.connect (mem_fun (*this, &RegionView::color_handler));
184
185         /* XXX sync mark drag? */
186 }
187
188 RegionView::~RegionView ()
189 {
190         in_destructor = true;
191
192         for (vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
193                 delete *g;
194         }
195
196         for (std::list<ArdourCanvas::SimpleRect*>::iterator i = _coverage_frames.begin (); i != _coverage_frames.end (); ++i) {
197                 delete *i;
198         }
199
200         delete editor;
201 }
202
203 gint
204 RegionView::_lock_toggle (ArdourCanvas::Item*, GdkEvent* ev, void* arg)
205 {
206         switch (ev->type) {
207         case GDK_BUTTON_RELEASE:
208                 static_cast<RegionView*>(arg)->lock_toggle ();
209                 return TRUE;
210                 break;
211         default:
212                 break;
213         } 
214         return FALSE;
215 }
216
217 void
218 RegionView::lock_toggle ()
219 {
220         _region->set_locked (!_region->locked());
221 }
222
223 void
224 RegionView::region_changed (Change what_changed)
225 {
226         ENSURE_GUI_THREAD (bind (mem_fun(*this, &RegionView::region_changed), what_changed));
227
228         if (what_changed & BoundsChanged) {
229                 region_resized (what_changed);
230                 region_sync_changed ();
231         }
232         if (what_changed & Region::MuteChanged) {
233                 region_muted ();
234         }
235         if (what_changed & Region::OpacityChanged) {
236                 region_opacity ();
237         }
238         if (what_changed & ARDOUR::NameChanged) {
239                 region_renamed ();
240         }
241         if (what_changed & Region::SyncOffsetChanged) {
242                 region_sync_changed ();
243         }
244         if (what_changed & Region::LockChanged) {
245                 region_locked ();
246         }
247 }
248
249 void
250 RegionView::region_locked ()
251 {
252         /* name will show locked status */
253         region_renamed ();
254 }
255
256 void
257 RegionView::region_resized (Change what_changed)
258 {
259         double unit_length;
260
261         if (what_changed & ARDOUR::PositionChanged) {
262                 set_position (_region->position(), 0);
263                 _time_converter.set_origin(_region->position());
264         }
265
266         if (what_changed & Change (StartChanged|LengthChanged)) {
267
268                 set_duration (_region->length(), 0);
269
270                 unit_length = _region->length() / samples_per_unit;
271                 
272                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
273
274                         (*i)->set_duration (unit_length);
275
276                 }
277         }
278 }
279
280 void
281 RegionView::reset_width_dependent_items (double pixel_width)
282 {
283         TimeAxisViewItem::reset_width_dependent_items (pixel_width);
284         _pixel_width = pixel_width;
285 }
286
287 void
288 RegionView::region_muted ()
289 {
290         set_frame_color ();
291         region_renamed ();
292 }
293
294 void
295 RegionView::region_opacity ()
296 {
297         set_frame_color ();
298 }
299
300 void
301 RegionView::raise_to_top ()
302 {
303         _region->raise_to_top ();
304 }
305
306 void
307 RegionView::lower_to_bottom ()
308 {
309         _region->lower_to_bottom ();
310 }
311
312 bool
313 RegionView::set_position (nframes_t pos, void* /*src*/, double* ignored)
314 {
315         double delta;
316         bool ret;
317
318         if (!(ret = TimeAxisViewItem::set_position (pos, this, &delta))) {
319                 return false;
320         }
321
322         if (ignored) {
323                 *ignored = delta;
324         }
325
326         if (delta) {
327                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
328                         (*i)->group->move (delta, 0.0);
329                 }
330         }
331
332         return ret;
333 }
334
335 void
336 RegionView::set_samples_per_unit (gdouble spu)
337 {
338         TimeAxisViewItem::set_samples_per_unit (spu);
339
340         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
341                 (*i)->set_samples_per_unit (spu);
342                 (*i)->set_duration (_region->length() / samples_per_unit);
343         }
344
345         region_sync_changed ();
346 }
347
348 bool
349 RegionView::set_duration (nframes_t frames, void *src)
350 {
351         if (!TimeAxisViewItem::set_duration (frames, src)) {
352                 return false;
353         }
354         
355         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
356                 (*i)->set_duration (_region->length() / samples_per_unit);
357         }
358
359         return true;
360 }
361
362 void
363 RegionView::set_colors ()
364 {
365         TimeAxisViewItem::set_colors ();
366         
367         if (sync_mark) {
368                 sync_mark->property_fill_color_rgba() = fill_color;
369                 sync_line->property_fill_color_rgba() = fill_color;
370         }
371 }
372
373 void
374 RegionView::set_frame_color ()
375 {
376         if (_region->opaque()) {
377                 fill_opacity = 130;
378         } else {
379                 fill_opacity = 60;
380         }
381
382         TimeAxisViewItem::set_frame_color ();
383 }
384
385 void
386 RegionView::fake_set_opaque (bool yn)
387 {
388        if (yn) {
389                fill_opacity = 130;
390        } else {
391                fill_opacity = 60;
392        }
393        
394        set_frame_color ();
395 }
396
397 void
398 RegionView::hide_region_editor()
399 {
400         if (editor) {
401                 editor->hide_all ();
402         }
403 }
404
405 Glib::ustring
406 RegionView::make_name () const
407 {
408         Glib::ustring str;
409
410         // XXX nice to have some good icons for this
411
412         if (_region->locked()) {
413                 str += '>';
414                 str += _region->name();
415                 str += '<';
416         } else if (_region->position_locked()) {
417                 str += '{';
418                 str += _region->name();
419                 str += '}';
420         } else {
421                 str = _region->name();
422         }
423
424         if (_region->muted()) {
425                 str = string ("!") + str;
426         }
427
428         return str;
429 }
430
431 void
432 RegionView::region_renamed ()
433 {
434         Glib::ustring str = make_name ();
435
436         set_item_name (str, this);
437         set_name_text (str);
438         reset_width_dependent_items (_pixel_width);
439 }
440
441 void
442 RegionView::region_sync_changed ()
443 {
444         int sync_dir;
445         nframes_t sync_offset;
446
447         sync_offset = _region->sync_offset (sync_dir);
448
449         if (sync_offset == 0) {
450                 /* no need for a sync mark */
451                 if (sync_mark) {
452                         sync_mark->hide();
453                         sync_line->hide ();
454                 }
455                 return;
456         }
457
458         if (!sync_mark) {
459
460                 /* points set below */
461                 
462                 sync_mark = new ArdourCanvas::Polygon (*group);
463                 sync_mark->property_fill_color_rgba() = fill_color;
464
465                 sync_line = new ArdourCanvas::Line (*group);
466                 sync_line->property_fill_color_rgba() = fill_color;
467                 sync_line->property_width_pixels() = 1;
468         }
469
470         /* this has to handle both a genuine change of position, a change of samples_per_unit,
471            and a change in the bounds of the _region->
472          */
473
474         if (sync_offset == 0) {
475
476                 /* no sync mark - its the start of the region */
477                 
478                 sync_mark->hide();
479                 sync_line->hide ();
480
481         } else {
482
483                 if ((sync_dir < 0) || ((sync_dir > 0) && (sync_offset > _region->length()))) { 
484
485                         /* no sync mark - its out of the bounds of the region */
486
487                         sync_mark->hide();
488                         sync_line->hide ();
489
490                 } else {
491
492                         /* lets do it */
493
494                         Points points;
495                         
496                         //points = sync_mark->property_points().get_value();
497                         
498                         double offset = sync_offset / samples_per_unit;
499                         points.push_back (Gnome::Art::Point (offset - ((sync_mark_width-1)/2), 1));
500                         points.push_back (Gnome::Art::Point (offset + ((sync_mark_width-1)/2), 1));
501                         points.push_back (Gnome::Art::Point (offset, sync_mark_width - 1));
502                         points.push_back (Gnome::Art::Point (offset - ((sync_mark_width-1)/2), 1));     
503                         sync_mark->property_points().set_value (points);
504                         sync_mark->show ();
505
506                         points.clear ();
507                         points.push_back (Gnome::Art::Point (offset, 0));
508                         points.push_back (Gnome::Art::Point (offset, trackview.current_height() - NAME_HIGHLIGHT_SIZE));
509
510                         sync_line->property_points().set_value (points);
511                         sync_line->show ();
512                 }
513         }
514 }
515
516 void
517 RegionView::move (double x_delta, double y_delta)
518 {
519         if (!_region->can_move() || (x_delta == 0 && y_delta == 0)) {
520                 return;
521         }
522
523         get_canvas_group()->move (x_delta, y_delta);
524
525         /* note: ghosts never leave their tracks so y_delta for them is always zero */
526
527         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
528                 (*i)->group->move (x_delta, 0.0);
529         }
530 }
531
532 void
533 RegionView::remove_ghost_in (TimeAxisView& tv)
534 {
535         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
536                 if (&(*i)->trackview == &tv) {
537                         delete *i;
538                         break;
539                 }
540         }
541 }
542
543 void
544 RegionView::remove_ghost (GhostRegion* ghost)
545 {
546         if (in_destructor) {
547                 return;
548         }
549
550         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
551                 if (*i == ghost) {
552                         ghosts.erase (i);
553                         break;
554                 }
555         }
556 }
557
558 uint32_t
559 RegionView::get_fill_color ()
560 {
561         return fill_color;
562 }
563
564 void
565 RegionView::set_height (double h)
566 {
567         TimeAxisViewItem::set_height(h);
568
569         if (sync_line) {
570                 Points points;
571                 int sync_dir;
572                 nframes_t sync_offset;
573                 sync_offset = _region->sync_offset (sync_dir);
574                 double offset = sync_offset / samples_per_unit;
575
576                 points.push_back (Gnome::Art::Point (offset, 0));
577                 points.push_back (Gnome::Art::Point (offset, h - NAME_HIGHLIGHT_SIZE));
578                 sync_line->property_points().set_value (points);
579         }
580 }
581
582 /** Remove old coverage frames and make new ones, if we're in a LayerDisplay mode
583  *  which uses them. */
584 void
585 RegionView::update_coverage_frames (LayerDisplay d)
586 {
587         /* remove old coverage frames */
588         for (std::list<ArdourCanvas::SimpleRect*>::iterator i = _coverage_frames.begin (); i != _coverage_frames.end (); ++i) {
589                 delete *i;
590         }
591
592         _coverage_frames.clear ();
593
594         if (d != Stacked) {
595                 /* don't do coverage frames unless we're in stacked mode */
596                 return;
597         }
598
599         boost::shared_ptr<Playlist> pl (_region->playlist ());
600         if (!pl) {
601                 return;
602         }
603
604         nframes_t const position = _region->first_frame ();
605         nframes_t t = position;
606         nframes_t const end = _region->last_frame ();
607
608         ArdourCanvas::SimpleRect* cr = 0;
609         bool me = false;
610
611         uint32_t const color = frame->property_fill_color_rgba ();
612         uint32_t const base_alpha = UINT_RGBA_A (color);
613         
614         while (t < end) {
615
616                 t++;
617
618                 /* is this region is on top at time t? */
619                 bool const new_me = (pl->top_unmuted_region_at (t) == _region);
620
621                 /* finish off any old rect, if required */
622                 if (cr && me != new_me) {
623                         cr->property_x2() = trackview.editor().frame_to_pixel (t - position);
624                 }
625
626                 /* start off any new rect, if required */
627                 if (cr == 0 || me != new_me) {
628                         cr = new ArdourCanvas::SimpleRect (*group);
629                         _coverage_frames.push_back (cr);
630                         cr->property_x1() = trackview.editor().frame_to_pixel (t - position);
631                         cr->property_y1() = 1;
632                         cr->property_y2() = _height + 1;
633                         cr->property_outline_pixels() = 0;
634                         /* areas that will be played get a lower alpha */
635                         uint32_t alpha = base_alpha;
636                         if (new_me) {
637                                 alpha /= 2;
638                         }
639                         cr->property_fill_color_rgba () = UINT_RGBA_CHANGE_A (color, alpha);
640                 }
641
642                 t = pl->find_next_region_boundary (t, 1);
643                 me = new_me;
644         }
645
646         if (cr) {
647                 /* finish off the last rectangle */
648                 cr->property_x2() = trackview.editor().frame_to_pixel (end - position);
649         }
650
651         frame_handle_start->raise_to_top ();
652         frame_handle_end->raise_to_top ();
653 }