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