initial commit of hand merging, plus getting "ancient" waf script to work correctly
[ardour.git] / gtk2_ardour / automation_streamview.cc
1 /*
2     Copyright (C) 2001-2007 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 #include <cassert>
21 #include <utility>
22
23 #include <gtkmm.h>
24
25 #include <gtkmm2ext/gtk_ui.h>
26
27 #include "ardour/midi_region.h"
28 #include "ardour/midi_source.h"
29
30 #include "automation_streamview.h"
31 #include "region_view.h"
32 #include "automation_region_view.h"
33 #include "automation_time_axis.h"
34 #include "region_selection.h"
35 #include "selection.h"
36 #include "public_editor.h"
37 #include "ardour_ui.h"
38 #include "rgb_macros.h"
39 #include "gui_thread.h"
40 #include "utils.h"
41
42 using namespace std;
43 using namespace ARDOUR;
44 using namespace PBD;
45 using namespace Editing;
46
47 AutomationStreamView::AutomationStreamView (AutomationTimeAxisView& tv)
48         : StreamView (*dynamic_cast<RouteTimeAxisView*>(tv.get_parent()),
49                       new ArdourCanvas::Group (tv.canvas_background()),
50                       new ArdourCanvas::Group (tv.canvas_display()))
51         , _automation_view(tv)
52         , _pending_automation_state (Off)
53 {
54         //canvas_rect->property_fill_color_rgba() = stream_base_color;
55         canvas_rect->set_outline_color (RGBA_BLACK);
56 }
57
58 AutomationStreamView::~AutomationStreamView ()
59 {
60 }
61
62
63 RegionView*
64 AutomationStreamView::add_region_view_internal (boost::shared_ptr<Region> region, bool wfd, bool /*recording*/)
65 {
66         assert (region);
67
68         if (wfd) {
69                 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region);
70                 if (mr) {
71                         mr->midi_source()->load_model();
72                 }
73         }
74
75         const boost::shared_ptr<AutomationControl> control = boost::dynamic_pointer_cast<AutomationControl> (
76                 region->control (_automation_view.parameter(), true)
77                 );
78
79         boost::shared_ptr<AutomationList> list;
80         if (control) {
81                 list = boost::dynamic_pointer_cast<AutomationList>(control->list());
82                 assert(!control->list() || list);
83         }
84
85         AutomationRegionView *region_view;
86         std::list<RegionView *>::iterator i;
87
88         for (i = region_views.begin(); i != region_views.end(); ++i) {
89                 if ((*i)->region() == region) {
90
91                         /* great. we already have an AutomationRegionView for this Region. use it again. */
92                         AutomationRegionView* arv = dynamic_cast<AutomationRegionView*>(*i);;
93
94                         if (arv->line()) {
95                                 arv->line()->set_list (list);
96                         }
97                         (*i)->set_valid (true);
98                         (*i)->enable_display(wfd);
99                         display_region(arv);
100
101                         return 0;
102                 }
103         }
104
105         region_view = new AutomationRegionView (
106                 _canvas_group, _automation_view, region,
107                 _automation_view.parameter (), list,
108                 _frames_per_pixel, region_color
109                 );
110
111         region_view->init (region_color, false);
112         region_views.push_front (region_view);
113
114         /* follow global waveform setting */
115
116         if (wfd) {
117                 region_view->enable_display(true);
118                 //region_view->midi_region()->midi_source(0)->load_model();
119         }
120
121         display_region(region_view);
122
123         /* catch regionview going away */
124         region->DropReferences.connect (*this, invalidator (*this), boost::bind (&AutomationStreamView::remove_region_view, this, boost::weak_ptr<Region>(region)), gui_context());
125
126         /* setup automation state for this region */
127         boost::shared_ptr<AutomationLine> line = region_view->line ();
128         if (line && line->the_list()) {
129                 line->the_list()->set_automation_state (automation_state ());
130         }
131
132         RegionViewAdded (region_view);
133
134         return region_view;
135 }
136
137 void
138 AutomationStreamView::display_region(AutomationRegionView* region_view)
139 {
140         region_view->line().reset();
141 }
142
143 void
144 AutomationStreamView::set_automation_state (AutoState state)
145 {
146         /* Setting the automation state for this view sets the state of all regions' lists to the same thing */
147
148         if (region_views.empty()) {
149                 _pending_automation_state = state;
150         } else {
151                 list<boost::shared_ptr<AutomationLine> > lines = get_lines ();
152
153                 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
154                         if ((*i)->the_list()) {
155                                 (*i)->the_list()->set_automation_state (state);
156                         }
157                 }
158         }
159 }
160
161 void
162 AutomationStreamView::redisplay_track ()
163 {
164         // Flag region views as invalid and disable drawing
165         for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
166                 (*i)->set_valid (false);
167                 (*i)->enable_display(false);
168         }
169
170         // Add and display region views, and flag them as valid
171         if (_trackview.is_track()) {
172                 _trackview.track()->playlist()->foreach_region (
173                         sigc::hide_return (sigc::mem_fun (*this, &StreamView::add_region_view))
174                         );
175         }
176
177         // Stack regions by layer, and remove invalid regions
178         layer_regions();
179 }
180
181
182 void
183 AutomationStreamView::setup_rec_box ()
184 {
185 }
186
187 void
188 AutomationStreamView::color_handler ()
189 {
190         /*if (_trackview.is_midi_track()) {
191                 canvas_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiTrackBase.get();
192         }
193
194         if (!_trackview.is_midi_track()) {
195                 canvas_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiBusBase.get();;
196         }*/
197 }
198
199 AutoState
200 AutomationStreamView::automation_state () const
201 {
202         if (region_views.empty()) {
203                 return _pending_automation_state;
204         }
205
206         boost::shared_ptr<AutomationLine> line = ((AutomationRegionView*) region_views.front())->line ();
207         if (!line || !line->the_list()) {
208                 return Off;
209         }
210
211         return line->the_list()->automation_state ();
212 }
213
214 bool
215 AutomationStreamView::has_automation () const
216 {
217         list<boost::shared_ptr<AutomationLine> > lines = get_lines ();
218
219         for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
220                 if ((*i)->npoints() > 0) {
221                         return true;
222                 }
223         }
224
225         return false;
226 }
227
228 /** Our parent AutomationTimeAxisView calls this when the user requests a particular
229  *  InterpolationStyle; tell the AutomationLists in our regions.
230  */
231 void
232 AutomationStreamView::set_interpolation (AutomationList::InterpolationStyle s)
233 {
234         list<boost::shared_ptr<AutomationLine> > lines = get_lines ();
235
236         for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
237                 (*i)->the_list()->set_interpolation (s);
238         }
239 }
240
241 AutomationList::InterpolationStyle
242 AutomationStreamView::interpolation () const
243 {
244         if (region_views.empty()) {
245                 return AutomationList::Linear;
246         }
247
248         AutomationRegionView* v = dynamic_cast<AutomationRegionView*> (region_views.front());
249         if (v) {
250                 return v->line()->the_list()->interpolation ();
251         }
252         return AutomationList::Linear;
253 }
254
255 /** Clear all automation displayed in this view */
256 void
257 AutomationStreamView::clear ()
258 {
259         list<boost::shared_ptr<AutomationLine> > lines = get_lines ();
260
261         for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
262                 (*i)->clear ();
263         }
264 }
265
266 /** @param start Start position in session frames.
267  *  @param end End position in session frames.
268  *  @param bot Bottom position expressed as a fraction of track height where 0 is the bottom of the track.
269  *  @param top Top position expressed as a fraction of track height where 0 is the bottom of the track.
270  *  NOTE: this y system is different to that for the StreamView method that this overrides, which is a little
271  *  confusing.
272  */
273 void
274 AutomationStreamView::get_selectables (framepos_t start, framepos_t end, double botfrac, double topfrac, list<Selectable*>& results)
275 {
276         for (list<RegionView*>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
277                 AutomationRegionView* arv = dynamic_cast<AutomationRegionView*> (*i);
278                 assert (arv);
279                 arv->line()->get_selectables (start, end, botfrac, topfrac, results);
280         }
281 }
282
283 void
284 AutomationStreamView::set_selected_points (PointSelection& ps)
285 {
286         list<boost::shared_ptr<AutomationLine> > lines = get_lines ();
287
288         for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
289                 (*i)->set_selected_points (ps);
290         }
291 }
292
293 list<boost::shared_ptr<AutomationLine> >
294 AutomationStreamView::get_lines () const
295 {
296         list<boost::shared_ptr<AutomationLine> > lines;
297
298         for (list<RegionView*>::const_iterator i = region_views.begin(); i != region_views.end(); ++i) {
299                 AutomationRegionView* arv = dynamic_cast<AutomationRegionView*> (*i);
300                 assert (arv);
301                 lines.push_back (arv->line());
302         }
303
304         return lines;
305 }
306
307 struct RegionPositionSorter {
308         bool operator() (RegionView* a, RegionView* b) {
309                 return a->region()->position() < b->region()->position();
310         }
311 };
312
313
314 /** @param pos Position, in session frames.
315  *  @return AutomationLine to paste to for that position, or 0 if there is none appropriate.
316  */
317 boost::shared_ptr<AutomationLine>
318 AutomationStreamView::paste_line (framepos_t pos)
319 {
320         /* XXX: not sure how best to pick this; for now, just use the last region which starts before pos */
321
322         if (region_views.empty()) {
323                 return boost::shared_ptr<AutomationLine> ();
324         }
325
326         region_views.sort (RegionPositionSorter ());
327
328         list<RegionView*>::const_iterator prev = region_views.begin ();
329
330         for (list<RegionView*>::const_iterator i = region_views.begin(); i != region_views.end(); ++i) {
331                 if ((*i)->region()->position() > pos) {
332                         break;
333                 }
334                 prev = i;
335         }
336
337         boost::shared_ptr<Region> r = (*prev)->region ();
338
339         /* If *prev doesn't cover pos, it's no good */
340         if (r->position() > pos || ((r->position() + r->length()) < pos)) {
341                 return boost::shared_ptr<AutomationLine> ();
342         }
343
344         AutomationRegionView* arv = dynamic_cast<AutomationRegionView*> (*prev);
345         assert (arv);
346
347         return arv->line ();
348 }