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