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