Committed underlay support (from Audun).
[ardour.git] / gtk2_ardour / streamview.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 #include <cmath>
20
21 #include <gtkmm.h>
22
23 #include <gtkmm2ext/gtk_ui.h>
24
25 #include <ardour/playlist.h>
26 #include <ardour/region.h>
27 #include <ardour/source.h>
28 #include <ardour/diskstream.h>
29 #include <ardour/track.h>
30
31 #include "streamview.h"
32 #include "region_view.h"
33 #include "route_time_axis.h"
34 #include "canvas-waveview.h"
35 #include "canvas-simplerect.h"
36 #include "region_selection.h"
37 #include "selection.h"
38 #include "public_editor.h"
39 #include "ardour_ui.h"
40 #include "rgb_macros.h"
41 #include "gui_thread.h"
42 #include "utils.h"
43
44 using namespace ARDOUR;
45 using namespace PBD;
46 using namespace Editing;
47
48 StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Group* group)
49         : _trackview (tv)
50         , canvas_group(group ? group : new ArdourCanvas::Group(*_trackview.canvas_display))
51         , canvas_rect(new ArdourCanvas::SimpleRect (*canvas_group))
52         , _samples_per_unit(_trackview.editor.get_current_zoom())
53         , rec_updating(false)
54         , rec_active(false)
55         , use_rec_regions(tv.editor.show_waveforms_recording())
56         , region_color(_trackview.color())
57         , stream_base_color(0xFFFFFFFF)
58         , layers(1)
59         , height(tv.height)
60         , layer_display(Overlaid)
61         , last_rec_data_frame(0)
62 {
63         /* set_position() will position the group */
64
65         canvas_rect->property_x1() = 0.0;
66         canvas_rect->property_y1() = 0.0;
67         canvas_rect->property_x2() = _trackview.editor.frame_to_pixel (max_frames);
68         canvas_rect->property_y2() = (double) tv.height;
69         canvas_rect->property_outline_what() = (guint32) (0x2|0x8);  // outline RHS and bottom 
70         // (Fill/Outline colours set in derived classes)
71
72         canvas_rect->signal_event().connect (bind (mem_fun (_trackview.editor, &PublicEditor::canvas_stream_view_event), canvas_rect, &_trackview));
73
74         if (_trackview.is_track()) {
75                 _trackview.track()->DiskstreamChanged.connect (mem_fun (*this, &StreamView::diskstream_changed));
76                 _trackview.session().TransportStateChange.connect (mem_fun (*this, &StreamView::transport_changed));
77                 _trackview.session().TransportLooped.connect (mem_fun (*this, &StreamView::transport_looped));
78                 _trackview.get_diskstream()->RecordEnableChanged.connect (mem_fun (*this, &StreamView::rec_enable_changed));
79                 _trackview.session().RecordStateChanged.connect (mem_fun (*this, &StreamView::sess_rec_enable_changed));
80         } 
81
82         ColorsChanged.connect (mem_fun (*this, &StreamView::color_handler));
83 }
84
85 StreamView::~StreamView ()
86 {
87         undisplay_diskstream ();
88         delete canvas_group;
89 }
90
91 void
92 StreamView::attach ()
93 {
94         if (_trackview.is_track()) {
95                 display_diskstream (_trackview.get_diskstream());
96         }
97 }
98
99 int
100 StreamView::set_position (gdouble x, gdouble y)
101 {
102         canvas_group->property_x() = x;
103         canvas_group->property_y() = y;
104         return 0;
105 }
106
107 int
108 StreamView::set_height (double h)
109 {
110         /* limit the values to something sane-ish */
111         if (h < 10.0 || h > 1000.0) {
112                 return -1;
113         }
114
115         if (canvas_rect->property_y2() == h) {
116                 return 0;
117         }
118
119         height = h;
120         update_contents_y_position_and_height ();
121         return 0;
122 }
123
124 int 
125 StreamView::set_samples_per_unit (gdouble spp)
126 {
127         RegionViewList::iterator i;
128
129         if (spp < 1.0) {
130                 return -1;
131         }
132
133         _samples_per_unit = spp;
134
135         for (i = region_views.begin(); i != region_views.end(); ++i) {
136                 (*i)->set_samples_per_unit (spp);
137         }
138
139         for (vector<RecBoxInfo>::iterator xi = rec_rects.begin(); xi != rec_rects.end(); ++xi) {
140                 RecBoxInfo &recbox = (*xi);
141                 
142                 gdouble xstart = _trackview.editor.frame_to_pixel ( recbox.start );
143                 gdouble xend = _trackview.editor.frame_to_pixel ( recbox.start + recbox.length );
144
145                 recbox.rectangle->property_x1() = xstart;
146                 recbox.rectangle->property_x2() = xend;
147         }
148
149         return 0;
150 }
151
152 void
153 StreamView::add_region_view (boost::shared_ptr<Region> r)
154 {
155         // ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::add_region_view), r));
156
157         add_region_view_internal (r, true);
158 }
159
160 void
161 StreamView::remove_region_view (boost::weak_ptr<Region> weak_r)
162 {
163         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::remove_region_view), weak_r));
164
165         boost::shared_ptr<Region> r (weak_r.lock());
166
167         if (!r) {
168                 return;
169         }
170
171         for (list<RegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
172                 if (((*i)->region()) == r) {
173                         delete *i;
174                         region_views.erase (i);
175                         break;
176                 }
177         }
178 }
179
180 void
181 StreamView::undisplay_diskstream ()
182 {
183         for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
184                 delete *i;
185         }
186
187         region_views.clear();
188 }
189
190 void
191 StreamView::display_diskstream (boost::shared_ptr<Diskstream> ds)
192 {
193         playlist_change_connection.disconnect();
194         playlist_changed (ds);
195         playlist_change_connection = ds->PlaylistChanged.connect (bind (mem_fun (*this, &StreamView::playlist_changed), ds));
196 }
197
198 void
199 StreamView::playlist_modified_weak (boost::weak_ptr<Diskstream> ds)
200 {
201         boost::shared_ptr<Diskstream> sp (ds.lock());
202         if (!sp) {
203                 return;
204         }
205
206         playlist_modified (sp);
207 }
208
209 void
210 StreamView::playlist_modified (boost::shared_ptr<Diskstream> ds)
211 {
212         /* we do not allow shared_ptr<T> to be bound to slots */
213         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::playlist_modified_weak), ds));
214
215         /* update layers count and the y positions and heights of our regions */
216         if (ds->playlist()) {
217                 layers = ds->playlist()->top_layer() + 1;
218                 update_contents_y_position_and_height ();
219                 redisplay_diskstream ();
220         }
221 }
222
223 void
224 StreamView::playlist_changed (boost::shared_ptr<Diskstream> ds)
225 {
226         /* XXX: binding to a shared_ptr, is this ok? */
227         ENSURE_GUI_THREAD (bind (mem_fun (*this, &StreamView::playlist_changed), ds));
228
229         /* disconnect from old playlist */
230
231         for (vector<sigc::connection>::iterator i = playlist_connections.begin(); i != playlist_connections.end(); ++i) {
232                 (*i).disconnect();
233         }
234         
235         playlist_connections.clear();
236         undisplay_diskstream ();
237
238         /* update layers count and the y positions and heights of our regions */
239         layers = ds->playlist()->top_layer() + 1;
240         update_contents_y_position_and_height ();
241         
242         /* draw it */
243         redisplay_diskstream ();
244
245         /* catch changes */
246
247         playlist_connections.push_back (ds->playlist()->Modified.connect (bind (mem_fun (*this, &StreamView::playlist_modified_weak), ds)));
248 }
249
250 void
251 StreamView::diskstream_changed ()
252 {
253         boost::shared_ptr<Track> t;
254
255         if ((t = _trackview.track()) != 0) {
256                 Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun (*this, &StreamView::display_diskstream), t->diskstream()));
257         } else {
258                 Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::undisplay_diskstream));
259         }
260 }
261
262 void
263 StreamView::apply_color (Gdk::Color& color, ColorTarget target)
264
265 {
266         list<RegionView *>::iterator i;
267
268         switch (target) {
269         case RegionColor:
270                 region_color = color;
271                 for (i = region_views.begin(); i != region_views.end(); ++i) {
272                         (*i)->set_color (region_color);
273                 }
274                 break;
275                 
276         case StreamBaseColor:
277                 stream_base_color = RGBA_TO_UINT (
278                         color.get_red_p(), color.get_green_p(), color.get_blue_p(), 255);
279                 canvas_rect->property_fill_color_rgba() = stream_base_color;
280                 break;
281         }
282 }
283
284 void
285 StreamView::region_layered (RegionView* rv)
286 {
287
288          /* 
289             Currently 'layer' has nothing to do with the desired canvas layer.
290             For now, ensure that multiple regionviews passed here in groups are 
291             ordered by 'layer' (lowest to highest). 
292
293             (see AudioStreamView::redisplay_diskstream ()).
294  
295             We move them to the top layer as they arrive. 
296          */
297
298         rv->get_canvas_group()->raise_to_top();
299 }
300
301 void
302 StreamView::rec_enable_changed ()
303 {
304         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
305 }
306
307 void
308 StreamView::sess_rec_enable_changed ()
309 {
310         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
311 }
312
313 void
314 StreamView::transport_changed()
315 {
316         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
317 }
318
319 void
320 StreamView::transport_looped()
321 {
322         // to force a new rec region
323         rec_active = false;
324         Gtkmm2ext::UI::instance()->call_slot (mem_fun (*this, &StreamView::setup_rec_box));
325 }
326
327 void
328 StreamView::update_rec_box ()
329 {
330         if (rec_active && rec_rects.size() > 0) {
331                 /* only update the last box */
332                 RecBoxInfo & rect = rec_rects.back();
333                 nframes_t at = _trackview.get_diskstream()->current_capture_end();
334                 double xstart;
335                 double xend;
336                 
337                 switch (_trackview.track()->mode()) {
338                 case Normal:
339                         rect.length = at - rect.start;
340                         xstart = _trackview.editor.frame_to_pixel (rect.start);
341                         xend = _trackview.editor.frame_to_pixel (at);
342                         break;
343                         
344                 case Destructive:
345                         rect.length = 2;
346                         xstart = _trackview.editor.frame_to_pixel (_trackview.get_diskstream()->current_capture_start());
347                         xend = _trackview.editor.frame_to_pixel (at);
348                         break;
349                 }
350                 
351                 rect.rectangle->property_x1() = xstart;
352                 rect.rectangle->property_x2() = xend;
353         }
354 }
355         
356 RegionView*
357 StreamView::find_view (boost::shared_ptr<const Region> region)
358 {
359         for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
360
361                 if ((*i)->region() == region) {
362                         return *i;
363                 }
364         }
365         return 0;
366 }
367         
368 void
369 StreamView::foreach_regionview (sigc::slot<void,RegionView*> slot)
370 {
371         for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
372                 slot (*i);
373         }
374 }
375
376 void
377 StreamView::set_selected_regionviews (RegionSelection& regions)
378 {
379         bool selected;
380
381         for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
382                 
383                 selected = false;
384                 
385                 for (RegionSelection::iterator ii = regions.begin(); ii != regions.end(); ++ii) {
386                         if (*i == *ii) {
387                                 selected = true;
388                         }
389                 }
390
391                 (*i)->set_selected (selected);
392         }
393 }
394
395 void
396 StreamView::get_selectables (nframes_t start, nframes_t end, list<Selectable*>& results)
397 {
398         for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
399                 if ((*i)->region()->coverage(start, end) != OverlapNone) {
400                         results.push_back (*i);
401                 }
402         }
403 }
404
405 void
406 StreamView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
407 {
408         for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
409                 if (!sel.regions.contains (*i)) {
410                         results.push_back (*i);
411                 }
412         }
413 }
414
415 void
416 StreamView::update_contents_y_position_and_height ()
417 {
418         canvas_rect->property_y2() = height;
419
420         const double lh = height / layers;
421
422         for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
423                 switch (layer_display) {
424                 case Overlaid:
425                         (*i)->set_y_position_and_height (0, height);
426                         break;
427                 case Stacked:
428                         double const y = (*i)->region()->layer() * lh;
429                         (*i)->set_y_position_and_height (y, lh);
430                         break;
431                 }
432         }
433
434         for (vector<RecBoxInfo>::iterator i = rec_rects.begin(); i != rec_rects.end(); ++i) {
435                 i->rectangle->property_y2() = height - 1.0;
436         }
437 }
438
439 void
440 StreamView::set_layer_display (LayerDisplay d)
441 {
442         layer_display = d;
443         update_contents_y_position_and_height ();
444 }