attempt to fix deep confusion in GUI code about converting between musical and audio...
[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 #include "ardour/session.h"
33
34 #include "ardour_ui.h"
35 #include "global_signals.h"
36 #include "canvas-noevent-text.h"
37 #include "streamview.h"
38 #include "region_view.h"
39 #include "automation_region_view.h"
40 #include "route_time_axis.h"
41 #include "simplerect.h"
42 #include "simpleline.h"
43 #include "waveview.h"
44 #include "public_editor.h"
45 #include "region_editor.h"
46 #include "ghostregion.h"
47 #include "route_time_axis.h"
48 #include "ui_config.h"
49 #include "utils.h"
50 #include "rgb_macros.h"
51 #include "gui_thread.h"
52
53 #include "i18n.h"
54
55 using namespace std;
56 using namespace ARDOUR;
57 using namespace PBD;
58 using namespace Editing;
59 using namespace Gtk;
60 using namespace ArdourCanvas;
61
62 static const int32_t sync_mark_width = 9;
63
64 PBD::Signal1<void,RegionView*> RegionView::RegionViewGoingAway;
65
66 RegionView::RegionView (ArdourCanvas::Group*              parent,
67                         TimeAxisView&                     tv,
68                         boost::shared_ptr<ARDOUR::Region> r,
69                         double                            spu,
70                         Gdk::Color const &                basic_color,
71                         bool                              automation)
72         : TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), false, automation,
73                             TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
74                                                           TimeAxisViewItem::ShowNameHighlight| TimeAxisViewItem::ShowFrame))
75         , _region (r)
76         , sync_mark(0)
77         , sync_line(0)
78         , editor(0)
79         , current_visible_sync_position(0.0)
80         , valid(false)
81         , _enable_display(false)
82         , _pixel_width(1.0)
83         , in_destructor(false)
84         , wait_for_data(false)
85         , _silence_text (0)
86         , _region_relative_time_converter(r->session().tempo_map(), r->position())
87         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
88 {
89         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
90 }
91
92 RegionView::RegionView (const RegionView& other)
93         : sigc::trackable(other)
94         , TimeAxisViewItem (other)
95         , _silence_text (0)
96         , _region_relative_time_converter(other.region_relative_time_converter())
97         , _source_relative_time_converter(other.source_relative_time_converter())
98 {
99         /* derived concrete type will call init () */
100
101         _region = other._region;
102         current_visible_sync_position = other.current_visible_sync_position;
103         valid = false;
104         _pixel_width = other._pixel_width;
105
106         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
107 }
108
109 RegionView::RegionView (const RegionView& other, boost::shared_ptr<Region> other_region)
110         : sigc::trackable(other)
111         , TimeAxisViewItem (other)
112         , _silence_text (0)
113         , _region_relative_time_converter(other_region->session().tempo_map(), other_region->position())
114         , _source_relative_time_converter(other_region->session().tempo_map(), other_region->position() - other_region->start())
115 {
116         /* this is a pseudo-copy constructor used when dragging regions
117            around on the canvas.
118         */
119
120         /* derived concrete type will call init () */
121
122         _region = other_region;
123         current_visible_sync_position = other.current_visible_sync_position;
124         valid = false;
125         _pixel_width = other._pixel_width;
126
127         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
128 }
129
130 RegionView::RegionView (ArdourCanvas::Group*         parent,
131                         TimeAxisView&                tv,
132                         boost::shared_ptr<ARDOUR::Region> r,
133                         double                       spu,
134                         Gdk::Color const &           basic_color,
135                         bool                         recording,
136                         TimeAxisViewItem::Visibility visibility)
137         : TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), recording, false, visibility)
138         , _region (r)
139         , sync_mark(0)
140         , sync_line(0)
141         , editor(0)
142         , current_visible_sync_position(0.0)
143         , valid(false)
144         , _enable_display(false)
145         , _pixel_width(1.0)
146         , in_destructor(false)
147         , wait_for_data(false)
148         , _silence_text (0)
149         , _region_relative_time_converter(r->session().tempo_map(), r->position())
150         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
151 {
152 }
153
154 void
155 RegionView::init (Gdk::Color const & basic_color, bool wfd)
156 {
157         editor        = 0;
158         valid         = true;
159         in_destructor = false;
160         wait_for_data = wfd;
161         sync_mark     = 0;
162         sync_line     = 0;
163         sync_mark     = 0;
164         sync_line     = 0;
165
166         compute_colors (basic_color);
167
168         if (name_highlight) {
169                 name_highlight->set_data ("regionview", this);
170                 name_highlight->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_name_highlight_event), name_highlight, this));
171
172                 if (frame_handle_start) {
173                         frame_handle_start->set_data ("regionview", this);
174                         frame_handle_start->set_data ("isleft", (void*) 1);
175                         frame_handle_start->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_frame_handle_event), frame_handle_start, this));
176                         frame_handle_start->raise_to_top();
177                 }
178
179                 if (frame_handle_end) {
180                         frame_handle_end->set_data ("regionview", this);
181                         frame_handle_end->set_data ("isleft", (void*) 0);
182                         frame_handle_end->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_frame_handle_event), frame_handle_end, this));
183                         frame_handle_end->raise_to_top();
184                 }
185         }
186
187         if (name_pixbuf) {
188                 name_pixbuf->set_data ("regionview", this);
189                 name_pixbuf->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_name_event), name_pixbuf, this));
190         }
191
192         if (wfd) {
193                 _enable_display = true;
194         }
195
196         set_height (trackview.current_height());
197
198         _region->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RegionView::region_changed, this, _1), gui_context());
199
200         group->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_event), group, this));
201
202         set_colors ();
203
204         ColorsChanged.connect (sigc::mem_fun (*this, &RegionView::color_handler));
205
206         /* XXX sync mark drag? */
207 }
208
209 RegionView::~RegionView ()
210 {
211         in_destructor = true;
212
213         for (vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
214                 delete *g;
215         }
216
217         for (list<ArdourCanvas::SimpleRect*>::iterator i = _coverage_frames.begin (); i != _coverage_frames.end (); ++i) {
218                 delete *i;
219         }
220
221         drop_silent_frames ();
222
223         delete editor;
224 }
225
226 void
227 RegionView::set_silent_frames (const AudioIntervalResult& silences, double threshold)
228 {
229         framecnt_t shortest = max_framecnt;
230
231         /* remove old silent frames */
232         drop_silent_frames ();
233
234         if (silences.empty()) {
235                 return;
236         }
237
238         uint32_t const color = ARDOUR_UI::config()->canvasvar_Silence.get();
239
240         for (AudioIntervalResult::const_iterator i = silences.begin(); i != silences.end(); ++i) {
241
242                 ArdourCanvas::SimpleRect* cr = new ArdourCanvas::SimpleRect (*group);
243                 _silent_frames.push_back (cr);
244
245                 /* coordinates for the rect are relative to the regionview origin */
246
247                 cr->property_x1() = trackview.editor().frame_to_pixel (i->first - _region->start());
248                 cr->property_x2() = trackview.editor().frame_to_pixel (i->second - _region->start());
249                 cr->property_y1() = 1;
250                 cr->property_y2() = _height - 2;
251                 cr->property_outline_pixels() = 0;
252                 cr->property_fill_color_rgba () = color;
253
254                 shortest = min (shortest, i->second - i->first);
255         }
256
257         /* Find shortest audible segment */
258         framecnt_t shortest_audible = max_framecnt;
259
260         framecnt_t s = _region->start();
261         for (AudioIntervalResult::const_iterator i = silences.begin(); i != silences.end(); ++i) {
262                 framecnt_t const dur = i->first - s;
263                 if (dur > 0) {
264                         shortest_audible = min (shortest_audible, dur);
265                 }
266
267                 s = i->second;
268         }
269
270         framecnt_t const dur = _region->start() + _region->length() - 1 - s;
271         if (dur > 0) {
272                 shortest_audible = min (shortest_audible, dur);
273         }
274
275         _silence_text = new ArdourCanvas::NoEventText (*group);
276         _silence_text->property_font_desc() = get_font_for_style (N_("SilenceText"));
277         _silence_text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SilenceText.get();
278         _silence_text->property_anchor() = ANCHOR_NW;
279
280         /* both positions are relative to the region start offset in source */
281
282         _silence_text->property_x() = trackview.editor().frame_to_pixel (silences.front().first - _region->start()) + 10.0;
283         _silence_text->property_y() = 20.0;
284
285         double ms = (float) shortest/_region->session().frame_rate();
286
287         /* ms are now in seconds */
288
289         char const * sunits;
290
291         if (ms >= 60.0) {
292                 sunits = _("minutes");
293                 ms /= 60.0;
294         } else if (ms < 1.0) {
295                 sunits = _("msecs");
296                 ms *= 1000.0;
297         } else {
298                 sunits = _("secs");
299         }
300
301         string text = string_compose (ngettext ("%1 silent segment", "%1 silent segments", silences.size()), silences.size())
302                 + ", "
303                 + string_compose (_("shortest = %1 %2"), ms, sunits);
304
305         if (shortest_audible != max_framepos) {
306                 /* ms are now in seconds */
307                 double ma = (float) shortest_audible / _region->session().frame_rate();
308                 char const * aunits;
309
310                 if (ma >= 60.0) {
311                         aunits = _("minutes");
312                         ma /= 60.0;
313                 } else if (ma < 1.0) {
314                         aunits = _("msecs");
315                         ma *= 1000.0;
316                 } else {
317                         aunits = _("secs");
318                 }
319
320                 text += string_compose (_("\n  (shortest audible segment = %1 %2)"), ma, aunits);
321         }
322
323         _silence_text->property_text() = text.c_str ();
324 }
325
326 void
327 RegionView::hide_silent_frames ()
328 {
329         for (list<ArdourCanvas::SimpleRect*>::iterator i = _silent_frames.begin (); i != _silent_frames.end (); ++i) {
330                 (*i)->hide ();
331         }
332         _silence_text->hide();
333 }
334
335 void
336 RegionView::drop_silent_frames ()
337 {
338         for (list<ArdourCanvas::SimpleRect*>::iterator i = _silent_frames.begin (); i != _silent_frames.end (); ++i) {
339                 delete *i;
340         }
341         _silent_frames.clear ();
342
343         delete _silence_text;
344         _silence_text = 0;
345 }
346
347 gint
348 RegionView::_lock_toggle (ArdourCanvas::Item*, GdkEvent* ev, void* arg)
349 {
350         switch (ev->type) {
351         case GDK_BUTTON_RELEASE:
352                 static_cast<RegionView*>(arg)->lock_toggle ();
353                 return TRUE;
354                 break;
355         default:
356                 break;
357         }
358         return FALSE;
359 }
360
361 void
362 RegionView::lock_toggle ()
363 {
364         _region->set_locked (!_region->locked());
365 }
366
367 void
368 RegionView::region_changed (const PropertyChange& what_changed)
369 {
370         ENSURE_GUI_THREAD (*this, &RegionView::region_changed, what_changed);
371
372         if (what_changed.contains (ARDOUR::bounds_change)) {
373                 region_resized (what_changed);
374                 region_sync_changed ();
375         }
376         if (what_changed.contains (ARDOUR::Properties::muted)) {
377                 region_muted ();
378         }
379         if (what_changed.contains (ARDOUR::Properties::opaque)) {
380                 region_opacity ();
381         }
382         if (what_changed.contains (ARDOUR::Properties::name)) {
383                 region_renamed ();
384         }
385         if (what_changed.contains (ARDOUR::Properties::sync_position)) {
386                 region_sync_changed ();
387         }
388         if (what_changed.contains (ARDOUR::Properties::locked)) {
389                 region_locked ();
390         }
391 }
392
393 void
394 RegionView::region_locked ()
395 {
396         /* name will show locked status */
397         region_renamed ();
398 }
399
400 void
401 RegionView::region_resized (const PropertyChange& what_changed)
402 {
403         double unit_length;
404
405         if (what_changed.contains (ARDOUR::Properties::position)) {
406                 set_position (_region->position(), 0);
407                 _region_relative_time_converter.set_origin_b (_region->position());
408         }
409
410         if (what_changed.contains (ARDOUR::Properties::start) || what_changed.contains (ARDOUR::Properties::position)) {
411                 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
412         }
413
414         PropertyChange s_and_l;
415         s_and_l.add (ARDOUR::Properties::start);
416         s_and_l.add (ARDOUR::Properties::length);
417
418         if (what_changed.contains (s_and_l)) {
419
420                 set_duration (_region->length(), 0);
421
422                 unit_length = _region->length() / samples_per_unit;
423
424                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
425
426                         (*i)->set_duration (unit_length);
427
428                 }
429         }
430 }
431
432 void
433 RegionView::reset_width_dependent_items (double pixel_width)
434 {
435         TimeAxisViewItem::reset_width_dependent_items (pixel_width);
436         _pixel_width = pixel_width;
437 }
438
439 void
440 RegionView::region_muted ()
441 {
442         set_frame_color ();
443         region_renamed ();
444 }
445
446 void
447 RegionView::region_opacity ()
448 {
449         set_frame_color ();
450 }
451
452 void
453 RegionView::raise_to_top ()
454 {
455         _region->raise_to_top ();
456 }
457
458 void
459 RegionView::lower_to_bottom ()
460 {
461         _region->lower_to_bottom ();
462 }
463
464 bool
465 RegionView::set_position (framepos_t pos, void* /*src*/, double* ignored)
466 {
467         double delta;
468         bool ret;
469
470         if (!(ret = TimeAxisViewItem::set_position (pos, this, &delta))) {
471                 return false;
472         }
473
474         if (ignored) {
475                 *ignored = delta;
476         }
477
478         if (delta) {
479                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
480                         (*i)->group->move (delta, 0.0);
481                 }
482         }
483
484         return ret;
485 }
486
487 void
488 RegionView::set_samples_per_unit (gdouble spu)
489 {
490         TimeAxisViewItem::set_samples_per_unit (spu);
491
492         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
493                 (*i)->set_samples_per_unit (spu);
494                 (*i)->set_duration (_region->length() / samples_per_unit);
495         }
496
497         region_sync_changed ();
498 }
499
500 bool
501 RegionView::set_duration (framecnt_t frames, void *src)
502 {
503         if (!TimeAxisViewItem::set_duration (frames, src)) {
504                 return false;
505         }
506
507         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
508                 (*i)->set_duration (_region->length() / samples_per_unit);
509         }
510
511         return true;
512 }
513
514 void
515 RegionView::set_colors ()
516 {
517         TimeAxisViewItem::set_colors ();
518
519         if (sync_mark) {
520                 sync_mark->property_fill_color_rgba() = RGBA_TO_UINT(0,255,0,255);     // fill_color;                   // FIXME make a themeable colour
521                 sync_line->property_fill_color_rgba() = RGBA_TO_UINT(0,255,0,255);     // fill_color;                   // FIXME make a themeable colour
522         }
523 }
524
525 void
526 RegionView::set_frame_color ()
527 {
528         if (_region->opaque()) {
529                 fill_opacity = 130;
530         } else {
531                 fill_opacity = 60;
532         }
533
534         TimeAxisViewItem::set_frame_color ();
535 }
536
537 void
538 RegionView::fake_set_opaque (bool yn)
539 {
540        if (yn) {
541                fill_opacity = 130;
542        } else {
543                fill_opacity = 60;
544        }
545
546        set_frame_color ();
547 }
548
549 void
550 RegionView::show_region_editor ()
551 {
552         if (editor == 0) {
553                 editor = new RegionEditor (trackview.session(), region());
554         }
555
556         editor->present ();
557         editor->show_all();
558 }
559
560 void
561 RegionView::hide_region_editor()
562 {
563         if (editor) {
564                 editor->hide_all ();
565         }
566 }
567
568 std::string
569 RegionView::make_name () const
570 {
571         std::string str;
572
573         // XXX nice to have some good icons for this
574
575         if (_region->locked()) {
576                 str += '>';
577                 str += _region->name();
578                 str += '<';
579         } else if (_region->position_locked()) {
580                 str += '{';
581                 str += _region->name();
582                 str += '}';
583         } else {
584                 str = _region->name();
585         }
586
587         if (_region->muted()) {
588                 str = string ("!") + str;
589         }
590
591         return str;
592 }
593
594 void
595 RegionView::region_renamed ()
596 {
597         std::string str = make_name ();
598
599         set_item_name (str, this);
600         set_name_text (str);
601         reset_width_dependent_items (_pixel_width);
602 }
603
604 void
605 RegionView::region_sync_changed ()
606 {
607         int sync_dir;
608         framecnt_t sync_offset;
609
610         sync_offset = _region->sync_offset (sync_dir);
611
612         if (sync_offset == 0) {
613                 /* no need for a sync mark */
614                 if (sync_mark) {
615                         sync_mark->hide();
616                         sync_line->hide ();
617                 }
618                 return;
619         }
620
621         if (!sync_mark) {
622
623                 /* points set below */
624
625                 sync_mark = new ArdourCanvas::Polygon (*group);
626                 sync_mark->property_fill_color_rgba() = RGBA_TO_UINT(0,255,0,255);     // fill_color;                   // FIXME make a themeable colour
627
628                 sync_line = new ArdourCanvas::Line (*group);
629                 sync_line->property_fill_color_rgba() = RGBA_TO_UINT(0,255,0,255);     // fill_color;                   // FIXME make a themeable colour
630                 sync_line->property_width_pixels() = 1;
631         }
632
633         /* this has to handle both a genuine change of position, a change of samples_per_unit,
634            and a change in the bounds of the _region->
635          */
636
637         if (sync_offset == 0) {
638
639                 /* no sync mark - its the start of the region */
640
641                 sync_mark->hide();
642                 sync_line->hide ();
643
644         } else {
645
646                 if ((sync_dir < 0) || ((sync_dir > 0) && (sync_offset > _region->length()))) {
647
648                         /* no sync mark - its out of the bounds of the region */
649
650                         sync_mark->hide();
651                         sync_line->hide ();
652
653                 } else {
654
655                         /* lets do it */
656
657                         Points points;
658
659                         //points = sync_mark->property_points().get_value();
660
661                         double offset = sync_offset / samples_per_unit;
662                         points.push_back (Gnome::Art::Point (offset - ((sync_mark_width-1)/2), 1));
663                         points.push_back (Gnome::Art::Point (offset + ((sync_mark_width-1)/2), 1));
664                         points.push_back (Gnome::Art::Point (offset, sync_mark_width - 1));
665                         points.push_back (Gnome::Art::Point (offset - ((sync_mark_width-1)/2), 1));
666                         sync_mark->property_points().set_value (points);
667                         sync_mark->show ();
668
669                         points.clear ();
670                         points.push_back (Gnome::Art::Point (offset, 0));
671                         points.push_back (Gnome::Art::Point (offset, trackview.current_height() - NAME_HIGHLIGHT_SIZE));
672
673                         sync_line->property_points().set_value (points);
674                         sync_line->show ();
675                 }
676         }
677 }
678
679 void
680 RegionView::move (double x_delta, double y_delta)
681 {
682         if (!_region->can_move() || (x_delta == 0 && y_delta == 0)) {
683                 return;
684         }
685
686         get_canvas_group()->move (x_delta, y_delta);
687
688         /* note: ghosts never leave their tracks so y_delta for them is always zero */
689
690         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
691                 (*i)->group->move (x_delta, 0.0);
692         }
693 }
694
695 void
696 RegionView::remove_ghost_in (TimeAxisView& tv)
697 {
698         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
699                 if (&(*i)->trackview == &tv) {
700                         delete *i;
701                         break;
702                 }
703         }
704 }
705
706 void
707 RegionView::remove_ghost (GhostRegion* ghost)
708 {
709         if (in_destructor) {
710                 return;
711         }
712
713         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
714                 if (*i == ghost) {
715                         ghosts.erase (i);
716                         break;
717                 }
718         }
719 }
720
721 uint32_t
722 RegionView::get_fill_color ()
723 {
724         return fill_color;
725 }
726
727 void
728 RegionView::set_height (double h)
729 {
730         TimeAxisViewItem::set_height(h);
731
732         if (sync_line) {
733                 Points points;
734                 int sync_dir;
735                 framecnt_t sync_offset;
736                 sync_offset = _region->sync_offset (sync_dir);
737                 double offset = sync_offset / samples_per_unit;
738
739                 points.push_back (Gnome::Art::Point (offset, 0));
740                 points.push_back (Gnome::Art::Point (offset, h - NAME_HIGHLIGHT_SIZE));
741                 sync_line->property_points().set_value (points);
742         }
743
744         for (list<ArdourCanvas::SimpleRect*>::iterator i = _coverage_frames.begin(); i != _coverage_frames.end(); ++i) {
745                 (*i)->property_y2() = h + 1;
746         }
747
748         for (list<ArdourCanvas::SimpleRect*>::iterator i = _silent_frames.begin(); i != _silent_frames.end(); ++i) {
749                 (*i)->property_y2() = h + 1;
750         }
751
752 }
753
754 /** Remove old coverage frames and make new ones, if we're in a LayerDisplay mode
755  *  which uses them. */
756 void
757 RegionView::update_coverage_frames (LayerDisplay d)
758 {
759         /* remove old coverage frames */
760         for (list<ArdourCanvas::SimpleRect*>::iterator i = _coverage_frames.begin (); i != _coverage_frames.end (); ++i) {
761                 delete *i;
762         }
763
764         _coverage_frames.clear ();
765
766         if (d != Stacked) {
767                 /* don't do coverage frames unless we're in stacked mode */
768                 return;
769         }
770
771         boost::shared_ptr<Playlist> pl (_region->playlist ());
772         if (!pl) {
773                 return;
774         }
775
776         framepos_t const position = _region->first_frame ();
777         framepos_t t = position;
778         framepos_t const end = _region->last_frame ();
779
780         ArdourCanvas::SimpleRect* cr = 0;
781         bool me = false;
782
783         uint32_t const color = frame->property_fill_color_rgba ();
784         uint32_t const base_alpha = UINT_RGBA_A (color);
785
786         while (t < end) {
787
788                 t++;
789
790                 /* is this region is on top at time t? */
791                 bool const new_me = (pl->top_unmuted_region_at (t) == _region);
792
793                 /* finish off any old rect, if required */
794                 if (cr && me != new_me) {
795                         cr->property_x2() = trackview.editor().frame_to_pixel (t - position);
796                 }
797
798                 /* start off any new rect, if required */
799                 if (cr == 0 || me != new_me) {
800                         cr = new ArdourCanvas::SimpleRect (*group);
801                         _coverage_frames.push_back (cr);
802                         cr->property_x1() = trackview.editor().frame_to_pixel (t - position);
803                         cr->property_y1() = 1;
804                         cr->property_y2() = _height + 1;
805                         cr->property_outline_pixels() = 0;
806                         /* areas that will be played get a lower alpha */
807                         uint32_t alpha = base_alpha;
808                         if (new_me) {
809                                 alpha /= 2;
810                         }
811                         cr->property_fill_color_rgba () = UINT_RGBA_CHANGE_A (color, alpha);
812                 }
813
814                 t = pl->find_next_region_boundary (t, 1);
815                 me = new_me;
816         }
817
818         if (cr) {
819                 /* finish off the last rectangle */
820                 cr->property_x2() = trackview.editor().frame_to_pixel (end - position);
821         }
822
823         if (frame_handle_start) {
824                 frame_handle_start->raise_to_top ();
825         }
826
827         if (frame_handle_end) {
828                 frame_handle_end->raise_to_top ();
829         }
830
831         if (name_highlight) {
832                 name_highlight->raise_to_top ();
833         }
834
835         if (name_pixbuf) {
836                 name_pixbuf->raise_to_top ();
837         }
838 }
839
840 void
841 RegionView::trim_front (framepos_t new_bound, bool no_overlap)
842 {
843         if (_region->locked()) {
844                 return;
845         }
846
847         RouteTimeAxisView& rtv = dynamic_cast<RouteTimeAxisView&> (trackview);
848         double const speed = rtv.track()->speed ();
849
850         framepos_t const pre_trim_first_frame = _region->first_frame();
851
852         _region->trim_front ((framepos_t) (new_bound * speed));
853
854         if (no_overlap) {
855                 // Get the next region on the left of this region and shrink/expand it.
856                 boost::shared_ptr<Playlist> playlist (_region->playlist());
857                 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
858
859                 bool regions_touching = false;
860
861                 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)) {
862                         regions_touching = true;
863                 }
864
865                 // Only trim region on the left if the first frame has gone beyond the left region's last frame.
866                 if (region_left != 0 && (region_left->last_frame() > _region->first_frame() || regions_touching)) {
867                         region_left->trim_end (_region->first_frame() - 1);
868                 }
869         }
870
871         region_changed (ARDOUR::bounds_change);
872 }
873
874 void
875 RegionView::trim_end (framepos_t new_bound, bool no_overlap)
876 {
877         if (_region->locked()) {
878                 return;
879         }
880
881         RouteTimeAxisView& rtv = dynamic_cast<RouteTimeAxisView&> (trackview);
882         double const speed = rtv.track()->speed ();
883
884         framepos_t const pre_trim_last_frame = _region->last_frame();
885
886         _region->trim_end ((framepos_t) (new_bound * speed));
887
888         if (no_overlap) {
889                 // Get the next region on the right of this region and shrink/expand it.
890                 boost::shared_ptr<Playlist> playlist (_region->playlist());
891                 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
892
893                 bool regions_touching = false;
894
895                 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)) {
896                         regions_touching = true;
897                 }
898
899                 // Only trim region on the right if the last frame has gone beyond the right region's first frame.
900                 if (region_right != 0 && (region_right->first_frame() < _region->last_frame() || regions_touching)) {
901                         region_right->trim_front (_region->last_frame() + 1);
902                 }
903
904                 region_changed (ARDOUR::bounds_change);
905
906         } else {
907                 region_changed (PropertyChange (ARDOUR::Properties::length));
908         }
909 }
910
911
912 void
913 RegionView::thaw_after_trim ()
914 {
915         if (_region->locked()) {
916                 return;
917         }
918
919         _region->resume_property_changes ();
920 }
921
922
923 void
924 RegionView::trim_contents (framepos_t frame_delta, bool left_direction, bool swap_direction)
925 {
926         if (_region->locked()) {
927                 return;
928         }
929
930         framepos_t new_bound;
931
932         RouteTimeAxisView& rtv = dynamic_cast<RouteTimeAxisView&> (trackview);
933         double const speed = rtv.track()->speed ();
934
935         if (left_direction) {
936                 if (swap_direction) {
937                         new_bound = (framepos_t) (_region->position() / speed) + frame_delta;
938                 } else {
939                         new_bound = (framepos_t) (_region->position() / speed) - frame_delta;
940                 }
941         } else {
942                 if (swap_direction) {
943                         new_bound = (framepos_t) (_region->position() / speed) - frame_delta;
944                 } else {
945                         new_bound = (framepos_t) (_region->position() / speed) + frame_delta;
946                 }
947         }
948
949         _region->trim_start ((framepos_t) (new_bound * speed));
950         region_changed (PropertyChange (ARDOUR::Properties::start));
951 }
952