Remove unused methods in ARDOUR_UI for starting/stopping engine
[ardour.git] / gtk2_ardour / ghostregion.cc
1 /*
2     Copyright (C) 2000-2007 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "ardour/parameter_descriptor.h"
21
22 #include "evoral/Note.hpp"
23 #include "canvas/container.h"
24 #include "canvas/polygon.h"
25 #include "canvas/rectangle.h"
26 #include "canvas/wave_view.h"
27 #include "canvas/debug.h"
28
29 #include "automation_time_axis.h"
30 #include "ghostregion.h"
31 #include "midi_streamview.h"
32 #include "midi_time_axis.h"
33 #include "rgb_macros.h"
34 #include "note.h"
35 #include "hit.h"
36 #include "ui_config.h"
37
38 using namespace std;
39 using namespace Editing;
40 using namespace ArdourCanvas;
41 using namespace ARDOUR;
42
43 PBD::Signal1<void,GhostRegion*> GhostRegion::CatchDeletion;
44
45 GhostRegion::GhostRegion (ArdourCanvas::Container* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_pos)
46         : trackview (tv)
47         , source_trackview (source_tv)
48 {
49         group = new ArdourCanvas::Container (parent);
50         CANVAS_DEBUG_NAME (group, "ghost region");
51         group->set_position (ArdourCanvas::Duple (initial_pos, 0));
52
53         base_rect = new ArdourCanvas::Rectangle (group);
54         CANVAS_DEBUG_NAME (base_rect, "ghost region rect");
55         base_rect->set_x0 (0);
56         base_rect->set_y0 (1.0);
57         base_rect->set_y1 (trackview.current_height());
58         base_rect->set_outline (false);
59
60         if (!is_automation_ghost()) {
61                 base_rect->hide();
62         }
63
64         GhostRegion::set_colors();
65
66         /* the parent group of a ghostregion is a dedicated group for ghosts,
67            so the new ghost would want to get to the top of that group*/
68         group->raise_to_top ();
69 }
70
71 GhostRegion::~GhostRegion ()
72 {
73         CatchDeletion (this);
74         delete base_rect;
75         delete group;
76 }
77
78 void
79 GhostRegion::set_duration (double units)
80 {
81         base_rect->set_x1 (units);
82 }
83
84 void
85 GhostRegion::set_height ()
86 {
87         base_rect->set_y1 (trackview.current_height());
88 }
89
90 void
91 GhostRegion::set_colors ()
92 {
93         if (is_automation_ghost()) {
94                 base_rect->set_fill_color (UIConfiguration::instance().color_mod ("ghost track base", "ghost track base"));
95         }
96 }
97
98 guint
99 GhostRegion::source_track_color(unsigned char alpha)
100 {
101         Gdk::Color color = source_trackview.color();
102         return RGBA_TO_UINT (color.get_red() / 256, color.get_green() / 256, color.get_blue() / 256, alpha);
103 }
104
105 bool
106 GhostRegion::is_automation_ghost()
107 {
108         return (dynamic_cast<AutomationTimeAxisView*>(&trackview)) != 0;
109 }
110
111 AudioGhostRegion::AudioGhostRegion(TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos)
112         : GhostRegion(tv.ghost_group(), tv, source_tv, initial_unit_pos)
113 {
114
115 }
116
117 void
118 AudioGhostRegion::set_samples_per_pixel (double fpp)
119 {
120         for (vector<WaveView*>::iterator i = waves.begin(); i != waves.end(); ++i) {
121                 (*i)->set_samples_per_pixel (fpp);
122         }
123 }
124
125 void
126 AudioGhostRegion::set_height ()
127 {
128         vector<WaveView*>::iterator i;
129         uint32_t n;
130
131         GhostRegion::set_height();
132
133         double const ht = ((trackview.current_height()) / (double) waves.size());
134
135         for (n = 0, i = waves.begin(); i != waves.end(); ++i, ++n) {
136                 (*i)->set_height (ht);
137                 (*i)->set_y_position (n * ht);
138         }
139 }
140
141 void
142 AudioGhostRegion::set_colors ()
143 {
144         GhostRegion::set_colors();
145         guint fill_color;
146
147         if (is_automation_ghost()) {
148                 fill_color = UIConfiguration::instance().color ("ghost track wave fill");
149         }
150         else {
151                 fill_color = source_track_color(200);
152         }
153
154         for (uint32_t n=0; n < waves.size(); ++n) {
155                 waves[n]->set_outline_color (UIConfiguration::instance().color ("ghost track wave"));
156                 waves[n]->set_fill_color (fill_color);
157                 waves[n]->set_clip_color (UIConfiguration::instance().color ("ghost track wave clip"));
158                 waves[n]->set_zero_color (UIConfiguration::instance().color ("ghost track zero line"));
159         }
160 }
161
162 /** The general constructor; called when the destination timeaxisview doesn't have
163  *  a midistreamview.
164  *
165  *  @param tv TimeAxisView that this ghost region is on.
166  *  @param source_tv TimeAxisView that we are the ghost for.
167  */
168 MidiGhostRegion::MidiGhostRegion(TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos)
169         : GhostRegion(tv.ghost_group(), tv, source_tv, initial_unit_pos)
170         , _optimization_iterator (events.end ())
171 {
172         base_rect->lower_to_bottom();
173         update_range ();
174
175         midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiGhostRegion::update_range));
176 }
177
178 /**
179  *  @param msv MidiStreamView that this ghost region is on.
180  *  @param source_tv TimeAxisView that we are the ghost for.
181  */
182 MidiGhostRegion::MidiGhostRegion(MidiStreamView& msv, TimeAxisView& source_tv, double initial_unit_pos)
183         : GhostRegion(msv.midi_underlay_group, msv.trackview(), source_tv, initial_unit_pos)
184         , _optimization_iterator (events.end ())
185 {
186         base_rect->lower_to_bottom();
187         update_range ();
188
189         midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiGhostRegion::update_range));
190 }
191
192 MidiGhostRegion::~MidiGhostRegion()
193 {
194         clear_events ();
195 }
196
197 MidiGhostRegion::GhostEvent::GhostEvent (NoteBase* e, ArdourCanvas::Container* g)
198         : event (e)
199 {
200         Hit* hit = NULL;
201         if (dynamic_cast<Note*>(e)) {
202                 item = new ArdourCanvas::Rectangle(
203                         g, ArdourCanvas::Rect(e->x0(), e->y0(), e->x1(), e->y1()));
204         } else if ((hit = dynamic_cast<Hit*>(e))) {
205                 ArdourCanvas::Polygon* poly = new ArdourCanvas::Polygon(g);
206                 poly->set(Hit::points(e->y1() - e->y0()));
207                 poly->set_position(hit->position());
208                 item = poly;
209         }
210
211         CANVAS_DEBUG_NAME (item, "ghost note item");
212 }
213
214 MidiGhostRegion::GhostEvent::~GhostEvent ()
215 {
216         /* event is not ours to delete */
217         delete item;
218 }
219
220 void
221 MidiGhostRegion::set_samples_per_pixel (double /*spu*/)
222 {
223 }
224
225 /** @return MidiStreamView that we are providing a ghost for */
226 MidiStreamView*
227 MidiGhostRegion::midi_view ()
228 {
229         StreamView* sv = source_trackview.view ();
230         assert (sv);
231         MidiStreamView* msv = dynamic_cast<MidiStreamView*> (sv);
232         assert (msv);
233
234         return msv;
235 }
236
237 void
238 MidiGhostRegion::set_height ()
239 {
240         GhostRegion::set_height();
241         update_range();
242 }
243
244 void
245 MidiGhostRegion::set_colors()
246 {
247         GhostRegion::set_colors();
248
249         for (EventList::iterator it = events.begin(); it != events.end(); ++it) {
250                 (*it)->item->set_fill_color (UIConfiguration::instance().color_mod((*it)->event->base_color(), "ghost track midi fill"));
251                 (*it)->item->set_outline_color (UIConfiguration::instance().color ("ghost track midi outline"));
252         }
253 }
254
255 static double
256 note_height(TimeAxisView& trackview, MidiStreamView* mv)
257 {
258         const double tv_height  = trackview.current_height();
259         const double note_range = mv->contents_note_range();
260
261         return std::max(1.0, floor(tv_height / note_range - 1.0));
262 }
263
264 static double
265 note_y(TimeAxisView& trackview, MidiStreamView* mv, uint8_t note_num)
266 {
267         const double tv_height  = trackview.current_height();
268         const double note_range = mv->contents_note_range();
269         const double s          = tv_height / note_range;
270
271         return tv_height - (note_num + 1 - mv->lowest_note()) * s;
272 }
273
274 void
275 MidiGhostRegion::update_range ()
276 {
277         MidiStreamView* mv = midi_view();
278
279         if (!mv) {
280                 return;
281         }
282
283         double const h = note_height(trackview, mv);
284
285         for (EventList::iterator it = events.begin(); it != events.end(); ++it) {
286                 uint8_t const note_num = (*it)->event->note()->note();
287
288                 if (note_num < mv->lowest_note() || note_num > mv->highest_note()) {
289                         (*it)->item->hide();
290                 } else {
291                         (*it)->item->show();
292                         double const y = note_y(trackview, mv, note_num);
293                         ArdourCanvas::Rectangle* rect = NULL;
294                         ArdourCanvas::Polygon*   poly = NULL;
295                         if ((rect = dynamic_cast<ArdourCanvas::Rectangle*>((*it)->item))) {
296                                 rect->set_y0 (y);
297                                 rect->set_y1 (y + h);
298                         } else if ((poly = dynamic_cast<ArdourCanvas::Polygon*>((*it)->item))) {
299                                 Duple position = poly->position();
300                                 position.y = y;
301                                 poly->set_position(position);
302                                 poly->set(Hit::points(h));
303                         }
304                 }
305         }
306 }
307
308 void
309 MidiGhostRegion::add_note (NoteBase* n)
310 {
311         GhostEvent* event = new GhostEvent (n, group);
312         events.push_back (event);
313
314         event->item->set_fill_color (UIConfiguration::instance().color_mod(n->base_color(), "ghost track midi fill"));
315         event->item->set_outline_color (UIConfiguration::instance().color ("ghost track midi outline"));
316
317         MidiStreamView* mv = midi_view();
318
319         if (mv) {
320                 uint8_t const note_num = n->note()->note();
321                 double const  h        = note_height(trackview, mv);
322                 double const  y        = note_y(trackview, mv, note_num);
323
324                 if (note_num < mv->lowest_note() || note_num > mv->highest_note()) {
325                         event->item->hide();
326                 } else {
327                         ArdourCanvas::Rectangle* rect = NULL;
328                         ArdourCanvas::Polygon*   poly = NULL;
329                         if ((rect = dynamic_cast<ArdourCanvas::Rectangle*>(event->item))) {
330                                 rect->set_y0 (y);
331                                 rect->set_y1 (y + h);
332                         } else if ((poly = dynamic_cast<ArdourCanvas::Polygon*>(event->item))) {
333                                 Duple position = poly->position();
334                                 position.y = y;
335                                 poly->set_position(position);
336                                 poly->set(Hit::points(h));
337                         }
338                 }
339         }
340 }
341
342 void
343 MidiGhostRegion::clear_events()
344 {
345         for (EventList::iterator it = events.begin(); it != events.end(); ++it) {
346                 delete *it;
347         }
348
349         events.clear();
350         _optimization_iterator = events.end ();
351 }
352
353 /** Update the x positions of our representation of a parent's note.
354  *  @param parent The CanvasNote from the parent MidiRegionView.
355  */
356 void
357 MidiGhostRegion::update_note (NoteBase* parent)
358 {
359         GhostEvent* ev = find_event (parent);
360         if (!ev) {
361                 return;
362         }
363
364         Note*                    note = NULL;
365         ArdourCanvas::Rectangle* rect = NULL;
366         Hit*                     hit  = NULL;
367         ArdourCanvas::Polygon*   poly = NULL;
368         if ((note = dynamic_cast<Note*>(parent))) {
369                 if ((rect = dynamic_cast<ArdourCanvas::Rectangle*>(ev->item))) {
370                         double const x1 = parent->x0 ();
371                         double const x2 = parent->x1 ();
372                         rect->set_x0 (x1);
373                         rect->set_x1 (x2);
374                 }
375         } else if ((hit = dynamic_cast<Hit*>(parent))) {
376                 if ((poly = dynamic_cast<ArdourCanvas::Polygon*>(ev->item))) {
377                         ArdourCanvas::Duple ppos = hit->position();
378                         ArdourCanvas::Duple gpos = poly->position();
379                         gpos.x = ppos.x;
380                         poly->set_position(gpos);
381                 }
382         }
383 }
384
385 void
386 MidiGhostRegion::remove_note (NoteBase* note)
387 {
388         GhostEvent* ev = find_event (note);
389         if (!ev) {
390                 return;
391         }
392
393         events.remove (ev);
394         delete ev;
395         _optimization_iterator = events.end ();
396 }
397
398 /** Given a note in our parent region (ie the actual MidiRegionView), find our
399  *  representation of it.
400  *  @return Our Event, or 0 if not found.
401  */
402
403 MidiGhostRegion::GhostEvent *
404 MidiGhostRegion::find_event (NoteBase* parent)
405 {
406         /* we are using _optimization_iterator to speed up the common case where a caller
407            is going through our notes in order.
408         */
409
410         if (_optimization_iterator != events.end()) {
411                 ++_optimization_iterator;
412         }
413
414         if (_optimization_iterator != events.end() && (*_optimization_iterator)->event == parent) {
415                 return *_optimization_iterator;
416         }
417
418         for (_optimization_iterator = events.begin(); _optimization_iterator != events.end(); ++_optimization_iterator) {
419                 if ((*_optimization_iterator)->event == parent) {
420                         return *_optimization_iterator;
421                 }
422         }
423
424         return 0;
425 }