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