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