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