fix lack of canvas.h
[ardour.git] / gtk2_ardour / regionview.cc
1 /*
2     Copyright (C) 2001 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     $Id$
19 */
20
21 #include <cmath>
22 #include <algorithm>
23
24 #include <gtkmm.h>
25
26 #include <gtkmm2ext/gtk_ui.h>
27
28 #include <ardour/playlist.h>
29 #include <ardour/audioregion.h>
30 #include <ardour/sndfilesource.h>
31 #include <ardour/diskstream.h>
32
33 #include "streamview.h"
34 #include "regionview.h"
35 #include "audio_time_axis.h"
36 #include "canvas-simplerect.h"
37 #include "canvas-simpleline.h"
38 #include "canvas-waveview.h"
39 #include "public_editor.h"
40 #include "region_editor.h"
41 #include "region_gain_line.h"
42 #include "ghostregion.h"
43 #include "audio_time_axis.h"
44 #include "utils.h"
45 #include "rgb_macros.h"
46 #include "gui_thread.h"
47
48 #include "i18n.h"
49
50 using namespace sigc;
51 using namespace ARDOUR;
52 using namespace Editing;
53
54 static const int32_t sync_mark_width = 9;
55
56 sigc::signal<void,AudioRegionView*> AudioRegionView::AudioRegionViewGoingAway;
57
58 AudioRegionView::AudioRegionView (Gnome::Canvas::Group *parent, AudioTimeAxisView &tv, 
59                                   AudioRegion& r, 
60                                   double spu, 
61                                   double amplitude_above_axis,
62                                   Gdk::Color& basic_color,
63                                   bool wfw)
64
65         : TimeAxisViewItem (r.name(), parent, tv, spu, basic_color, r.position(), r.length(),
66                             TimeAxisViewItem::Visibility (TimeAxisViewItem::ShowNameText|
67                                                           TimeAxisViewItem::ShowNameHighlight|
68                                                           TimeAxisViewItem::ShowFrame)),
69
70           region (r)
71 {
72         Gnome::Canvas::Points *shape;
73         XMLNode *node;
74
75         editor = 0;
76         valid = true;
77         in_destructor = false;
78         _amplitude_above_axis = amplitude_above_axis;
79         zero_line = 0;
80         wait_for_waves = wfw;
81         _height = 0;
82
83         _flags = 0;
84
85         if ((node = region.extra_xml ("GUI")) != 0) {
86                 set_flags (node);
87         } else {
88                 _flags = WaveformVisible;
89                 store_flags ();
90         }
91
92         if (trackview.editor.new_regionviews_display_gain()) {
93                 _flags |= EnvelopeVisible;
94         }
95
96         compute_colors (basic_color);
97
98         create_waves ();
99
100         gtk_object_set_data (GTK_OBJECT(name_highlight), "regionview", this);
101         gtk_object_set_data (GTK_OBJECT(name_text), "regionview", this);
102
103         shape = new Gnome::Canvas::Points ();
104
105         /* an equilateral triangle */
106
107         shape->push_back (Gnome::Art::Point (-((sync_mark_width-1)/2), 1));
108         shape->push_back (Gnome::Art::Point ((sync_mark_width - 1)/2, 1));
109         shape->push_back (Gnome::Art::Point (0, sync_mark_width - 1));
110         shape->push_back (Gnome::Art::Point (-((sync_mark_width-1)/2), 1));
111
112         sync_mark =  new Gnome::Canvas::Polygon (*group);
113         sync_mark->set_property ("points", shape);
114         sync_mark->set_property ("fill_color_rgba", fill_color);
115         sync_mark->hide();
116         gnome_canvas_points_unref (shape->gobj());
117
118         fade_in_shape = new Gnome::Canvas::Polygon (*group);
119         fade_in_shape->set_property ("fill_color_rgba", fade_color);
120         fade_in_shape->set_data ("regionview", this);
121         
122         fade_out_shape = new Gnome::Canvas::Polygon (*group);
123         fade_out_shape->set_property ("fill_color_rgba", fade_color);
124         fade_out_shape->set_data ("regionview", this);
125         
126
127
128         {
129                         uint32_t r,g,b,a;
130                         UINT_TO_RGBA(fill_color,&r,&g,&b,&a);
131         
132
133         fade_in_handle = new Gnome::Canvas::SimpleRect (*group);
134         fade_in_handle->set_property ("fill_color_rgba", RGBA_TO_UINT(r,g,b,0));
135         fade_in_handle->set_property ("outline_pixels", 0);
136         fade_in_handle->set_property ("y1", 2.0);
137         fade_in_handle->set_property ("y2", 7.0);
138
139         fade_in_handle->set_data ("regionview", this);
140         
141         fade_out_handle = new Gnome::Canvas::SimpleRect (*group);
142         fade_out_handle->set_property ("fill_color_rgba", RGBA_TO_UINT(r,g,b,0));
143         fade_out_handle->set_property ("outline_pixels", 0);
144         fade_out_handle->set_property ("y1", 2.0);
145         fade_out_handle->set_property ("y2", 7.0);
146
147         gtk_object_set_data (GTK_OBJECT(fade_out_handle), "regionview", this);
148         }
149
150         string foo = region.name();
151         foo += ':';
152         foo += "gain";
153
154         gain_line = new AudioRegionGainLine (foo, tv.session(), *this, &group, region.envelope(),
155                                              PublicEditor::canvas_control_point_event,
156                                              PublicEditor::canvas_line_event);
157
158         if (!(_flags & EnvelopeVisible)) {
159                 gain_line->hide ();
160         } else {
161                 gain_line->show ();
162         }
163
164         reset_width_dependent_items ((double) region.length() / samples_per_unit);
165
166         gain_line->reset ();
167
168         set_height (trackview.height);
169
170         region_muted ();
171         region_sync_changed ();
172         region_resized (BoundsChanged);
173         set_waveview_data_src();
174         region_locked ();
175         envelope_active_changed ();
176         fade_in_active_changed ();
177         fade_out_active_changed ();
178
179         region.StateChanged.connect (mem_fun(*this, &AudioRegionView::region_changed));
180
181         gtk_signal_connect (GTK_OBJECT(group), "event",
182                             GTK_SIGNAL_FUNC (PublicEditor::canvas_region_view_event),
183                             this);
184         gtk_signal_connect (GTK_OBJECT(name_highlight), "event",
185                             GTK_SIGNAL_FUNC (PublicEditor::canvas_region_view_name_highlight_event),
186                             this);
187
188         gtk_signal_connect (GTK_OBJECT(name_text), "event",
189                             GTK_SIGNAL_FUNC (PublicEditor::canvas_region_view_name_event),
190                             this);
191
192         gtk_signal_connect (GTK_OBJECT(fade_in_shape), "event",
193                             GTK_SIGNAL_FUNC (PublicEditor::canvas_fade_in_event),
194                             this);
195
196         gtk_signal_connect (GTK_OBJECT(fade_in_handle), "event",
197                             GTK_SIGNAL_FUNC (PublicEditor::canvas_fade_in_handle_event),
198                             this);
199
200         gtk_signal_connect (GTK_OBJECT(fade_out_shape), "event",
201                             GTK_SIGNAL_FUNC ( PublicEditor::canvas_fade_out_event),
202                             this);
203
204         gtk_signal_connect_object (GTK_OBJECT(fade_out_handle), "event",
205                             GTK_SIGNAL_FUNC ( PublicEditor::canvas_fade_out_handle_event),
206                             this);
207
208         set_colors ();
209
210         /* XXX sync mark drag? */
211
212 }
213
214 AudioRegionView::~AudioRegionView ()
215 {
216         in_destructor = true;
217
218         AudioRegionViewGoingAway (this); /* EMIT_SIGNAL */
219
220         for (vector<GnomeCanvasWaveViewCache *>::iterator cache = wave_caches.begin(); cache != wave_caches.end() ; ++cache) {
221                 gnome_canvas_waveview_cache_destroy (*cache);
222         }
223
224         /* all waveviews will be destroyed when the group is destroyed */
225
226         for (vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
227                 delete *g;
228         }
229
230         if (editor) {
231                 delete editor;
232         }
233
234         delete gain_line;
235 }
236
237 gint
238 AudioRegionView::_lock_toggle (Gnome::Canvas::Item* item, GdkEvent* ev, void* arg)
239 {
240         switch (ev->type) {
241         case GDK_BUTTON_RELEASE:
242                 static_cast<AudioRegionView*>(arg)->lock_toggle ();
243                 return TRUE;
244                 break;
245         default:
246                 break;
247         } 
248         return FALSE;
249 }
250
251 void
252 AudioRegionView::lock_toggle ()
253 {
254         region.set_locked (!region.locked());
255 }
256
257 void
258 AudioRegionView::region_changed (Change what_changed)
259 {
260         ENSURE_GUI_THREAD (bind (mem_fun(*this, &AudioRegionView::region_changed), what_changed));
261
262         if (what_changed & BoundsChanged) {
263                 region_resized (what_changed);
264                 region_sync_changed ();
265         }
266         if (what_changed & Region::MuteChanged) {
267                 region_muted ();
268         }
269         if (what_changed & Region::OpacityChanged) {
270                 region_opacity ();
271         }
272         if (what_changed & ARDOUR::NameChanged) {
273                 region_renamed ();
274         }
275         if (what_changed & Region::SyncOffsetChanged) {
276                 region_sync_changed ();
277         }
278         if (what_changed & Region::LayerChanged) {
279                 region_layered ();
280         }
281         if (what_changed & Region::LockChanged) {
282                 region_locked ();
283         }
284         if (what_changed & AudioRegion::ScaleAmplitudeChanged) {
285                 region_scale_amplitude_changed ();
286         }
287         if (what_changed & AudioRegion::FadeInChanged) {
288                 fade_in_changed ();
289         }
290         if (what_changed & AudioRegion::FadeOutChanged) {
291                 fade_out_changed ();
292         }
293         if (what_changed & AudioRegion::FadeInActiveChanged) {
294                 fade_in_active_changed ();
295         }
296         if (what_changed & AudioRegion::FadeOutActiveChanged) {
297                 fade_out_active_changed ();
298         }
299         if (what_changed & AudioRegion::EnvelopeActiveChanged) {
300                 envelope_active_changed ();
301         }
302 }
303
304 void
305 AudioRegionView::fade_in_changed ()
306 {
307         reset_fade_in_shape ();
308 }
309
310 void
311 AudioRegionView::fade_out_changed ()
312 {
313         reset_fade_out_shape ();
314 }
315
316 void
317 AudioRegionView::set_fade_in_active (bool yn)
318 {
319         region.set_fade_in_active (yn);
320 }
321
322 void
323 AudioRegionView::set_fade_out_active (bool yn)
324 {
325         region.set_fade_out_active (yn);
326 }
327
328 void
329 AudioRegionView::fade_in_active_changed ()
330 {
331         uint32_t r,g,b,a;
332         uint32_t col;
333         UINT_TO_RGBA(fade_color,&r,&g,&b,&a);
334
335         if (region.fade_in_active()) {
336                 col = RGBA_TO_UINT(r,g,b,120);
337                 fade_in_shape->set_property ("fill_color_rgba", col);
338                 fade_in_shape->set_property ("width_pixels", 0);
339                 fade_in_shape->set_property ("outline_color_rgba", RGBA_TO_UINT(r,g,b,0));
340         } else { 
341                 col = RGBA_TO_UINT(r,g,b,0);
342                 fade_in_shape->set_property ("fill_color_rgba", col);
343                 fade_in_shape->set_property ("width_pixels", 1);
344                 fade_in_shape->set_property ("outline_color_rgba", RGBA_TO_UINT(r,g,b,255));
345         }
346 }
347
348 void
349 AudioRegionView::fade_out_active_changed ()
350 {
351         uint32_t r,g,b,a;
352         uint32_t col;
353         UINT_TO_RGBA(fade_color,&r,&g,&b,&a);
354
355         if (region.fade_out_active()) {
356                 col = RGBA_TO_UINT(r,g,b,120);
357                 fade_out_shape->set_property ("fill_color_rgba", col);
358                 fade_out_shape->set_property ("width_pixels", 0);
359                 fade_out_shape->set_property ("outline_color_rgba", RGBA_TO_UINT(r,g,b,0));
360         } else { 
361                 col = RGBA_TO_UINT(r,g,b,0);
362                 fade_out_shape->set_property ("fill_color_rgba", col);
363                 fade_out_shape->set_property ("width_pixels", 1);
364                 fade_out_shape->set_property ("outline_color_rgba", RGBA_TO_UINT(r,g,b,255));
365         }
366 }
367
368
369 void
370 AudioRegionView::region_scale_amplitude_changed ()
371 {
372         ENSURE_GUI_THREAD (mem_fun(*this, &AudioRegionView::region_scale_amplitude_changed));
373
374         for (uint32_t n = 0; n < waves.size(); ++n) {
375                 // force a reload of the cache
376                 gnome_canvas_item_set (waves[n], "data_src", &region, NULL);
377         }
378 }
379
380 void
381 AudioRegionView::region_locked ()
382 {
383         /* name will show locked status */
384         region_renamed ();
385 }
386
387 void
388 AudioRegionView::region_resized (Change what_changed)
389 {
390         double unit_length;
391
392         if (what_changed & ARDOUR::PositionChanged) {
393                 set_position (region.position(), 0);
394         }
395
396         if (what_changed & Change (StartChanged|LengthChanged)) {
397
398                 set_duration (region.length(), 0);
399
400                 unit_length = region.length() / samples_per_unit;
401                 
402                 reset_width_dependent_items (unit_length);
403                 
404                 for (uint32_t n = 0; n < waves.size(); ++n) {
405                         gnome_canvas_item_set (waves[n], "region_start", (guint32) region.start(), NULL);
406                 }
407                 
408                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
409
410                         (*i)->set_duration (unit_length);
411
412                         for (vector<GnomeCanvasItem*>::iterator w = (*i)->waves.begin(); w != (*i)->waves.end(); ++w) {
413                                 gnome_canvas_item_set ((*w), "region_start", region.start(), NULL);
414                         }
415                 }
416         }
417 }
418
419 void
420 AudioRegionView::reset_width_dependent_items (double pixel_width)
421 {
422         TimeAxisViewItem::reset_width_dependent_items (pixel_width);
423         _pixel_width = pixel_width;
424
425         if (zero_line) {
426                 zero_line->set_property ("x2", pixel_width - 1.0);
427         }
428
429         if (pixel_width <= 6.0) {
430                 fade_in_handle->hide();
431                 fade_out_handle->hide();
432         } else {
433                 if (_height < 5.0) {
434                         fade_in_handle->hide();
435                         fade_out_handle->hide();
436                 } else {
437                         fade_in_handle->show();
438                         fade_out_handle->show();
439                 }
440         }
441
442         reset_fade_shapes ();
443 }
444
445 void
446 AudioRegionView::region_layered ()
447 {
448         AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (&get_time_axis_view());
449         atv->view->region_layered (this);
450 }
451         
452 void
453 AudioRegionView::region_muted ()
454 {
455         set_frame_color ();
456         region_renamed ();
457
458         for (uint32_t n=0; n < waves.size(); ++n) {
459                 if (region.muted()) {
460                         gnome_canvas_item_set (waves[n], "wave_color", color_map[cMutedWaveForm], NULL);
461                 } else {
462                         gnome_canvas_item_set (waves[n], "wave_color", color_map[cWaveForm], NULL);
463                 }
464         }
465 }
466
467 void
468 AudioRegionView::region_opacity ()
469 {
470         set_frame_color ();
471 }
472
473 void
474 AudioRegionView::raise ()
475 {
476         region.raise ();
477 }
478
479 void
480 AudioRegionView::raise_to_top ()
481 {
482         region.raise_to_top ();
483 }
484
485 void
486 AudioRegionView::lower ()
487 {
488         region.lower ();
489 }
490
491 void
492 AudioRegionView::lower_to_bottom ()
493 {
494         region.lower_to_bottom ();
495 }
496
497 bool
498 AudioRegionView::set_position (jack_nframes_t pos, void* src, double* ignored)
499 {
500         double delta;
501         bool ret;
502
503         if (!(ret = TimeAxisViewItem::set_position (pos, this, &delta))) {
504                 return false;
505         }
506
507         if (ignored) {
508                 *ignored = delta;
509         }
510
511         if (delta) {
512                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
513                         (*i)->group->move (delta, 0.0);
514                 }
515         }
516
517         return ret;
518 }
519
520 void
521 AudioRegionView::set_height (gdouble height)
522 {
523         uint32_t wcnt = waves.size();
524
525         TimeAxisViewItem::set_height (height - 2);
526         
527         _height = height;
528
529         for (uint32_t n=0; n < wcnt; ++n) {
530                 gdouble ht;
531
532                 if ((height) < NAME_HIGHLIGHT_THRESH) {
533                         ht = ((height-2*wcnt) / (double) wcnt);
534                 } else {
535                         ht = (((height-2*wcnt) - NAME_HIGHLIGHT_SIZE) / (double) wcnt);
536                 }
537                 
538                 gdouble yoff = n * (ht+1);
539                 
540                 gnome_canvas_item_set (waves[n], "height", ht, NULL);
541                 gnome_canvas_item_set (waves[n], "y", yoff + 2, NULL);
542         }
543
544         if ((height/wcnt) < NAME_HIGHLIGHT_SIZE) {
545                 gain_line->hide ();
546         } else {
547                 if (_flags & EnvelopeVisible) {
548                         gain_line->show ();
549                 }
550         }
551
552         manage_zero_line ();
553         gain_line->set_height ((uint32_t) rint (height - NAME_HIGHLIGHT_SIZE));
554         reset_fade_shapes ();
555
556         name_text->raise_to_top();
557 }
558
559 void
560 AudioRegionView::manage_zero_line ()
561 {
562         if (!zero_line) {
563                 return;
564         }
565
566         if (_height >= 100) {
567                 gdouble wave_midpoint = (_height - NAME_HIGHLIGHT_SIZE) / 2.0;
568                 zero_line->set_property ("y1", wave_midpoint);
569                 zero_line->set_property ("y2", wave_midpoint);
570                 zero_line->show();
571         } else {
572                 zero_line->hide();
573         }
574 }
575
576 void
577 AudioRegionView::reset_fade_shapes ()
578 {
579         reset_fade_in_shape ();
580         reset_fade_out_shape ();
581 }
582
583 void
584 AudioRegionView::reset_fade_in_shape ()
585 {
586         reset_fade_in_shape_width ((jack_nframes_t) region.fade_in().back()->when);
587 }
588         
589 void
590 AudioRegionView::reset_fade_in_shape_width (jack_nframes_t width)
591 {
592         /* smallest size for a fade is 64 frames */
593
594         width = std::max ((jack_nframes_t) 64, width);
595
596         GnomeCanvasPoints* points;
597         double pwidth = width / samples_per_unit;
598         uint32_t npoints = std::min (gdk_screen_width(), (int) pwidth);
599         double h; 
600         
601         if (_height < 5) {
602                 fade_in_shape->hide();
603                 fade_in_handle->hide();
604                 return;
605         }
606
607         double handle_center;
608         handle_center = pwidth;
609         
610         if (handle_center > 7.0) {
611                 handle_center -= 3.0;
612         } else {
613                 handle_center = 3.0;
614         }
615         
616         fade_in_handle->set_property ("x1",  handle_center - 3.0);
617         fade_in_handle->set_property ("x2",  handle_center + 3.0);
618         
619         if (pwidth < 5) {
620                 fade_in_shape->hide();
621                 return;
622         }
623
624         fade_in_shape->show();
625
626         float curve[npoints];
627         region.fade_in().get_vector (0, region.fade_in().back()->when, curve, npoints);
628
629         points = get_canvas_points ("fade in shape", npoints+3);
630
631         if (_height > NAME_HIGHLIGHT_THRESH) {
632                 h = _height - NAME_HIGHLIGHT_SIZE;
633         } else {
634                 h = _height;
635         }
636
637         /* points *MUST* be in anti-clockwise order */
638
639         uint32_t pi, pc;
640         double xdelta = pwidth/npoints;
641
642         for (pi = 0, pc = 0; pc < npoints; ++pc) {
643                 points->coords[pi++] = 1 + (pc * xdelta);
644                 points->coords[pi++] = 2 + (h - (curve[pc] * h));
645         }
646         
647         /* fold back */
648
649         points->coords[pi++] = pwidth;
650         points->coords[pi++] = 2;
651
652         points->coords[pi++] = 1;
653         points->coords[pi++] = 2;
654
655         /* connect the dots ... */
656
657         points->coords[pi++] = points->coords[0];
658         points->coords[pi] = points->coords[1];
659         
660         fade_in_shape->set_property ("points", points);
661         gnome_canvas_points_unref (points);
662 }
663
664 void
665 AudioRegionView::reset_fade_out_shape ()
666 {
667         reset_fade_out_shape_width ((jack_nframes_t) region.fade_out().back()->when);
668 }
669
670 void
671 AudioRegionView::reset_fade_out_shape_width (jack_nframes_t width)
672 {       
673         /* smallest size for a fade is 64 frames */
674
675         width = std::max ((jack_nframes_t) 64, width);
676
677         GnomeCanvasPoints* points;
678         double pwidth = width / samples_per_unit;
679         uint32_t npoints = std::min (gdk_screen_width(), (int) pwidth);
680         double h;
681
682         if (_height < 5) {
683                 fade_out_shape->hide();
684                 fade_out_handle->hide();
685                 return;
686         }
687
688         double handle_center;
689         handle_center = (region.length() - width) / samples_per_unit;
690         
691         if (handle_center > 7.0) {
692                 handle_center -= 3.0;
693         } else {
694                 handle_center = 3.0;
695         }
696         
697         fade_out_handle->set_property ("x1",  handle_center - 3.0);
698         fade_out_handle->set_property ("x2",  handle_center + 3.0);
699
700         /* don't show shape if its too small */
701         
702         if (pwidth < 5) {
703                 fade_out_shape->hide();
704                 return;
705         } 
706         
707         fade_out_shape->show();
708
709         float curve[npoints];
710         region.fade_out().get_vector (0, region.fade_out().back()->when, curve, npoints);
711
712         if (_height > NAME_HIGHLIGHT_THRESH) {
713                 h = _height - NAME_HIGHLIGHT_SIZE;
714         } else {
715                 h = _height;
716         }
717
718         /* points *MUST* be in anti-clockwise order */
719
720         points = get_canvas_points ("fade out shape", npoints+3);
721
722         uint32_t pi, pc;
723         double xdelta = pwidth/npoints;
724
725         for (pi = 0, pc = 0; pc < npoints; ++pc) {
726                 points->coords[pi++] = _pixel_width - 1 - pwidth + (pc*xdelta);
727                 points->coords[pi++] = 2 + (h - (curve[pc] * h));
728         }
729         
730         /* fold back */
731
732         points->coords[pi++] = _pixel_width;
733         points->coords[pi++] = h;
734
735         points->coords[pi++] = _pixel_width;
736         points->coords[pi++] = 2;
737
738         /* connect the dots ... */
739
740         points->coords[pi++] = points->coords[0];
741         points->coords[pi] = points->coords[1];
742
743         fade_out_shape->set_property ("points", points);
744         gnome_canvas_points_unref (points);
745 }
746
747 void
748 AudioRegionView::set_samples_per_unit (gdouble spu)
749 {
750         TimeAxisViewItem::set_samples_per_unit (spu);
751
752         for (uint32_t n=0; n < waves.size(); ++n) {
753                 gnome_canvas_item_set (waves[n], "samples_per_unit", spu, NULL);
754         }
755
756         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
757                 (*i)->set_samples_per_unit (spu);
758                 (*i)->set_duration (region.length() / samples_per_unit);
759         }
760
761         gain_line->reset ();
762         reset_fade_shapes ();
763         region_sync_changed ();
764 }
765
766 bool
767 AudioRegionView::set_duration (jack_nframes_t frames, void *src)
768 {
769         if (!TimeAxisViewItem::set_duration (frames, src)) {
770                 return false;
771         }
772         
773         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
774                 (*i)->set_duration (region.length() / samples_per_unit);
775         }
776
777         return true;
778 }
779
780 void
781 AudioRegionView::set_amplitude_above_axis (gdouble spp)
782 {
783         for (uint32_t n=0; n < waves.size(); ++n) {
784                 gnome_canvas_item_set (waves[n], "amplitude_above_axis", spp, NULL);
785         }
786 }
787
788 void
789 AudioRegionView::compute_colors (Gdk::Color& basic_color)
790 {
791         TimeAxisViewItem::compute_colors (basic_color);
792         uint32_t r, g, b, a;
793
794         /* gain color computed in envelope_active_changed() */
795
796         UINT_TO_RGBA (fill_color, &r, &g, &b, &a);
797         fade_color = RGBA_TO_UINT(r,g,b,120);
798 }
799
800 void
801 AudioRegionView::set_colors ()
802 {
803         TimeAxisViewItem::set_colors ();
804         
805         gain_line->set_line_color (region.envelope_active() ? color_map[cGainLine] : color_map[cGainLineInactive]);
806         sync_mark->set_property ("fill_color_rgba", fill_color);
807
808         for (uint32_t n=0; n < waves.size(); ++n) {
809                 if (region.muted()) {
810                         gnome_canvas_item_set (waves[n], "wave_color", color_map[cMutedWaveForm], NULL);
811                 } else {
812                         gnome_canvas_item_set (waves[n], "wave_color", color_map[cWaveForm], NULL);
813                 }
814         }
815 }
816
817 void
818 AudioRegionView::set_frame_color ()
819 {
820         if (region.opaque()) {
821                 fill_opacity = 180;
822         } else {
823                 fill_opacity = 100;
824         }
825
826         TimeAxisViewItem::set_frame_color ();
827 }
828
829 void
830 AudioRegionView::show_region_editor ()
831 {
832         if (editor == 0) {
833                 editor = new AudioRegionEditor (trackview.session(), region, *this);
834                 // GTK2FIX : how to ensure float without realizing
835                 // editor->realize ();
836                 // trackview.editor.ensure_float (*editor);
837         } 
838
839         editor->show_all ();
840         editor->get_window()->raise();
841 }
842
843 void
844 AudioRegionView::hide_region_editor()
845 {
846         if (editor) {
847                 editor->hide_all ();
848         }
849 }
850
851 void
852 AudioRegionView::region_renamed ()
853 {
854         string str;
855
856         if (region.locked()) {
857                 str += '>';
858                 str += region.name();
859                 str += '<';
860         } else {
861                 str = region.name();
862         }
863
864         if (region.muted()) {
865                 str = string ("!") + str;
866         }
867
868         set_item_name (region.name(), this);
869         set_name_text (str);
870 }
871
872 void
873 AudioRegionView::region_sync_changed ()
874 {
875         int sync_dir;
876         jack_nframes_t sync_offset;
877
878         sync_offset = region.sync_offset (sync_dir);
879
880         /* this has to handle both a genuine change of position, a change of samples_per_unit,
881            and a change in the bounds of the region.
882          */
883
884         if (sync_offset == 0) {
885
886                 /* no sync mark - its the start of the region */
887
888                 sync_mark->hide();
889
890         } else {
891
892                 if ((sync_dir < 0) || ((sync_dir > 0) && (sync_offset > region.length()))) { 
893
894                         /* no sync mark - its out of the bounds of the region */
895
896                         sync_mark->hide();
897
898                 } else {
899
900                         /* lets do it */
901
902                         GtkArg args[1];
903                         GnomeCanvasPoints* points;
904                         
905                         args[0].name = X_("points");
906                         
907                         sync_mark->get (X_("points"), &points);
908                         
909                         double offset = sync_offset / samples_per_unit;
910                         
911                         points->coords[0] = offset - ((sync_mark_width-1)/2);
912                         points->coords[1] = 1;
913                         
914                         points->coords[2] = offset + (sync_mark_width-1)/2;
915                         points->coords[3] = 1;
916                         
917                         points->coords[4] = offset;
918                         points->coords[5] = sync_mark_width - 1;
919                         
920                         points->coords[6] = offset - ((sync_mark_width-1)/2);
921                         points->coords[7] = 1;
922                         
923                         sync_mark->show();
924                         sync_mark->set_property ("points", points);
925
926                         gnome_canvas_points_unref (points);
927                 }
928         }
929 }
930
931 void
932 AudioRegionView::set_waveform_visible (bool yn)
933 {
934         if (((_flags & WaveformVisible) != yn)) {
935                 if (yn) {
936                         for (uint32_t n=0; n < waves.size(); ++n) {
937                                 gnome_canvas_item_show (waves[n]);
938                         }
939                         _flags |= WaveformVisible;
940                 } else {
941                         for (uint32_t n=0; n < waves.size(); ++n) {
942                                 gnome_canvas_item_hide (waves[n]);
943                         }
944                         _flags &= ~WaveformVisible;
945                 }
946                 store_flags ();
947         }
948 }
949
950 void
951 AudioRegionView::temporarily_hide_envelope ()
952 {
953         gain_line->hide ();
954 }
955
956 void
957 AudioRegionView::unhide_envelope ()
958 {
959         if (_flags & EnvelopeVisible) {
960                 gain_line->show ();
961         }
962 }
963
964 void
965 AudioRegionView::set_envelope_visible (bool yn)
966 {
967         if ((_flags & EnvelopeVisible) != yn) {
968                 if (yn) {
969                         gain_line->show ();
970                         _flags |= EnvelopeVisible;
971                 } else {
972                         gain_line->hide ();
973                         _flags &= ~EnvelopeVisible;
974                 }
975                 store_flags ();
976         }
977 }
978
979 void
980 AudioRegionView::create_waves ()
981 {
982         bool create_zero_line = true;
983
984         AudioTimeAxisView& atv (*(dynamic_cast<AudioTimeAxisView*>(&trackview))); // ick
985
986         if (!atv.get_diskstream()) {
987                 return;
988         }
989
990         uint32_t nchans = atv.get_diskstream()->n_channels();
991         
992 //      if (wait_for_waves) {
993                 /* in tmp_waves, set up null pointers for each channel so the vector is allocated */
994                 for (uint32_t n = 0; n < nchans; ++n) {
995                         tmp_waves.push_back (0);
996                 }
997 //      }
998         
999         for (uint32_t n = 0; n < nchans; ++n) {
1000                 
1001                 if (n >= region.n_channels()) {
1002                         break;
1003                 }
1004                 
1005                 wave_caches.push_back (gnome_canvas_waveview_cache_new ());
1006
1007                 if (wait_for_waves) {
1008                         if (region.source(n).peaks_ready (bind (mem_fun(*this, &AudioRegionView::peaks_ready_handler), n))) {
1009                                 create_one_wave (n, true);
1010                         } else {
1011                                 create_zero_line = false;
1012                         }
1013                 } else {
1014                         create_one_wave (n, true);
1015                 }
1016         }
1017
1018         if (create_zero_line) {
1019                 zero_line = new Gnome::Canvas::Line (*group);
1020                 zero_line->set_property ("x1", (gdouble) 1.0);
1021                 zero_line->set_property ("x2", (gdouble) (region.length() / samples_per_unit) - 1.0);
1022                 zero_line->set_property ("color_rgba", (guint) color_map[cZeroLine]);
1023                 manage_zero_line ();
1024         }
1025 }
1026
1027 void
1028 AudioRegionView::create_one_wave (uint32_t which, bool direct)
1029 {
1030         AudioTimeAxisView& atv (*(dynamic_cast<AudioTimeAxisView*>(&trackview))); // ick
1031         uint32_t nchans = atv.get_diskstream()->n_channels();
1032         uint32_t n;
1033         uint32_t nwaves = std::min (nchans, region.n_channels());
1034         
1035         gdouble ht;
1036         if (trackview.height < NAME_HIGHLIGHT_SIZE) {
1037                 ht = ((trackview.height) / (double) nchans);
1038         } else {
1039                 ht = ((trackview.height - NAME_HIGHLIGHT_SIZE) / (double) nchans);
1040         }
1041         gdouble yoff = which * ht;
1042
1043         GnomeCanvasItem *wave = gnome_canvas_item_new (GNOME_CANVAS_GROUP(group),
1044                                                    gnome_canvas_waveview_get_type (),
1045                                                    "data_src", (gpointer) &region,
1046                                                    "cache", wave_caches[which],
1047                                                    "cache_updater", (gboolean) true,
1048                                                    "channel", (guint32) which,
1049                                                    "length_function", (gpointer) region_length_from_c,
1050                                                    "sourcefile_length_function",(gpointer) sourcefile_length_from_c,
1051                                                    "peak_function", (gpointer) region_read_peaks_from_c,
1052                                                    "x", 0.0,
1053                                                    "y", yoff,
1054                                                    "height", (double) ht,
1055                                                    "samples_per_unit", samples_per_unit,
1056                                                    "amplitude_above_axis", _amplitude_above_axis,
1057                                                    "wave_color", (guint32) (region.muted() ? color_map[cMutedWaveForm] : color_map[cWaveForm]),
1058                                                    "region_start",(guint32) region.start(),
1059                                                    NULL);
1060         
1061         if (!(_flags & WaveformVisible)) {
1062                 gnome_canvas_item_hide (wave);
1063         }
1064
1065         /* note: calling this function is serialized by the lock
1066            held in the peak building thread that signals that
1067            peaks are ready for use *or* by the fact that it is
1068            called one by one from the GUI thread.
1069         */
1070
1071         if (which < nchans) {
1072                 tmp_waves[which] = (wave);
1073         } else {
1074                 /* n-channel track, >n-channel source */
1075         }
1076         
1077         /* see if we're all ready */
1078         
1079         for (n = 0; n < nchans; ++n) {
1080                 if (tmp_waves[n] == 0) {
1081                         break;
1082                 }
1083         }
1084         
1085         if (n == nwaves) {
1086                 /* all waves are ready */
1087                 tmp_waves.resize(nwaves);
1088                 waves = tmp_waves;
1089                 tmp_waves.clear ();
1090                 
1091                 if (!zero_line) {
1092                         zero_line = new Gnome::Canvas::Line (*group);
1093                         zero_line->set_property ("x1", (gdouble) 1.0);
1094                         zero_line->set_property ("x2", (gdouble) (region.length() / samples_per_unit) - 1.0);
1095                         zero_line->set_property ("color_rgba", (guint) color_map[cZeroLine]);
1096                         manage_zero_line ();
1097                 }
1098         }
1099 }
1100
1101 void
1102 AudioRegionView::peaks_ready_handler (uint32_t which)
1103 {
1104         Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun(*this, &AudioRegionView::create_one_wave), which, false));
1105 }
1106
1107 void
1108 AudioRegionView::add_gain_point_event (Gnome::Canvas::Item *item, GdkEvent *ev)
1109 {
1110         double x, y;
1111
1112         /* don't create points that can't be seen */
1113
1114         set_envelope_visible (true);
1115         
1116         x = ev->button.x;
1117         y = ev->button.y;
1118
1119         item->w2i (x, y);
1120
1121         jack_nframes_t fx = trackview.editor.pixel_to_frame (x);
1122
1123         if (fx > region.length()) {
1124                 return;
1125         }
1126
1127         /* compute vertical fractional position */
1128
1129         y = 1.0 - (y / (trackview.height - NAME_HIGHLIGHT_SIZE));
1130         
1131         /* map using gain line */
1132
1133         gain_line->view_to_model_y (y);
1134
1135         trackview.session().begin_reversible_command (_("add gain control point"));
1136         trackview.session().add_undo (region.envelope().get_memento());
1137
1138
1139         if (!region.envelope_active()) {
1140                 trackview.session().add_undo( bind( mem_fun(region, &AudioRegion::set_envelope_active), false) );
1141                 region.set_envelope_active(true);
1142                 trackview.session().add_redo( bind( mem_fun(region, &AudioRegion::set_envelope_active), true) );
1143         }
1144
1145         region.envelope().add (fx, y);
1146         
1147         trackview.session().add_redo_no_execute (region.envelope().get_memento());
1148         trackview.session().commit_reversible_command ();
1149 }
1150
1151 void
1152 AudioRegionView::remove_gain_point_event (Gnome::Canvas::Item *item, GdkEvent *ev)
1153 {
1154         ControlPoint *cp = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1155         region.envelope().erase (cp->model);
1156 }
1157
1158 void
1159 AudioRegionView::store_flags()
1160 {
1161         XMLNode *node = new XMLNode ("GUI");
1162
1163         node->add_property ("waveform-visible", (_flags & WaveformVisible) ? "yes" : "no");
1164         node->add_property ("envelope-visible", (_flags & EnvelopeVisible) ? "yes" : "no");
1165
1166         region.add_extra_xml (*node);
1167 }
1168
1169 void
1170 AudioRegionView::set_flags (XMLNode* node)
1171 {
1172         XMLProperty *prop;
1173
1174         if ((prop = node->property ("waveform-visible")) != 0) {
1175                 if (prop->value() == "yes") {
1176                         _flags |= WaveformVisible;
1177                 }
1178         }
1179
1180         if ((prop = node->property ("envelope-visible")) != 0) {
1181                 if (prop->value() == "yes") {
1182                         _flags |= EnvelopeVisible;
1183                 }
1184         }
1185 }
1186         
1187 void
1188 AudioRegionView::set_waveform_shape (WaveformShape shape)
1189 {
1190         bool yn;
1191
1192         /* this slightly odd approach is to leave the door open to 
1193            other "shapes" such as spectral displays, etc.
1194         */
1195
1196         switch (shape) {
1197         case Rectified:
1198                 yn = true;
1199                 break;
1200
1201         default:
1202                 yn = false;
1203                 break;
1204         }
1205
1206         if (yn != (bool) (_flags & WaveformRectified)) {
1207                 for (vector<GnomeCanvasItem *>::iterator wave = waves.begin(); wave != waves.end() ; ++wave) {
1208                         gnome_canvas_item_set ((*wave), "rectified", (gboolean) yn, NULL);
1209                 }
1210
1211                 if (zero_line) {
1212                         if (yn) {
1213                                 zero_line->hide();
1214                         } else {
1215                                 zero_line->show();
1216                         }
1217                 }
1218
1219                 if (yn) {
1220                         _flags |= WaveformRectified;
1221                 } else {
1222                         _flags &= ~WaveformRectified;
1223                 }
1224         }
1225 }
1226
1227 std::string
1228 AudioRegionView::get_item_name ()
1229 {
1230         return region.name();
1231 }
1232
1233 void
1234 AudioRegionView::move (double x_delta, double y_delta)
1235 {
1236         if (region.locked() || (x_delta == 0 && y_delta == 0)) {
1237                 return;
1238         }
1239
1240         get_canvas_group()->move (x_delta, y_delta);
1241
1242         /* note: ghosts never leave their tracks so y_delta for them is always zero */
1243
1244         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1245                 (*i)->group->move (x_delta, 0.0);
1246         }
1247 }
1248
1249 GhostRegion*
1250 AudioRegionView::add_ghost (AutomationTimeAxisView& atv)
1251 {
1252         AudioTimeAxisView& myatv (*(dynamic_cast<AudioTimeAxisView*>(&trackview))); // ick
1253         double unit_position = region.position () / samples_per_unit;
1254         GhostRegion* ghost = new GhostRegion (atv, unit_position);
1255         uint32_t nchans;
1256         
1257         nchans = myatv.get_diskstream()->n_channels();
1258
1259         for (uint32_t n = 0; n < nchans; ++n) {
1260                 
1261                 if (n >= region.n_channels()) {
1262                         break;
1263                 }
1264                 
1265                 GnomeCanvasItem *wave = gnome_canvas_item_new (GNOME_CANVAS_GROUP(ghost->group),
1266                                                            gnome_canvas_waveview_get_type (),
1267                                                            "data_src", (gpointer) &region,
1268                                                            "cache", wave_caches[n],
1269                                                            "cache_updater", (gboolean) false,
1270                                                            "channel", (guint32) n,
1271                                                            "length_function", (gpointer) region_length_from_c,
1272                                                            "sourcefile_length_function",(gpointer) sourcefile_length_from_c,
1273                                                            "peak_function", (gpointer) region_read_peaks_from_c,
1274                                                            "x", 0.0,
1275                                                            "samples_per_unit", samples_per_unit,
1276                                                            "amplitude_above_axis", _amplitude_above_axis,
1277                                                            "wave_color", color_map[cGhostTrackWave],
1278                                                            "region_start", (guint32) region.start(),
1279                                                            NULL);
1280
1281                 
1282                 ghost->waves.push_back(wave);
1283         }
1284
1285         ghost->set_height ();
1286         ghost->set_duration (region.length() / samples_per_unit);
1287         ghosts.push_back (ghost);
1288
1289         ghost->GoingAway.connect (mem_fun(*this, &AudioRegionView::remove_ghost));
1290
1291         return ghost;
1292 }
1293
1294 void
1295 AudioRegionView::remove_ghost (GhostRegion* ghost)
1296 {
1297         if (in_destructor) {
1298                 return;
1299         }
1300
1301         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1302                 if (*i == ghost) {
1303                         ghosts.erase (i);
1304                         break;
1305                 }
1306         }
1307 }
1308
1309 uint32_t
1310 AudioRegionView::get_fill_color ()
1311 {
1312         return fill_color;
1313 }
1314
1315 void
1316 AudioRegionView::entered ()
1317 {
1318         if (_flags & EnvelopeVisible) {
1319                 gain_line->show_all_control_points ();
1320         }
1321
1322         uint32_t r,g,b,a;
1323         UINT_TO_RGBA(fade_color,&r,&g,&b,&a);
1324         a=255;
1325         
1326         fade_in_handle->set_property ("fill_color_rgba", RGBA_TO_UINT(r,g,b,a));
1327         fade_out_handle->set_property ("fill_color_rgba", RGBA_TO_UINT(r,g,b,a));
1328 }
1329
1330 void
1331 AudioRegionView::exited ()
1332 {
1333         gain_line->hide_all_but_selected_control_points ();
1334         
1335         uint32_t r,g,b,a;
1336         UINT_TO_RGBA(fade_color,&r,&g,&b,&a);
1337         a=0;
1338         
1339         fade_in_handle->set_property ("fill_color_rgba", RGBA_TO_UINT(r,g,b,a));
1340         fade_out_handle->set_property ("fill_color_rgba", RGBA_TO_UINT(r,g,b,a));
1341 }
1342
1343 void
1344 AudioRegionView::envelope_active_changed ()
1345 {
1346         gain_line->set_line_color (region.envelope_active() ? color_map[cGainLine] : color_map[cGainLineInactive]);
1347 }
1348
1349 void
1350 AudioRegionView::set_waveview_data_src()
1351 {
1352
1353         double unit_length= region.length() / samples_per_unit;
1354
1355         for (uint32_t n = 0; n < waves.size(); ++n) {
1356                 // TODO: something else to let it know the channel
1357                 gnome_canvas_item_set (waves[n], "data_src", &region, NULL);
1358         }
1359         
1360         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1361                 
1362                 (*i)->set_duration (unit_length);
1363                 
1364                 for (vector<GnomeCanvasItem*>::iterator w = (*i)->waves.begin(); w != (*i)->waves.end(); ++w) {
1365                         gnome_canvas_item_set ((*w), "data_src", &region, NULL);
1366                 }
1367         }
1368
1369 }
1370
1371