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