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