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