Revert internals of the last layering-related commit, and go back a slightly-cleaned...
[ardour.git] / gtk2_ardour / crossfade_view.cc
1 /*
2     Copyright (C) 2003 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 <algorithm>
21
22 #include "ardour/region.h"
23 #include <gtkmm2ext/doi.h>
24
25 #include "canvas-simplerect.h"
26 #include "canvas-curve.h"
27 #include "crossfade_view.h"
28 #include "global_signals.h"
29 #include "gui_thread.h"
30 #include "rgb_macros.h"
31 #include "audio_time_axis.h"
32 #include "public_editor.h"
33 #include "audio_region_view.h"
34 #include "utils.h"
35 #include "canvas_impl.h"
36 #include "ardour_ui.h"
37
38 using namespace ARDOUR;
39 using namespace PBD;
40 using namespace Editing;
41 using namespace Gnome;
42 using namespace Canvas;
43
44 PBD::Signal1<void,CrossfadeView*> CrossfadeView::CatchDeletion;
45
46 CrossfadeView::CrossfadeView (ArdourCanvas::Group *parent,
47                               RouteTimeAxisView &tv,
48                               boost::shared_ptr<Crossfade> xf,
49                               double spu,
50                               Gdk::Color& basic_color,
51                               AudioRegionView& lview,
52                               AudioRegionView& rview)
53
54
55         : TimeAxisViewItem ("xfade" /*xf.name()*/, *parent, tv, spu, basic_color, xf->position(),
56                             xf->length(), false, false, TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowFrame)),
57           crossfade (xf),
58           left_view (lview),
59           right_view (rview),
60           _all_in_view (false),
61           _child_height (0)
62 {
63         _valid = true;
64         _visible = true;
65
66         fade_in = new Line (*group);
67         fade_in->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeLine.get();
68         fade_in->property_width_pixels() = 1;
69
70         fade_out = new Line (*group);
71         fade_out->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeLine.get();
72         fade_out->property_width_pixels() = 1;
73
74         /* no frame around the xfade or overlap rects */
75
76         frame->property_outline_what() = 0;
77
78         /* never show the vestigial frame */
79         vestigial_frame->hide();
80         show_vestigial = false;
81
82         group->signal_event().connect (sigc::bind (sigc::mem_fun (tv.editor(), &PublicEditor::canvas_crossfade_view_event), group, this));
83
84         PropertyChange all_crossfade_properties;
85         all_crossfade_properties.add (ARDOUR::Properties::active);
86         all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
87         crossfade_changed (all_crossfade_properties);
88
89         crossfade->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&CrossfadeView::crossfade_changed, this, _1), gui_context());
90         crossfade->FadesChanged.connect (*this, invalidator (*this), ui_bind (&CrossfadeView::crossfade_fades_changed, this), gui_context());
91         ColorsChanged.connect (sigc::mem_fun (*this, &CrossfadeView::color_handler));
92 }
93
94 CrossfadeView::~CrossfadeView ()
95 {
96          CatchDeletion (this) ; /* EMIT_SIGNAL */
97 }
98
99 void
100 CrossfadeView::reset_width_dependent_items (double pixel_width)
101 {
102         TimeAxisViewItem::reset_width_dependent_items (pixel_width);
103
104         active_changed ();
105
106         if (pixel_width < 5) {
107                 fade_in->hide();
108                 fade_out->hide();
109         }
110 }
111
112 void
113 CrossfadeView::set_heights (double fade_height, double child_height)
114 {
115         if (child_height > TimeAxisViewItem::NAME_HIGHLIGHT_THRESH) {
116                 fade_height -= NAME_HIGHLIGHT_SIZE;
117                 child_height -= NAME_HIGHLIGHT_SIZE;
118         }
119
120         TimeAxisViewItem::set_height (fade_height);
121         _child_height = child_height;
122
123         redraw_curves ();
124 }
125
126 void
127 CrossfadeView::crossfade_changed (const PropertyChange& what_changed)
128 {
129         bool need_redraw_curves = false;
130
131         if (what_changed.contains (ARDOUR::bounds_change)) {
132                 set_position (crossfade->position(), this);
133                 set_duration (crossfade->length(), this);
134
135                 /* set_duration will call reset_width_dependent_items which in turn will call redraw_curves via active_changed,
136                    so no need for us to call it */
137                 need_redraw_curves = false;
138         }
139
140         if (what_changed.contains (ARDOUR::Properties::follow_overlap)) {
141                 need_redraw_curves = true;
142         }
143
144         if (what_changed.contains (ARDOUR::Properties::active)) {
145                 /* calls redraw_curves */
146                 active_changed ();
147         } else if (need_redraw_curves) {
148                 redraw_curves ();
149         }
150 }
151
152 /** Set up our fade_in and fade_out curves to contain points for the currently visible portion
153  *  of the crossfade.
154  */
155 void
156 CrossfadeView::redraw_curves ()
157 {
158         if (!crossfade->following_overlap()) {
159                 /* curves should not be visible */
160                 fade_in->hide ();
161                 fade_out->hide ();
162                 return;
163         }
164
165         if (_height < 0) {
166                 /* no space allocated yet */
167                 return;
168         }
169
170         PublicEditor& editor = get_time_axis_view().editor ();
171
172         framepos_t const editor_left = editor.leftmost_position ();
173         framepos_t const editor_right = editor_left + editor.current_page_frames ();
174         framepos_t const xfade_left = crossfade->position ();
175         framepos_t const xfade_right = xfade_left + crossfade->length ();
176
177         /* Work out the range of our frames that are visible */
178         framepos_t const min_frames = std::max (editor_left, xfade_left);
179         framepos_t const max_frames = std::min (editor_right, xfade_right);
180
181         _all_in_view = (editor_left <= xfade_left && editor_right >= xfade_right);
182
183         /* Hence the number of points that we will render */
184         int32_t const npoints = editor.frame_to_pixel (max_frames - min_frames);
185
186         if (!_visible || !crossfade->active() || npoints < 3) {
187                 fade_in->hide();
188                 fade_out->hide();
189                 return;
190         } else {
191                 fade_in->show();
192                 fade_out->show();
193         }
194
195         Points* points = get_canvas_points ("xfade edit redraw", npoints);
196         float* vec = new float[npoints];
197
198         crossfade->fade_in().curve().get_vector (min_frames - crossfade->position(), max_frames - crossfade->position(), vec, npoints);
199
200         /* Work out the offset from the start of the crossfade to the visible part, in pixels */
201         double xoff = 0;
202         if (crossfade->position() < editor.leftmost_position()) {
203                 xoff = editor.frame_to_pixel (min_frames) - editor.frame_to_pixel (crossfade->position ());
204         }
205
206         for (int i = 0, pci = 0; i < npoints; ++i) {
207                 Art::Point &p = (*points)[pci++];
208                 p.set_x (xoff + i + 1);
209
210                 double const ho = crossfade->in()->layer() > crossfade->out()->layer() ? _child_height : _height;
211                 p.set_y (ho - ((_child_height - 2) * vec[i]));
212         }
213
214         fade_in->property_points() = *points;
215
216         crossfade->fade_out().curve().get_vector (min_frames - crossfade->position(), max_frames - crossfade->position(), vec, npoints);
217
218         for (int i = 0, pci = 0; i < npoints; ++i) {
219                 Art::Point &p = (*points)[pci++];
220                 p.set_x (xoff + i + 1);
221                 
222                 double const ho = crossfade->in()->layer() < crossfade->out()->layer() ? _child_height : _height;
223                 p.set_y (ho - ((_child_height - 2) * vec[i]));
224         }
225
226         fade_out->property_points() = *points;
227
228         delete [] vec;
229
230         delete points;
231
232         /* XXX this is ugly, but it will have to wait till Crossfades are reimplented
233            as regions. This puts crossfade views on top of a track, above all regions.
234         */
235
236         group->raise_to_top();
237 }
238
239 void
240 CrossfadeView::active_changed ()
241 {
242         if (crossfade->active()) {
243                 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_ActiveCrossfade.get();
244         } else {
245                 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_InactiveCrossfade.get();
246         }
247
248         redraw_curves ();
249 }
250
251 void
252 CrossfadeView::color_handler ()
253 {
254         active_changed ();
255 }
256
257 void
258 CrossfadeView::set_valid (bool yn)
259 {
260         _valid = yn;
261 }
262
263 void
264 CrossfadeView::show ()
265 {
266         _visible = true;
267         group->show();
268         redraw_curves ();
269 }
270
271 void
272 CrossfadeView::hide ()
273 {
274         group->hide();
275         _visible = false;
276 }
277
278 void
279 CrossfadeView::fake_hide ()
280 {
281         group->hide();
282 }
283
284 void
285 CrossfadeView::crossfade_fades_changed ()
286 {
287         redraw_curves ();
288 }
289
290 void
291 CrossfadeView::horizontal_position_changed ()
292 {
293         /* If the crossfade curves are entirely within the editor's visible space, there is
294            no need to redraw them here as they will be completely drawn (as distinct from
295            the other case where the horizontal position change will uncover `undrawn'
296            sections).
297         */
298
299         if (!_all_in_view) {
300                 redraw_curves ();
301         }
302 }