Add back 2 patches for histogram and overlayframe that are working correctly and...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / trackcanvas.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "apatchgui.inc"
23 #include "asset.h"
24 #include "autoconf.h"
25 #include "automation.h"
26 #include "bcsignals.h"
27 #include "bctimer.h"
28 #include "clip.h"
29 #include "bccolors.h"
30 #include "cache.h"
31 #include "canvas.h"
32 #include "cplayback.h"
33 #include "cursors.h"
34 #include "cwindowgui.h"
35 #include "cwindow.h"
36 #include "edithandles.h"
37 #include "editpopup.h"
38 #include "edits.h"
39 #include "edl.h"
40 #include "edlsession.h"
41 #include "floatauto.h"
42 #include "floatautos.h"
43 #include "gwindowgui.h"
44 #include "indexstate.h"
45 #include "intauto.h"
46 #include "intautos.h"
47 #include "keyframe.h"
48 #include "keyframepopup.h"
49 #include "keyframes.h"
50 #include "keys.h"
51 #include "labels.h"
52 #include "localsession.h"
53 #include "mainclock.h"
54 #include "maincursor.h"
55 #include "mainsession.h"
56 #include "mainundo.h"
57 #include "maskautos.h"
58 #include "mbuttons.h"
59 #include "mtimebar.h"
60 #include "mwindowgui.h"
61 #include "mwindow.h"
62 #include "panautos.h"
63 #include "patchbay.h"
64 #include "playbackengine.h"
65 #include "playtransport.h"
66 #include "plugin.h"
67 #include "pluginpopup.h"
68 #include "pluginserver.h"
69 #include "pluginset.h"
70 #include "plugintoggles.h"
71 #include "preferences.h"
72 #include "renderengine.h"
73 #include "resourcepixmap.h"
74 #include "resourcethread.h"
75 #include "swindow.h"
76 #include "theme.h"
77 #include "trackcanvas.h"
78 #include "tracking.h"
79 #include "trackpopup.h"
80 #include "tracks.h"
81 #include "transition.h"
82 #include "transitionhandles.h"
83 #include "transitionpopup.h"
84 #include "transportque.h"
85 #include "vframe.h"
86 #include "vpatchgui.inc"
87 #include "vrender.h"
88 #include "zoombar.h"
89
90 #include <string.h>
91
92 //#define PIXMAP_AGE -5
93 #define PIXMAP_AGE -32
94
95 TrackCanvas::TrackCanvas(MWindow *mwindow,
96         TimelinePane *pane,
97         int x,
98         int y,
99         int w,
100         int h)
101  : BC_SubWindow(x,
102         y,
103         w,
104         h)
105 {
106         this->mwindow = mwindow;
107         this->gui = mwindow->gui;
108         this->pane = pane;
109
110         selection_midpoint = 0;
111         drag_scroll = 0;
112         active = 0;
113         temp_picon = 0;
114         resource_timer = new Timer;
115         render_timer = new Timer;
116         hourglass_enabled = 0;
117         timebar_position = -1;
118         snapped = 0;
119 }
120
121 TrackCanvas::~TrackCanvas()
122 {
123 //      delete transition_handles;
124         delete edit_handles;
125         delete keyframe_pixmap;
126         delete camerakeyframe_pixmap;
127         delete modekeyframe_pixmap;
128         delete pankeyframe_pixmap;
129         delete projectorkeyframe_pixmap;
130         delete maskkeyframe_pixmap;
131         delete background_pixmap;
132         if(temp_picon) delete temp_picon;
133         delete render_timer;
134         delete resource_timer;
135 }
136
137 void TrackCanvas::create_objects()
138 {
139         background_pixmap = new BC_Pixmap(this, get_w(), get_h());
140 //      transition_handles = new TransitionHandles(mwindow, this);
141         edit_handles = new EditHandles(mwindow, this);
142         keyframe_pixmap = new BC_Pixmap(this, mwindow->theme->keyframe_data, PIXMAP_ALPHA);
143         camerakeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->camerakeyframe_data, PIXMAP_ALPHA);
144         modekeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->modekeyframe_data, PIXMAP_ALPHA);
145         pankeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->pankeyframe_data, PIXMAP_ALPHA);
146         projectorkeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->projectorkeyframe_data, PIXMAP_ALPHA);
147         maskkeyframe_pixmap = new BC_Pixmap(this, mwindow->theme->maskkeyframe_data, PIXMAP_ALPHA);
148         draw(NORMAL_DRAW, 1);
149         update_cursor(0);
150         flash(0);
151 }
152
153 void TrackCanvas::resize_event()
154 {
155 //printf("TrackCanvas::resize_event 1\n");
156         draw(NORMAL_DRAW, 0);
157         flash(0);
158 //printf("TrackCanvas::resize_event 2\n");
159 }
160
161 // *** CONTEXT_HELP ***
162 // This complicated implementation (up to *** END_CONTEXT_HELP ***)
163 // serves solely for context dependent help
164 int TrackCanvas::keypress_event()
165 {
166         int cursor_x, cursor_y;
167
168 //      printf("TrackCanvas::keypress_event: %d\n", get_keypress());
169         if (get_keypress() != 'h' || ! alt_down())         return 0;
170         if (! is_tooltip_event_win() || ! cursor_inside()) return 0;
171
172         cursor_x = get_cursor_x();
173         cursor_y = get_cursor_y();
174
175 // Provide different help depending on the kind of object under the cursor:
176 // transition border handles
177 // transition icons themselves
178 // autos (keyframes or lines) and plugin keyframes
179 // asset border handles
180 // plugin border handles
181 // plugin bars themselves
182         if (help_transition_handles(cursor_x, cursor_y)) return 1;
183         if (help_transitions(cursor_x, cursor_y))        return 1;
184         if (help_keyframes(cursor_x, cursor_y))          return 1;
185         if (help_edit_handles(cursor_x, cursor_y))       return 1;
186         if (help_plugin_handles(cursor_x, cursor_y))     return 1;
187         if (help_plugins(cursor_x, cursor_y))            return 1;
188
189 // Show "Editing" chapter as a fallback when cursor was over anything else
190         context_help_show("Editing");
191         return 1;
192 }
193
194 int TrackCanvas::help_transitions(int cursor_x, int cursor_y)
195 {
196         int done = 0;
197         int64_t x, y, w, h;
198         Transition *transition = 0;
199
200         // Detect, if any, the transition under cursor
201         for( Track *track = mwindow->edl->tracks->first; track && !done; track = track->next ) {
202                 if( track->is_hidden() ) continue;
203                 if( !track->show_transitions() ) continue;
204
205                 for( Edit *edit = track->edits->first; edit; edit = edit->next ) {
206                         if( edit->transition ) {
207                                 edit_dimensions(edit, x, y, w, h);
208                                 get_transition_coords(edit, x, y, w, h);
209
210                                 if( MWindowGUI::visible(x, x + w, 0, get_w()) &&
211                                         MWindowGUI::visible(y, y + h, 0, get_h()) ) {
212                                         if( cursor_x >= x && cursor_x < x + w &&
213                                                 cursor_y >= y && cursor_y < y + h ) {
214                                                 transition = edit->transition;
215                                                 done = 1;
216                                                 break;
217                                         }
218                                 }
219                         }
220                 }
221         }
222
223         // If transition found, display its context help
224         if(transition) {
225                 context_help_show(transition->title);
226                 return 1;
227         }
228
229         return 0;
230 }
231
232 int TrackCanvas::help_keyframes(int cursor_x, int cursor_y)
233 {
234         int result = 0;
235         EDLSession *session = mwindow->edl->session;
236
237         static BC_Pixmap *help_pixmaps[AUTOMATION_TOTAL] =
238         {
239                 0, 0, 0, 0, 0, 0, 0, 0,
240                 pankeyframe_pixmap,
241                 modekeyframe_pixmap,
242                 maskkeyframe_pixmap,
243                 0,
244         };
245
246         for(Track *track = mwindow->edl->tracks->first;
247                 track && !result;
248                 track = track->next) {
249                 if( track->is_hidden() ) continue;
250                 Automation *automation = track->automation;
251
252                 for(int i = 0; i < AUTOMATION_TOTAL && !result; i ++)
253                 {
254 // Event not trapped and automation visible
255                         Autos *autos = automation->autos[i];
256                         if(!result && session->auto_conf->autos[i] && autos) {
257                                 switch(i) {
258                                 case AUTOMATION_MODE:
259                                 case AUTOMATION_PAN:
260                                 case AUTOMATION_MASK:
261                                         result = help_autos(track, automation->autos[i],
262                                                 cursor_x, cursor_y,
263                                                 help_pixmaps[i]);
264                                                 break;
265
266                                 default: {
267                                         switch(autos->get_type()) {
268                                         case Autos::AUTOMATION_TYPE_FLOAT: {
269                                                 Automation automation(0, track);
270                                                 int grouptype = automation.autogrouptype(i, track);
271
272                                                 result = help_float_autos(track, autos,
273                                                         cursor_x, cursor_y,
274                                                         grouptype);
275
276                                                 break; }
277
278                                         case Autos::AUTOMATION_TYPE_INT: {
279                                                 result = help_int_autos(track, autos,
280                                                         cursor_x, cursor_y);
281                                                 break; }
282                                         }
283                                         break; }
284                                 }
285
286                                 if(result)
287                                 {
288                                         context_help_show("Using Autos");
289                                 }
290                         }
291                 }
292
293                 if(!result && session->auto_conf->plugins) {
294                         result = help_plugin_autos(track, cursor_x, cursor_y);
295                         if(result) {
296                                 context_help_show("Edit Params");
297                         }
298                 }
299         }
300
301         return result;
302 }
303
304 int TrackCanvas::help_plugin_autos(Track *track, int cursor_x, int cursor_y)
305 {
306         int result = 0;
307
308         double view_start;
309         double unit_start;
310         double view_end;
311         double unit_end;
312         double yscale;
313         int center_pixel;
314         double zoom_sample;
315         double zoom_units;
316
317         if(!track->expand_view) return 0;
318
319         calculate_viewport(track,
320                 view_start,
321                 unit_start,
322                 view_end,
323                 unit_end,
324                 yscale,
325                 center_pixel,
326                 zoom_sample,
327                 zoom_units);
328
329         for(int i = 0; i < track->plugin_set.total && !result; i++)
330         {
331                 PluginSet *plugin_set = track->plugin_set.values[i];
332                 int center_pixel = track->y_pixel -
333                         mwindow->edl->local_session->track_start[pane->number];
334                 if( track->show_titles() )
335                         center_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
336                 if( track->show_assets() )
337                         center_pixel += track->data_h;
338                 center_pixel += (i + 0.5) * mwindow->theme->get_image("plugin_bg_data")->get_h();
339
340                 for(Plugin *plugin = (Plugin*)plugin_set->first;
341                         plugin && !result;
342                         plugin = (Plugin*)plugin->next)
343                 {
344                         for(KeyFrame *keyframe = (KeyFrame*)plugin->keyframes->first;
345                                 keyframe && !result;
346                                 keyframe = (KeyFrame*)keyframe->next)
347                         {
348                                 if(keyframe->position >= unit_start && keyframe->position < unit_end)
349                                 {
350                                         int64_t x = (int64_t)((keyframe->position - unit_start) / zoom_units);
351                                         int y = center_pixel - keyframe_pixmap->get_h() / 2;
352
353                                         if(cursor_x >= x && cursor_y >= y &&
354                                                 cursor_x < x + keyframe_pixmap->get_w() &&
355                                                 cursor_y < y + keyframe_pixmap->get_h())
356                                         {
357                                                 result = 1;
358                                         }
359                                 }
360                         }
361                 }
362         }
363
364         return result;
365 }
366
367 int TrackCanvas::help_autos(Track *track, Autos *autos, int cursor_x,
368                 int cursor_y, BC_Pixmap *pixmap)
369 {
370         int result = 0;
371
372         double view_start;
373         double unit_start;
374         double view_end;
375         double unit_end;
376         double yscale;
377         int center_pixel;
378         double zoom_sample;
379         double zoom_units;
380
381         calculate_viewport(track,
382                 view_start,
383                 unit_start,
384                 view_end,
385                 unit_end,
386                 yscale,
387                 center_pixel,
388                 zoom_sample,
389                 zoom_units);
390
391         Auto *current;
392
393         for(current = autos->first; current && !result; current = NEXT)
394         {
395                 if(current->position >= unit_start && current->position < unit_end)
396                 {
397                         int64_t x, y;
398                         x = (int64_t)((double)(current->position - unit_start) /
399                                 zoom_units - (pixmap->get_w() / 2.0 + 0.5));
400                         y = center_pixel - pixmap->get_h() / 2;
401
402                         if(cursor_x >= x && cursor_y >= y &&
403                                 cursor_x < x + pixmap->get_w() &&
404                                 cursor_y < y + pixmap->get_h())
405                         {
406                                 result = 1;
407                         }
408                 }
409         }
410
411         return result;
412 }
413
414 int TrackCanvas::help_float_autos(Track *track, Autos *autos, int cursor_x, int cursor_y, int autogrouptype)
415 {
416         int result = 0;
417         int center_pixel;
418         double view_start, unit_start;
419         double view_end, unit_end, yscale;
420         double zoom_sample, zoom_units;
421         double slope;
422
423         calculate_viewport(track, view_start, unit_start, view_end, unit_end,
424                         yscale, center_pixel, zoom_sample, zoom_units);
425
426 // Get first auto before start
427         Auto *current = 0, *previous = 0;
428
429         for( current = autos->last;
430                 current && current->position >= unit_start;
431                 current = PREVIOUS ) ;
432
433         Auto *first_auto = current ? current :
434                  autos->first ? autos->first : autos->default_auto;
435
436         double ax = 0, ay = 0, ax2 = 0, ay2 = 0;
437         if( first_auto ) {
438                 calculate_auto_position(0, &ax, &ay, 0, 0, 0, 0,
439                         first_auto, unit_start, zoom_units, yscale, autogrouptype);
440         }
441         if( current )
442                 current = NEXT;
443         else {
444                 current = autos->first;
445                 ax = 0;
446         }
447
448         do {
449                 if(current) {
450                         calculate_auto_position(1, &ax2, &ay2, 0, 0, 0, 0,
451                                 current, unit_start, zoom_units, yscale, autogrouptype);
452                 }
453                 else {
454                         ax2 = get_w();
455                         ay2 = ay;
456                 }
457
458                 slope = ax2 > ax ? (ay2 - ay) / (ax2 - ax) : 0;
459
460                 if(ax2 > get_w()) {
461                         ax2 = get_w();
462                         ay2 = ay + slope * (get_w() - ax);
463                 }
464
465                 if(ax < 0) {
466                         ay = ay + slope * (0 - ax);
467                         ax = 0;
468                 }
469
470 // test handle
471                 if( current && !result && current != autos->default_auto ) {
472                         if( track->is_armed() ) {
473                                 result = test_floatauto((FloatAuto*)current, 0,
474                                         (int)center_pixel, (int)yscale, cursor_x, cursor_y,
475                                         unit_start, zoom_units, yscale, autogrouptype);
476                         }
477                 }
478
479 // test joining line
480                 if( !result && track->is_armed() ) {
481                         result = test_floatline(center_pixel,
482                                 (FloatAutos*)autos, unit_start, zoom_units, yscale,
483 // Exclude auto coverage from the end of the line.  The auto overlaps
484                                 (int)ax, (int)ax2 - HANDLE_W / 2, cursor_x, cursor_y,
485                                 0, autogrouptype);
486                 }
487
488                 if( current ) {
489                         previous = current;
490                         calculate_auto_position(0, &ax2, &ay2, 0, 0, 0, 0, previous,
491                                 unit_start, zoom_units, yscale, autogrouptype);
492                         current = NEXT;
493                 }
494                 ax = ax2;  ay = ay2;
495         } while( current && current->position <= unit_end && !result );
496
497         if( ax < get_w() && !result ) {
498                 ax2 = get_w();  ay2 = ay;
499                 if(track->is_armed()) {
500                         result = test_floatline(center_pixel,
501                                 (FloatAutos*)autos, unit_start, zoom_units, yscale,
502                                 (int)ax, (int)ax2, cursor_x, cursor_y,
503                                 0, autogrouptype);
504                 }
505         }
506
507         return result;
508 }
509
510 int TrackCanvas::help_int_autos(Track *track, Autos *autos, int cursor_x, int cursor_y)
511 {
512         int result = 0;
513         double view_start;
514         double unit_start;
515         double view_end;
516         double unit_end;
517         double yscale;
518         int center_pixel;
519         double zoom_sample;
520         double zoom_units;
521         double ax, ay, ax2, ay2;
522
523         calculate_viewport(track,
524                 view_start,
525                 unit_start,
526                 view_end,
527                 unit_end,
528                 yscale,
529                 center_pixel,
530                 zoom_sample,
531                 zoom_units);
532
533         double high = -yscale * 0.8 / 2;
534         double low = yscale * 0.8 / 2;
535
536 // Get first auto before start
537         Auto *current;
538         for(current = autos->last; current && current->position >= unit_start; current = PREVIOUS)
539                 ;
540
541         if(current)
542         {
543                 ax = 0;
544                 ay = ((IntAuto*)current)->value > 0 ? high : low;
545                 current = NEXT;
546         }
547         else
548         {
549                 current = autos->first ? autos->first : autos->default_auto;
550                 if(current)
551                 {
552                         ax = 0;
553                         ay = ((IntAuto*)current)->value > 0 ? high : low;
554                 }
555                 else
556                 {
557                         ax = 0;
558                         ay = yscale;
559                 }
560         }
561
562         do
563         {
564                 if(current)
565                 {
566                         ax2 = (double)(current->position - unit_start) / zoom_units;
567                         ay2 = ((IntAuto*)current)->value > 0 ? high : low;
568                 }
569                 else
570                 {
571                         ax2 = get_w();
572                         ay2 = ay;
573                 }
574
575                 if(ax2 > get_w()) ax2 = get_w();
576
577                 if(current && !result)
578                 {
579                         if(current != autos->default_auto)
580                         {
581                                 if(track->is_armed())
582                                 {
583                                         result = test_auto(current,
584                                                 (int)ax2,
585                                                 (int)ay2,
586                                                 (int)center_pixel,
587                                                 (int)yscale,
588                                                 cursor_x,
589                                                 cursor_y,
590                                                 0);
591                                 }
592                         }
593
594                         current = NEXT;
595                 }
596
597                 if(!result)
598                 {
599                         if(track->is_armed())
600                         {
601                                 result = test_toggleline(autos,
602                                         center_pixel,
603                                         (int)ax,
604                                         (int)ay,
605                                         (int)ax2,
606                                         (int)ay2,
607                                         cursor_x,
608                                         cursor_y,
609                                         0);
610                         }
611                 }
612
613                 ax = ax2;
614                 ay = ay2;
615         }while(current && current->position <= unit_end && !result);
616
617         if(ax < get_w() && !result)
618         {
619                 ax2 = get_w();
620                 ay2 = ay;
621                 if(track->is_armed())
622                 {
623                         result = test_toggleline(autos,
624                                 center_pixel,
625                                 (int)ax,
626                                 (int)ay,
627                                 (int)ax2,
628                                 (int)ay2,
629                                 cursor_x,
630                                 cursor_y,
631                                 0);
632                 }
633         }
634         return result;
635 }
636
637 int TrackCanvas::help_transition_handles(int cursor_x, int cursor_y)
638 {
639         if( !mwindow->edl->session->auto_conf->transitions )
640                 return 0;
641         int result = 0;
642
643         Track *track = mwindow->edl->tracks->first;
644         for( ; track && !result; track=track->next) {
645                 if( track->is_hidden() ) continue;
646                 Edit *edit = track->edits->first;
647                 for( ; edit && !result; edit=edit->next ) {
648                         Transition *trans = edit->transition;
649                         if( !trans ) continue;
650                         int64_t x, y, w, h;
651                         edit_dimensions(edit, x, y, w, h);
652                         int strip_x = x, edit_y = y;
653                         get_transition_coords(edit, x, y, w, h);
654                         VFrame *strip = mwindow->theme->get_image("plugin_bg_data");
655                         int strip_y = y - strip->get_h();
656                         if( track->show_assets() && track->show_titles() )
657                                 edit_y += mwindow->theme->get_image("title_bg_data")->get_h();
658                         if( strip_y < edit_y ) strip_y = edit_y;
659                         int strip_w = Units::round(edit->track->from_units(edit->transition->length) *
660                                 mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample);
661                         int x1 = strip_x + strip_w - HANDLE_W/2, x2 = x1 + HANDLE_W;
662                         int y1 = strip_y + strip->get_h()/2 - HANDLE_H/2, y2 = y1 + HANDLE_W;
663                         if( cursor_x >= x1 && cursor_x < x2 &&
664                             cursor_y >= y1 && cursor_y < y2 ) {
665                                 result = 1;
666                         }
667                 }
668         }
669
670         if( result ) {
671                 context_help_show("Editing Effects");
672         }
673
674         return result;
675 }
676
677 int TrackCanvas::help_edit_handles(int cursor_x, int cursor_y)
678 {
679         int result = 0;
680
681         for( Track *track=mwindow->edl->tracks->first; track && !result; track=track->next) {
682                 if( track->is_hidden() ) continue;
683                 for( Edit *edit=track->edits->first; edit && !result; edit=edit->next ) {
684                         int64_t edit_x, edit_y, edit_w, edit_h;
685                         edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h);
686
687                         if( cursor_x >= edit_x && cursor_x <= edit_x + edit_w &&
688                             cursor_y >= edit_y && cursor_y < edit_y + edit_h &&
689                             ( cursor_x < edit_x + HANDLE_W ||
690                               cursor_x >= edit_x + edit_w - HANDLE_W )) {
691                                 result = 1;
692                         }
693                 }
694         }
695
696         if( result ) {
697                 context_help_show("Trimming");
698         }
699
700         return result;
701 }
702
703 int TrackCanvas::help_plugin_handles(int cursor_x, int cursor_y)
704 {
705         int result = 0;
706
707         for(Track *track = mwindow->edl->tracks->first;
708                 track && !result;
709                 track = track->next) {
710                 if( track->is_hidden() ) continue;
711                 for(int i = 0; i < track->plugin_set.total && !result; i++) {
712                         PluginSet *plugin_set = track->plugin_set.values[i];
713                         for(Plugin *plugin = (Plugin*)plugin_set->first;
714                                 plugin && !result;
715                                 plugin = (Plugin*)plugin->next) {
716                                 int64_t plugin_x, plugin_y, plugin_w, plugin_h;
717                                 plugin_dimensions(plugin, plugin_x, plugin_y, plugin_w, plugin_h);
718
719                                 if(cursor_x >= plugin_x && cursor_x <= plugin_x + plugin_w &&
720                                    cursor_y >= plugin_y && cursor_y < plugin_y + plugin_h &&
721                                    (cursor_x < plugin_x + HANDLE_W ||
722                                     cursor_x >= plugin_x + plugin_w - HANDLE_W)) {
723                                         result = 1;
724                                 }
725                         }
726                 }
727         }
728
729         if(result) {
730                 context_help_show("Editing Effects");
731         }
732
733         return result;
734 }
735
736 int TrackCanvas::help_plugins(int cursor_x, int cursor_y)
737 {
738         int done = 0;
739         int64_t x, y, w, h;
740         Track *track = 0;
741         Plugin *plugin = 0;
742         char title[BCTEXTLEN];
743
744         // Detect, if any, the plugin under cursor
745         for(track = mwindow->edl->tracks->first; track && !done; track = track->next) {
746                 if(!track->expand_view) continue;
747
748                 for(int i = 0; i < track->plugin_set.total && !done; i++) {
749                         // first check if plugins are visible at all
750                         if (!track->expand_view)
751                                 continue;
752                         PluginSet *plugin_set = track->plugin_set.values[i];
753                         for(plugin = (Plugin*)plugin_set->first;
754                                 plugin && !done;
755                                 plugin = (Plugin*)plugin->next) {
756                                 plugin_dimensions(plugin, x, y, w, h);
757                                 if(MWindowGUI::visible(x, x + w, 0, get_w()) &&
758                                         MWindowGUI::visible(y, y + h, 0, get_h())) {
759                                         if(cursor_x >= x && cursor_x < x + w &&
760                                                 cursor_y >= y && cursor_y < y + h) {
761                                                 done = 1;
762                                                 break;
763                                         }
764                                 }
765                         }
766                 }
767         }
768
769         // If plugin found, display its context help
770         if(plugin) {
771                 strcpy(title, plugin->title);
772                 if(! strcmp(title, "Overlay"))
773                         // "Overlay" plugin title is ambiguous
774                         switch(plugin->track->data_type)
775                         {
776                         case TRACK_AUDIO:
777                                 strcat(title, " \\(Audio\\)");
778                                 break;
779                         case TRACK_VIDEO:
780                                 strcat(title, " \\(Video\\)");
781                                 break;
782                         }
783                 if(! strncmp(title, "F_", 2))
784                         // FFmpeg plugins can be audio or video
785                         switch(plugin->track->data_type)
786                         {
787                         case TRACK_AUDIO:
788                                 strcpy(title, "FFmpeg Audio Plugins");
789                                 break;
790                         case TRACK_VIDEO:
791                                 strcpy(title, "FFmpeg Video Plugins");
792                                 break;
793                         }
794                 context_help_show(title);
795                 return 1;
796         }
797
798         return 0;
799 }
800 // *** END_CONTEXT_HELP ***
801
802 int TrackCanvas::cursor_leave_event()
803 {
804 // Because drag motion calls get_cursor_over_window we can be sure that
805 // all highlights get deleted now.
806 // This ended up blocking keyboard input from the drag operations.
807         if( timebar_position >= 0 )
808         {
809                 timebar_position = -1;
810                 if( pane->timebar )
811                         pane->timebar->update(1);
812         }
813
814         return 0;
815 //      return drag_motion();
816 }
817
818 int TrackCanvas::drag_motion_event()
819 {
820         return gui->drag_motion();
821 }
822
823 int TrackCanvas::drag_motion(
824                 Track **over_track, Edit **over_edit,
825                 PluginSet **over_pluginset, Plugin **over_plugin)
826 {
827         int update_scroll = 0;
828         int cursor_x = get_relative_cursor_x();
829         int cursor_y = get_relative_cursor_y();
830         if( get_cursor_over_window() ) {
831                 drag_cursor_motion(cursor_x, cursor_y,
832                         over_track, over_edit, over_pluginset, over_plugin);
833         }
834         if( over_track && !*over_track )
835                 *over_track = pane->over_patchbay();
836
837         switch( mwindow->session->current_operation ) {
838         case DRAG_EDIT_SELECT: {
839                 Edit *edit = over_edit ? *over_edit : 0;
840                 double position = mwindow->edl->get_cursor_position(cursor_x, pane->number);
841                 mwindow->session->drag_position = mwindow->edl->align_to_frame(position, 0);
842                 drag_edit_select(edit, 0, 1);
843                 update_scroll = 1;
844                 break; }
845         }
846
847         if( update_scroll ) {
848                 if( !drag_scroll &&
849                     (cursor_x >= get_w() || cursor_x < 0 ||
850                      cursor_y >= get_h() || cursor_y < 0))
851                         start_dragscroll();
852                 else if( drag_scroll &&
853                          cursor_x < get_w() && cursor_x >= 0 &&
854                          cursor_y < get_h() && cursor_y >= 0 )
855                         stop_dragscroll();
856         }
857
858         return 0;
859 }
860
861 int TrackCanvas::drag_cursor_motion(int cursor_x, int cursor_y,
862                 Track **over_track, Edit **over_edit,
863                 PluginSet **over_pluginset, Plugin **over_plugin)
864 {
865         if( cursor_x >= 0 && cursor_y >= 0 &&
866             cursor_x < get_w() && cursor_y < get_h() )
867         {
868 //printf("TrackCanvas::drag_motion %d %d\n", __LINE__, pane->number);
869 // Find the edit and track the cursor is over
870                 for(Track *track = mwindow->edl->tracks->first; track; track = track->next)
871                 {
872                         if( track->is_hidden() ) continue;
873                         int64_t track_x, track_y, track_w, track_h;
874                         track_dimensions(track, track_x, track_y, track_w, track_h);
875
876                         if(cursor_y >= track_y &&
877                                 cursor_y < track_y + track_h)
878                         {
879                                 *over_track = track;
880                                 for(Edit *edit = track->edits->first; edit; edit = edit->next)
881                                 {
882                                         int64_t edit_x, edit_y, edit_w, edit_h;
883                                         edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h);
884                                         if( cursor_y < edit_y || cursor_y >= edit_y + edit_h ) continue;
885                                         if( cursor_x >= edit_x && cursor_x < edit_x + edit_w ) {
886                                                 *over_edit = edit;
887                                                 break;
888                                         }
889                                         if( edit != track->edits->last ) continue;
890                                         if( mwindow->session->current_operation != DRAG_ATRANSITION &&
891                                             mwindow->session->current_operation != DRAG_VTRANSITION ) continue;
892                                         if( !edit->silence() ) {
893                                                 // add silence to allow drag transition past last edit
894                                                 //  will be deleted by Edits::optimize if not used
895                                                 double length = mwindow->edl->session->default_transition_length;
896                                                 int64_t start = edit->startproject+edit->length;
897                                                 int64_t units = track->to_units(length, 1);
898                                                 track->edits->create_silence(start, start+units);
899                                                 continue;
900                                         }
901                                         if( cursor_x >= edit_x ) { *over_edit = edit; break; }
902                                 }
903
904                                 for(int i = 0; i < track->plugin_set.total; i++)
905                                 {
906                                         PluginSet *pluginset = track->plugin_set.values[i];
907                                         for(Plugin *plugin = (Plugin*)pluginset->first;
908                                                 plugin;
909                                                 plugin = (Plugin*)plugin->next)
910                                         {
911                                                 int64_t plugin_x, plugin_y, plugin_w, plugin_h;
912                                                 plugin_dimensions(plugin, plugin_x, plugin_y, plugin_w, plugin_h);
913
914                                                 if(cursor_y >= plugin_y &&
915                                                         cursor_y < plugin_y + plugin_h)
916                                                 {
917                                                         *over_pluginset = plugin->plugin_set;
918
919                                                         if(cursor_x >= plugin_x &&
920                                                                 cursor_x < plugin_x + plugin_w)
921                                                         {
922                                                                 *over_plugin = plugin;
923                                                                 break;
924                                                         }
925                                                 }
926                                         }
927                                 }
928                                 break;
929                         }
930                 }
931         }
932
933         return 0;
934 }
935
936
937 int TrackCanvas::drag_stop_event()
938 {
939         int result = gui->drag_stop();
940         if( !result && mwindow->session->current_operation ) {
941                 mwindow->session->current_operation = NO_OPERATION;
942                 drag_scroll = 0;
943         }
944         return result;
945 }
946
947 int TrackCanvas::drag_stop(int *redraw)
948 {
949         int result = 0;
950         int insertion  = 0;
951         int over_window = 0;
952         int cursor_x = -1, cursor_y = -1;
953
954         if( get_cursor_over_window() ) {
955                 if( (cursor_x = get_relative_cursor_x()) >= 0 && cursor_x < get_w() &&
956                     (cursor_y = get_relative_cursor_y()) >= 0 && cursor_y < get_h() )
957                         over_window = 1;
958                 else {
959                         Track *track = pane->over_patchbay();
960                         if( track && mwindow->session->track_highlighted == track )
961                                 over_window = 1;
962                 }
963         }
964
965         if( over_window ) {
966                 switch(mwindow->session->current_operation) {
967                 case DRAG_VTRANSITION:
968                 case DRAG_ATRANSITION:
969                         if(mwindow->session->edit_highlighted) {
970                                 if( (mwindow->session->current_operation == DRAG_ATRANSITION &&
971                                      mwindow->session->track_highlighted->data_type == TRACK_AUDIO) ||
972                                     (mwindow->session->current_operation == DRAG_VTRANSITION &&
973                                      mwindow->session->track_highlighted->data_type == TRACK_VIDEO) ) {
974                                         mwindow->session->current_operation = NO_OPERATION;
975                                         mwindow->paste_transition();
976                                         result = 1;
977                                 }
978                         }
979                         *redraw = 1;
980                         break;
981
982 // Behavior for dragged plugins is limited by the fact that a shared plugin
983 // can only refer to a standalone plugin that exists in the same position in
984 // time.  Dragging a plugin from one point in time to another can't produce
985 // a shared plugin to the original plugin.  In this case we relocate the
986 // plugin instead of sharing it.
987                 case DRAG_AEFFECT_COPY:
988                 case DRAG_VEFFECT_COPY:
989                         if( mwindow->session->track_highlighted &&
990                             ((mwindow->session->current_operation == DRAG_AEFFECT_COPY &&
991                               mwindow->session->track_highlighted->data_type == TRACK_AUDIO) ||
992                              (mwindow->session->current_operation == DRAG_VEFFECT_COPY &&
993                               mwindow->session->track_highlighted->data_type == TRACK_VIDEO)) ) {
994                                 mwindow->session->current_operation = NO_OPERATION;
995
996                                 int64_t drop_position = -1;
997                                 Plugin *drag_plugin = mwindow->session->drag_plugin;
998                                 PluginSet *hi_plugin_set = mwindow->session->pluginset_highlighted;
999                                 Track *hi_track = mwindow->session->track_highlighted;
1000
1001 // Insert shared plugin in source
1002 // Move source to different location
1003                                 if( hi_plugin_set && hi_plugin_set->track == drag_plugin->track ) {
1004 //printf("TrackCanvas::drag_stop 6\n");
1005                                         drop_position = drop_plugin_position(hi_plugin_set, drag_plugin);
1006                                         if( drop_position < 0 ) {
1007                                                 result = 1;
1008                                                 break;          // Do not do anything
1009                                         }
1010
1011                                         Track *track = mwindow->session->track_highlighted;
1012                                         drop_position = track->frame_align(drop_position, 0);
1013                                         mwindow->move_effect(drag_plugin, hi_plugin_set, drop_position);
1014                                         result = 1;
1015                                 }
1016                                 else if( hi_track ) {
1017 // Put it in a new plugin set determined by an edit boundary, or at the start of the track
1018                                         Edit *hi_edit = mwindow->session->edit_highlighted;
1019                                         drop_position = hi_edit ? hi_edit->startproject : 0;
1020                                         if( drop_position < 0 ) {
1021                                                 result = 1;
1022                                                 break;          // Do not do anything
1023                                         }
1024
1025                                         Track *track = mwindow->session->track_highlighted;
1026                                         drop_position = track->frame_align(drop_position, 0);
1027                                         mwindow->move_effect(drag_plugin, hi_track, drop_position);
1028                                         result = 1;
1029                                 }
1030                         }
1031                         break;
1032
1033                 case DRAG_AEFFECT:
1034                 case DRAG_VEFFECT:
1035                         if( mwindow->session->track_highlighted &&
1036                             ((mwindow->session->current_operation == DRAG_AEFFECT &&
1037                               mwindow->session->track_highlighted->data_type == TRACK_AUDIO) ||
1038                              (mwindow->session->current_operation == DRAG_VEFFECT &&
1039                               mwindow->session->track_highlighted->data_type == TRACK_VIDEO)) ) {
1040 // Drop all the effects
1041                                 PluginSet *plugin_set = mwindow->session->pluginset_highlighted;
1042                                 Track *track = mwindow->session->track_highlighted;
1043                                 double start = 0;
1044                                 double length = track->get_length();
1045
1046                                 if(mwindow->session->plugin_highlighted) {
1047                                         start = track->from_units(mwindow->session->plugin_highlighted->startproject);
1048                                         length = track->from_units(mwindow->session->plugin_highlighted->length);
1049                                         if(length <= 0) length = track->get_length();
1050                                 }
1051                                 else if(mwindow->session->pluginset_highlighted) {
1052                                                 start = track->from_units(plugin_set->length());
1053                                         length = track->get_length() - start;
1054                                         if(length <= 0) length = track->get_length();
1055                                 }
1056                                 else if(mwindow->edl->local_session->get_selectionend() >
1057                                         mwindow->edl->local_session->get_selectionstart()) {
1058                                         start = mwindow->edl->local_session->get_selectionstart();
1059                                         length = mwindow->edl->local_session->get_selectionend() -
1060                                                 mwindow->edl->local_session->get_selectionstart();
1061                                 }
1062 // Move to a point between two edits
1063                                 else if(mwindow->session->edit_highlighted) {
1064                                         start = mwindow->session->track_highlighted->from_units(
1065                                                 mwindow->session->edit_highlighted->startproject);
1066                                         length = mwindow->session->track_highlighted->from_units(
1067                                                 mwindow->session->edit_highlighted->length);
1068                                 }
1069                                 start = mwindow->edl->align_to_frame(start, 0);
1070                                 mwindow->insert_effects_canvas(track, start, length);
1071                                 *redraw = 1;
1072                         }
1073                         if( mwindow->session->track_highlighted )
1074                                 result = 1;  // we have to cleanup
1075                         break;
1076
1077                 case DRAG_ASSET:
1078                         if(mwindow->session->track_highlighted) {
1079                                 double asset_duration = 0;
1080                                 int64_t asset_length_units = 0;
1081
1082                                 if(mwindow->session->current_operation == DRAG_ASSET &&
1083                                         mwindow->session->drag_assets->total) {
1084                                         Indexable *indexable = mwindow->session->drag_assets->values[0];
1085                                         // we use video if we are over video and audio if we are over audio
1086                                         if( indexable->have_video() &&
1087                                             mwindow->session->track_highlighted->data_type == TRACK_VIDEO ) {
1088                                                 // Images have length -1
1089                                                 int64_t video_length = indexable->get_video_frames();
1090                                                 if (video_length < 0) {
1091                                                         if(mwindow->edl->session->si_useduration)
1092                                                                 video_length = mwindow->edl->session->si_duration;
1093                                                         else
1094                                                                 video_length = 1.0 / mwindow->edl->session->frame_rate ;
1095                                                 }
1096                                                 asset_duration = video_length / indexable->get_frame_rate();
1097                                         }
1098                                         else if( indexable->have_audio() &&
1099                                                  mwindow->session->track_highlighted->data_type == TRACK_AUDIO ) {
1100                                                 int64_t audio_length = indexable->get_audio_samples();
1101                                                 asset_duration = (double)audio_length / indexable->get_sample_rate();
1102                                         }
1103                                         else {
1104                                                 result = 1;
1105                                                 break;  // Do not do anything
1106                                         }
1107                                 }
1108                                 else if( mwindow->session->current_operation == DRAG_ASSET &&
1109                                          mwindow->session->drag_clips->total ) {
1110                                         EDL *clip = mwindow->session->drag_clips->values[0];
1111                                         asset_duration = clip->tracks->total_length();
1112                                 }
1113                                 else {
1114                                         printf("DRAG_ASSET error: Asset dropped, but both drag_clips and drag_assets total is zero\n");
1115                                 }
1116
1117                                 asset_length_units = mwindow->session->track_highlighted->to_units(asset_duration, 1);
1118                                 int64_t drop_position = drop_edit_position (&insertion, NULL, asset_length_units);
1119                                 if( drop_position < 0 ) {
1120                                         result = 1;
1121                                         break;          // Do not do anything
1122                                 }
1123
1124                                 Track *track = mwindow->session->track_highlighted;
1125                                 double track_position = track->from_units(drop_position);
1126                                 track_position = mwindow->edl->align_to_frame(track_position, 0);
1127
1128 //                              if (!insertion) {
1129 //                                      // FIXME, we should create an mwindow/EDL method that overwrites, without clearing the keyframes and autos
1130 //                                      // Unfortunately, this is _a lot_ of work to do right
1131 //                                      printf("Problematic insertion\n");
1132 //                                      mwindow->edl->tracks->clear(track_position,
1133 //                                              track_position + asset_duration, 0);
1134 //                              }
1135                                 mwindow->paste_assets(track_position, track, !insertion);
1136                                 result = 1;    // need to be one no matter what, since we have track highlited so we have to cleanup....
1137                         }
1138                         break;
1139
1140                 case DRAG_EDIT:
1141                         mwindow->session->current_operation = NO_OPERATION;
1142                         if(mwindow->session->track_highlighted) {
1143                                 if( mwindow->session->track_highlighted->data_type ==
1144                                         mwindow->session->drag_edit->track->data_type) {
1145                                         int64_t drop_position = drop_edit_position(&insertion,
1146                                                         mwindow->session->drag_edit,
1147                                                         mwindow->session->drag_edit->length);
1148                                         if( drop_position < 0 ) {
1149                                                 result = 1;
1150                                                 break;          // Do not do anything
1151                                         }
1152                                         Track *track = mwindow->session->track_highlighted;
1153                                         double track_position = track->from_units(drop_position);
1154                                         track_position = mwindow->edl->align_to_frame(track_position, 0);
1155                                         mwindow->move_edits(mwindow->session->drag_edits,
1156                                                         track, track_position, insertion);
1157                                 }
1158
1159                                 result = 1;
1160                         }
1161                         break;
1162                 case DRAG_GROUP: {
1163                         mwindow->session->current_operation = NO_OPERATION;
1164                         EDL *drag_group = mwindow->session->drag_group;
1165                         if( drag_group ) {
1166                                 Track *drop_track = mwindow->session->track_highlighted;
1167                                 Track *drag_track = mwindow->session->drag_group_first_track;
1168                                 Edit *drag_edit = mwindow->session->drag_group_edit;
1169                                 Track *edit_track = drag_edit ? drag_edit->track : 0;
1170                                 while( drop_track && edit_track ) {
1171                                         if( edit_track == drag_track ) break;
1172                                         edit_track = edit_track->previous;
1173                                         drop_track = drop_track->previous;
1174                                 }
1175                                 if( drop_track ) {
1176                                         double cur_pos = mwindow->session->drag_group_position;
1177                                         double new_pos = mwindow->edl->get_cursor_position(cursor_x, pane->number);
1178                                         new_pos -= mwindow->session->drag_position - cur_pos;
1179                                         new_pos = mwindow->edl->align_to_frame(new_pos, 0);
1180                                         double drop_position = new_pos;
1181                                         int ret = test_track_group(drag_group, drop_track, new_pos);
1182                                         if( !ret && new_pos != drop_position ) {
1183                                                 drop_position = new_pos;
1184                                                 ret = test_track_group(drag_group, drop_track, new_pos);
1185                                         }
1186                                         if( ret >= 0 )
1187                                                 mwindow->move_group(drag_group, drop_track, drop_position,
1188                                                         ret > 0 ? !shift_down() : shift_down());
1189                                         drag_group->remove_user();
1190                                         mwindow->session->drag_group = 0;
1191                                 }
1192                         }
1193                         result = 1;
1194                         break; }
1195                 case DRAG_EDIT_SELECT:
1196                         int select = ctrl_down() ? -1 : 1;
1197                         drag_edit_select(mwindow->session->edit_highlighted, select, 0);
1198                         break;
1199                 }
1200         }
1201
1202         switch( mwindow->session->current_operation ) {
1203         case DRAG_EDIT_SELECT:
1204                 mwindow->session->current_operation = NO_OPERATION;
1205                 result = 1;
1206                 break;
1207         }
1208
1209         return result;
1210 }
1211
1212 int TrackCanvas::drag_start_event()
1213 {
1214         int result = 0;
1215         int redraw = 0;
1216         int rerender = 0;
1217         int new_cursor, update_cursor;
1218
1219         if( mwindow->session->current_operation == GROUP_TOGGLE )
1220                 mwindow->session->current_operation = NO_OPERATION;
1221         else if( mwindow->session->current_operation != NO_OPERATION )
1222                 return 0;
1223
1224         if(is_event_win()) {
1225                 if(do_plugins(get_drag_x(), get_drag_y(),
1226                                 1, 0, redraw, rerender)) {
1227                         result = 1;
1228                 }
1229                 else if(do_edits(get_drag_x(), get_drag_y(),
1230                                 0, 1, redraw, rerender, new_cursor, update_cursor)) {
1231                         result = 1;
1232                 }
1233         }
1234
1235         return result;
1236 }
1237
1238 int64_t TrackCanvas::drop_edit_position(int *is_insertion, Edit *moved_edit, int64_t moved_edit_length)
1239 {
1240         // get the canvas/track position
1241         Track *track = mwindow->session->track_highlighted;
1242         int cursor_x = get_relative_cursor_x();
1243         double zoom_scale = (double)mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample;
1244         double cur_pos = (cursor_x + mwindow->edl->local_session->view_start[pane->number]) / zoom_scale;
1245         double position = mwindow->edl->align_to_frame(cur_pos, 1);
1246         if( position <= 0 ) {
1247                 *is_insertion = 1;
1248                 return 0;
1249         }
1250         double cursor_position = position;
1251         int64_t drop_position = track->to_units(cursor_position, 1);
1252         if( moved_edit ) { // relative cursor position depends upon drop point
1253                 double moved_edit_start = moved_edit->track->from_units(moved_edit->startproject);
1254                 position -= mwindow->session->drag_position - moved_edit_start;
1255         }
1256         int64_t edit_position = track->to_units(position, 1);
1257         int64_t grab_position = edit_position;
1258         if( !moved_edit )  // for clips and assets acts as they were grabbed in the middle
1259                 grab_position -= moved_edit_length / 2;
1260         Edit *last_edit = track->edits->last;
1261         if( !last_edit || edit_position >= (last_edit->startproject+last_edit->length) ) {
1262                 *is_insertion = 0;
1263                 return edit_position;
1264         }
1265
1266         int64_t drop_start = 0, drop_end = 0;  // drop zone boundries
1267         Edit *next_edit = track->edits->first;
1268         while( next_edit ) {
1269                 Edit *edit = next_edit;  next_edit = (Edit *)edit->next;
1270                 int64_t edit_start = edit->startproject;
1271                 int64_t edit_end = edit_start + edit->length;
1272                 double edit_start_pos = edit->track->from_units(edit_start);
1273                 if( (fabs(edit_start_pos-cursor_position)*zoom_scale) < HANDLE_W ) {
1274                         *is_insertion = 1; // cursor is close to the beginning of an edit -> insertion
1275                         return edit_start;
1276                 }
1277                 double edit_end_pos = edit->track->from_units(edit_end);
1278                 if( (fabs(edit_end_pos-cursor_position)*zoom_scale) < HANDLE_W ) {
1279                         *is_insertion = 1; // cursor is close to the end of an edit -> insertion
1280                         return edit_end;
1281                 }
1282                 if( edit != moved_edit && !edit->silence() )
1283                         drop_start = edit_end; // reset drop zone
1284                 if( next_edit ) {
1285                         if( next_edit == moved_edit || next_edit->silence() ) continue;
1286                         drop_end = edit_end;
1287                 }
1288                 else
1289                         drop_end = INT64_MAX;
1290                 if( edit_position >= drop_start &&
1291                     edit_position+moved_edit_length < drop_end ) {
1292                         *is_insertion = 0; // fits in the zone
1293                         return edit_position;
1294                 }
1295                 if( drop_position < drop_end ) { // drop in the zone
1296                         if( (drop_end - drop_start) >= moved_edit_length ) {
1297                                 *is_insertion = 0; // fits in the zone, but over the edge
1298                                 int64_t dx0 = llabs(drop_position - drop_start);
1299                                 int64_t dx1 = llabs(drop_position - drop_end);
1300                                 return dx0 < dx1 ? drop_start : drop_end - moved_edit_length;
1301                         }
1302                         *is_insertion = 1;
1303                         int64_t edit_center = (edit_start + edit_end) / 2;
1304                         return position < edit_center ? drop_start : drop_end;
1305                 }
1306         }
1307
1308         *is_insertion = 0;
1309         return -1;
1310 }
1311
1312 int64_t TrackCanvas::drop_plugin_position(PluginSet *plugin_set, Plugin *moved_plugin)
1313 {
1314         // get the canvas/track position
1315         Track *track = plugin_set->track;
1316         double moved_plugin_length = moved_plugin->track->from_units(moved_plugin->length);
1317         int64_t track_plugin_length = track->to_units(moved_plugin_length, 0);
1318         int cursor_x = get_relative_cursor_x();
1319         double zoom_scale = (double)mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample;
1320         double cur_pos = (cursor_x + mwindow->edl->local_session->view_start[pane->number]) / zoom_scale;
1321         double position = mwindow->edl->align_to_frame(cur_pos, 1);
1322         if( position <= 0 ) return 0;
1323         int64_t drop_position = track->to_units(position, 1);
1324         Plugin *last_plugin = (Plugin *)plugin_set->last;
1325         if( !last_plugin ) return drop_position;
1326         double plugin_set_end = last_plugin->track->from_units(last_plugin->startproject+last_plugin->length);
1327         if( position >= plugin_set_end ) return drop_position;
1328         double moved_plugin_start = moved_plugin->track->from_units(moved_plugin->startproject);
1329         position -= mwindow->session->drag_position - moved_plugin_start;
1330         int64_t plugin_position = track->to_units(position, 1);
1331
1332         int64_t drop_start = 0, drop_end = 0;  // drop zone boundries
1333         Plugin *next_plugin = (Plugin *)plugin_set->first;
1334         while( next_plugin ) {
1335                 Plugin *plugin = next_plugin;  next_plugin = (Plugin *)plugin->next;
1336                 int64_t plugin_start = plugin->startproject;
1337                 int64_t plugin_end = plugin_start + plugin->length;
1338                 double plugin_end_pos = plugin->track->from_units(plugin_end);
1339                 int64_t track_plugin_end = track->to_units(plugin_end_pos, 0);
1340                 if( plugin != moved_plugin && !plugin->silence() )
1341                         drop_start = track_plugin_end;
1342                 if( next_plugin ) {
1343                         if( next_plugin == moved_plugin || next_plugin->silence() ) continue;
1344                         drop_end = track_plugin_end;
1345                 }
1346                 else
1347                         drop_end = INT64_MAX;
1348                 if( plugin_position >= drop_start && // fits in the zone
1349                     plugin_position+track_plugin_length < drop_end ) {
1350                         return plugin_position;
1351                 }
1352                 if( drop_position < drop_end ) { // drop in the zone
1353                         if( (drop_end - drop_start) >= track_plugin_length ) {
1354                                 int64_t dx0 = llabs(drop_position - drop_start);
1355                                 int64_t dx1 = llabs(drop_position - drop_end);
1356                                 return dx0 < dx1 ? drop_start : drop_end - track_plugin_length;
1357                         }
1358                 }
1359         }
1360
1361         return -1;
1362 }
1363
1364 void TrackCanvas::draw(int mode, int hide_cursor)
1365 {
1366         const int debug = 0;
1367
1368 // Swap pixmap layers
1369         if(get_w() != background_pixmap->get_w() ||
1370                 get_h() != background_pixmap->get_h())
1371         {
1372                 delete background_pixmap;
1373                 background_pixmap = new BC_Pixmap(this, get_w(), get_h());
1374         }
1375
1376 // Cursor disappears after resize when this is called.
1377 // Cursor doesn't redraw after editing when this isn't called.
1378         if(pane->cursor && hide_cursor) pane->cursor->hide();
1379         draw_top_background(get_parent(), 0, 0, get_w(), get_h(), background_pixmap);
1380
1381         if(debug) PRINT_TRACE
1382         draw_resources(mode);
1383
1384         if(debug) PRINT_TRACE
1385         draw_overlays();
1386         if(debug) PRINT_TRACE
1387 }
1388
1389 void TrackCanvas::update_cursor(int flush)
1390 {
1391         if( arrow_mode() )
1392                 set_cursor(ARROW_CURSOR, 0, flush);
1393         else if( ibeam_mode() )
1394                 set_cursor(IBEAM_CURSOR, 0, flush);
1395 }
1396
1397
1398 void TrackCanvas::test_timer()
1399 {
1400         if( resource_timer->get_difference() > 1000 && !hourglass_enabled ) {
1401                 start_hourglass();
1402                 hourglass_enabled = 1;
1403         }
1404 }
1405
1406
1407 void TrackCanvas::draw_indexes(Indexable *indexable)
1408 {
1409         IndexState *index_state = indexable->index_state;
1410 // Don't redraw raw samples
1411         if(index_state->index_zoom > mwindow->edl->local_session->zoom_sample)
1412                 return;
1413         draw_resources(NORMAL_DRAW, 1, indexable);
1414         draw_overlays();
1415         flash(0);
1416 }
1417
1418 void TrackCanvas::draw_resources(int mode,
1419         int indexes_only,
1420         Indexable *indexable)
1421 {
1422         const int debug = 0;
1423
1424         if(debug) PRINT_TRACE
1425
1426 // can't stop thread here, because this is called for every pane
1427 //      if(mode != IGNORE_THREAD && !indexes_only)
1428 //              gui->resource_thread->stop_draw(!indexes_only);
1429
1430         if(mode != IGNORE_THREAD &&
1431                 !indexes_only &&
1432                 !gui->resource_thread->interrupted)
1433         {
1434                 printf("TrackCanvas::draw_resources %d: called without stopping ResourceThread\n",
1435                         __LINE__);
1436
1437                 BC_Signals::dump_stack();
1438         }
1439
1440         if(debug) PRINT_TRACE
1441
1442         resource_timer->update();
1443
1444         if(!indexes_only) {
1445 // Age resource pixmaps for deletion
1446                 for(int i = 0; i < gui->resource_pixmaps.total; i++)
1447                         gui->resource_pixmaps.values[i]->visible--;
1448         }
1449         if(debug) PRINT_TRACE
1450         if(mode != IGNORE_THREAD)
1451                 gui->resource_thread->reset(pane->number, indexes_only);
1452
1453 // Search every edit
1454         for(Track *current = mwindow->edl->tracks->first;
1455                 current;
1456                 current = NEXT)
1457         {
1458                 if( current->is_hidden() ) continue;
1459                 if(debug) PRINT_TRACE
1460                 for(Edit *edit = current->edits->first; edit; edit = edit->next)
1461                 {
1462                         if(debug) PRINT_TRACE
1463                         if( current->data_type != TRACK_SUBTITLE )
1464                                 if(!edit->asset && !edit->nested_edl) continue;
1465                         if(indexes_only)
1466                         {
1467                                 if(edit->track->data_type != TRACK_AUDIO) continue;
1468
1469                                 if(edit->nested_edl &&
1470                                         strcmp(indexable->path, edit->nested_edl->path)) continue;
1471
1472                                 if(edit->asset &&
1473                                         strcmp(indexable->path, edit->asset->path)) continue;
1474                         }
1475
1476                         if(debug) PRINT_TRACE
1477
1478                         int64_t edit_x, edit_y, edit_w, edit_h;
1479                         edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h);
1480
1481 // Edit is visible
1482                         if(MWindowGUI::visible(edit_x, edit_x + edit_w, 0, get_w()) &&
1483                                 MWindowGUI::visible(edit_y, edit_y + edit_h, 0, get_h()))
1484                         {
1485                                 int64_t pixmap_x, pixmap_w, pixmap_h;
1486                                 if(debug) PRINT_TRACE
1487
1488 // Search for existing pixmap containing edit
1489                                 for(int i = 0; i < gui->resource_pixmaps.total; i++)
1490                                 {
1491                                         ResourcePixmap* pixmap = gui->resource_pixmaps.values[i];
1492 // Same pointer can be different edit if editing took place
1493                                         if(pixmap->edit_id == edit->id &&
1494                                                 pixmap->pane_number == pane->number)
1495                                         {
1496                                                 pixmap->visible = 1;
1497                                                 break;
1498                                         }
1499                                 }
1500                                 if(debug) PRINT_TRACE
1501
1502 // Get new size, offset of pixmap needed
1503                                 get_pixmap_size(edit,
1504                                         edit_x,
1505                                         edit_w,
1506                                         pixmap_x,
1507                                         pixmap_w,
1508                                         pixmap_h);
1509                                 if(debug) PRINT_TRACE
1510
1511 // Draw new data
1512                                 if(pixmap_w && pixmap_h)
1513                                 {
1514 // Create pixmap if it doesn't exist
1515                                         ResourcePixmap* pixmap = create_pixmap(edit, edit_x,
1516                                                 pixmap_x, pixmap_w, pixmap_h);
1517 // Resize it if it's bigger
1518                                         if( pixmap_w > pixmap->pixmap_w ||
1519                                             pixmap_h > pixmap->pixmap_h)
1520                                                 pixmap->resize(pixmap_w, pixmap_h);
1521                                         pixmap->update_settings(edit, edit_x, edit_w,
1522                                                 pixmap_x, pixmap_w, pixmap_h);
1523 // Draw data
1524                                         if( current->show_assets() )
1525                                                 pixmap->draw_data(this,
1526                                                         edit, edit_x, edit_w,
1527                                                         pixmap_x, pixmap_w, pixmap_h,
1528                                                         mode, indexes_only);
1529                                         else {
1530                                                 set_bg_color(BLACK);
1531                                                 clear_box(0,0, pixmap_w,pixmap_h, pixmap);
1532                                         }
1533 // Draw title
1534                                         if( current->show_titles() )
1535                                                 pixmap->draw_title(this,
1536                                                         edit, edit_x, edit_w,
1537                                                         pixmap_x, pixmap_w);
1538 // Resize it if it's smaller
1539                                         if(pixmap_w < pixmap->pixmap_w ||
1540                                                 pixmap_h < pixmap->pixmap_h)
1541                                                 pixmap->resize(pixmap_w, pixmap_h);
1542
1543 // Copy pixmap to background canvas
1544                                         background_pixmap->draw_pixmap(pixmap,
1545                                                 pixmap->pixmap_x,
1546                                                 current->y_pixel - mwindow->edl->local_session->track_start[pane->number],
1547                                                 pixmap->pixmap_w,
1548                                                 edit_h);
1549                                 }
1550                                 if(debug) PRINT_TRACE
1551
1552                         }
1553                 }
1554                 int64_t track_x, track_y, track_w, track_h;
1555                 track_dimensions(current,
1556                         track_x, track_y, track_w, track_h);
1557                 set_color((~get_resources()->get_bg_color()) & 0xffffff);
1558                 set_opaque();
1559                 int x1 = track_x, x2 = x1+track_w;
1560                 int y1 = track_y+track_h-1;
1561                 draw_line(x1,y1, x2,y1, background_pixmap);
1562         }
1563
1564
1565 // Delete unused pixmaps
1566         if(debug) PRINT_TRACE
1567         if(!indexes_only)
1568                 for(int i = gui->resource_pixmaps.total - 1; i >= 0; i--)
1569                         if(gui->resource_pixmaps.values[i]->visible < PIXMAP_AGE)
1570                         {
1571 //printf("TrackCanvas::draw_resources %d\n", __LINE__);
1572                                 delete gui->resource_pixmaps.values[i];
1573                                 gui->resource_pixmaps.remove(gui->resource_pixmaps.values[i]);
1574                         }
1575         if(debug) PRINT_TRACE
1576
1577         if(hourglass_enabled)
1578         {
1579                 stop_hourglass();
1580                 hourglass_enabled = 0;
1581         }
1582         if(debug) PRINT_TRACE
1583
1584 // can't stop thread here, because this is called for every pane
1585 //      if(mode != IGNORE_THREAD && !indexes_only)
1586 //              gui->resource_thread->start_draw();
1587         if(debug) PRINT_TRACE
1588
1589
1590 }
1591
1592 ResourcePixmap* TrackCanvas::create_pixmap(Edit *edit,
1593         int64_t edit_x,
1594         int64_t pixmap_x,
1595         int64_t pixmap_w,
1596         int64_t pixmap_h)
1597 {
1598         ResourcePixmap *result = 0;
1599
1600         for(int i = 0; i < gui->resource_pixmaps.total; i++)
1601         {
1602 //printf("TrackCanvas::create_pixmap 1 %d %d\n", edit->id, resource_pixmaps.values[i]->edit->id);
1603                 if(gui->resource_pixmaps.values[i]->edit_id == edit->id &&
1604                         gui->resource_pixmaps.values[i]->pane_number == pane->number)
1605                 {
1606                         result = gui->resource_pixmaps.values[i];
1607                         break;
1608                 }
1609         }
1610
1611         if(!result)
1612         {
1613 //SET_TRACE
1614                 result = new ResourcePixmap(mwindow, gui, edit, pane->number, pixmap_w, pixmap_h);
1615                 set_bg_color(BLACK);
1616                 clear_box(0,0, pixmap_w,pixmap_h, result);
1617 //SET_TRACE
1618                 gui->resource_pixmaps.append(result);
1619         }
1620
1621 //      result->resize(pixmap_w, pixmap_h);
1622         return result;
1623 }
1624
1625 void TrackCanvas::get_pixmap_size(Edit *edit,
1626         int64_t edit_x,
1627         int64_t edit_w,
1628         int64_t &pixmap_x,
1629         int64_t &pixmap_w,
1630         int64_t &pixmap_h)
1631 {
1632
1633 // Align x on frame boundaries
1634
1635
1636 //      switch(edit->edits->track->data_type)
1637 //      {
1638 //              case TRACK_AUDIO:
1639
1640                         pixmap_x = edit_x;
1641                         pixmap_w = edit_w;
1642                         if(pixmap_x < 0)
1643                         {
1644                                 pixmap_w -= -edit_x;
1645                                 pixmap_x = 0;
1646                         }
1647
1648                         if(pixmap_x + pixmap_w > get_w())
1649                         {
1650                                 pixmap_w = get_w() - pixmap_x;
1651                         }
1652
1653 //                      break;
1654 //
1655 //              case TRACK_VIDEO:
1656 //              {
1657 //                      int64_t picon_w = (int64_t)(edit->picon_w() + 0.5);
1658 //                      int64_t frame_w = (int64_t)(edit->frame_w() + 0.5);
1659 //                      int64_t pixel_increment = MAX(picon_w, frame_w);
1660 //                      int64_t pixmap_x1 = edit_x;
1661 //                      int64_t pixmap_x2 = edit_x + edit_w;
1662 //
1663 //                      if(pixmap_x1 < 0)
1664 //                      {
1665 //                              pixmap_x1 = (int64_t)((double)-edit_x / pixel_increment) *
1666 //                                      pixel_increment +
1667 //                                      edit_x;
1668 //                      }
1669 //
1670 //                      if(pixmap_x2 > get_w())
1671 //                      {
1672 //                              pixmap_x2 = (int64_t)((double)(get_w() - edit_x) / pixel_increment + 1) *
1673 //                                      pixel_increment +
1674 //                                      edit_x;
1675 //                      }
1676 //                      pixmap_x = pixmap_x1;
1677 //                      pixmap_w = pixmap_x2 - pixmap_x1;
1678 //                      break;
1679 //              }
1680 //      }
1681
1682         Track *track = edit->edits->track;
1683         pixmap_h = track->data_h;
1684         if( track->show_titles() )
1685                 pixmap_h += mwindow->theme->get_image("title_bg_data")->get_h();
1686 //printf("get_pixmap_size %d %d %d %d\n", edit_x, edit_w, pixmap_x, pixmap_w);
1687 }
1688
1689 void TrackCanvas::edit_dimensions(Edit *edit,
1690         int64_t &x, int64_t &y, int64_t &w, int64_t &h)
1691 {
1692         x = Units::round(edit->track->from_units(edit->startproject) *
1693                         mwindow->edl->session->sample_rate /
1694                         mwindow->edl->local_session->zoom_sample -
1695                         mwindow->edl->local_session->view_start[pane->number]);
1696
1697         y = edit->edits->track->y_pixel - mwindow->edl->local_session->track_start[pane->number];
1698
1699 // Method for calculating w so when edits are together we never get off by one error due to rounding
1700         int64_t x_next = Units::round(edit->track->from_units(edit->startproject + edit->length) *
1701                         mwindow->edl->session->sample_rate /
1702                         mwindow->edl->local_session->zoom_sample -
1703                         mwindow->edl->local_session->view_start[pane->number]);
1704         w = x_next - x;
1705
1706         int edit_h = 0;
1707         if( edit->track->show_titles() )
1708                 edit_h += mwindow->theme->get_image("title_bg_data")->get_h();
1709         if( edit->track->show_assets() )
1710                 edit_h += edit->track->data_h;
1711         h = edit_h;
1712 }
1713
1714 void TrackCanvas::track_dimensions(Track *track, int64_t &x, int64_t &y, int64_t &w, int64_t &h)
1715 {
1716         x = 0;
1717         w = get_w();
1718         y = track->y_pixel - mwindow->edl->local_session->track_start[pane->number];
1719         h = track->vertical_span(mwindow->theme);
1720 }
1721
1722
1723 void TrackCanvas::draw_paste_destination()
1724 {
1725         //int cursor_x = get_relative_cursor_x();
1726         //int cursor_y = get_relative_cursor_y();
1727         int current_atrack = 0;
1728         int current_vtrack = 0;
1729         int current_aedit = 0;
1730         int current_vedit = 0;
1731         int64_t w = 0;
1732         int64_t x;
1733         double position;
1734         int insertion  = 0;
1735
1736 //if(pane->number == BOTTOM_RIGHT_PANE)
1737 //printf("TrackCanvas::draw_paste_destination %d %p\n", __LINE__, mwindow->session->track_highlighted);
1738
1739         if((mwindow->session->current_operation == DRAG_ASSET &&
1740                         (mwindow->session->drag_assets->total ||
1741                         mwindow->session->drag_clips->total)) ||
1742                 (mwindow->session->current_operation == DRAG_EDIT &&
1743                         mwindow->session->drag_edits->total)) {
1744                 Indexable *indexable = 0;
1745                 EDL *clip = 0;
1746                 //int draw_box = 0;
1747
1748                 if(mwindow->session->current_operation == DRAG_ASSET &&
1749                         mwindow->session->drag_assets->size())
1750                         indexable = mwindow->session->drag_assets->get(0);
1751
1752                 if(mwindow->session->current_operation == DRAG_ASSET &&
1753                         mwindow->session->drag_clips->size())
1754                         clip = mwindow->session->drag_clips->get(0);
1755
1756                 int has_audio = 0, has_video = 0;
1757                 double paste_audio_length = 0, paste_video_length = 0;
1758                 double paste_audio_position = -1, paste_video_position = -1;
1759
1760                 if( indexable ) {
1761                         has_audio = indexable->have_audio();
1762                         paste_audio_length = !has_audio ? 0 :
1763                                  (double)indexable->get_audio_samples() / indexable->get_sample_rate();
1764                         has_video = indexable->have_video();
1765                         Asset *asset = indexable->is_asset ? (Asset *)indexable : 0;
1766                         // Images have length -1
1767                         if( asset && asset->video_data && asset->video_length < 0 ) {
1768                                 paste_video_length = mwindow->edl->session->si_useduration ?
1769                                         mwindow->edl->session->si_duration / asset->frame_rate :
1770                                         1.0 / asset->frame_rate ;  // 1 frame
1771                         }
1772                         else
1773                                 paste_video_length = !has_video ? 0 :
1774                                         (double)indexable->get_video_frames() / indexable->get_frame_rate();
1775                 }
1776
1777                 if( clip ) {
1778                         has_audio = clip->tracks->total_audio_tracks() > 0 ? 1 : 0;
1779                         paste_audio_length = !has_audio ? 0 : clip->tracks->total_audio_length();
1780                         has_video = clip->tracks->total_video_tracks() > 0 ? 1 : 0;
1781                         paste_video_length = !has_video ? 0 : clip->tracks->total_video_length();
1782                 }
1783
1784 // 'Align cursor on frame' lengths calculations
1785                 if( mwindow->edl->session->cursor_on_frames ) {
1786                         double fps = mwindow->edl->session->frame_rate;
1787                         double aud = floor(paste_audio_length * fps) / fps;
1788                         double vid = floor(paste_video_length * fps) / fps;
1789                         double length = has_video && has_audio ?
1790                                         aud < vid ? aud : vid :
1791                                 has_video ? vid :
1792                                 has_audio ? aud : 0;
1793                         paste_video_length = paste_audio_length = length;
1794                 }
1795
1796                 if( has_audio ) { // we use video if we are over video and audio if we are over audio
1797                         int64_t length = 0;
1798                         switch( mwindow->session->track_highlighted->data_type ) {
1799                         case TRACK_VIDEO: length = paste_video_length;  break;
1800                         case TRACK_AUDIO: length = paste_audio_length;  break;
1801                         }
1802                         int64_t asset_length = mwindow->session->track_highlighted->
1803                                 to_units(length, 1);
1804                         paste_audio_position = mwindow->session->track_highlighted->
1805                                 from_units(drop_edit_position(&insertion, NULL, asset_length));
1806                 }
1807                 if( has_video ) {
1808                         int64_t asset_length = mwindow->session->track_highlighted->
1809                                 to_units(paste_video_length, 1);
1810                         paste_video_position = mwindow->session->track_highlighted->
1811                                 from_units(drop_edit_position(&insertion, NULL, asset_length));
1812                 }
1813
1814 // Get destination track
1815                 for(Track *dest = mwindow->session->track_highlighted;
1816                         dest;
1817                         dest = dest->next) {
1818                         if( dest->is_hidden() ) continue;
1819                         if(dest->is_armed()) {
1820 // Get source width in pixels
1821                                 w = -1;
1822 // Use start of highlighted edit
1823                                 if(mwindow->session->edit_highlighted) {
1824                                         position = mwindow->session->track_highlighted->from_units(
1825                                                 mwindow->session->edit_highlighted->startproject);
1826                                 }
1827                                 else {
1828 // Use end of highlighted track, disregarding effects
1829                                         position = mwindow->session->track_highlighted->from_units(
1830                                                 mwindow->session->track_highlighted->edits->length());
1831                                 }
1832
1833 // Get the x coordinate
1834                                 x = Units::to_int64(position *
1835                                         mwindow->edl->session->sample_rate /
1836                                         mwindow->edl->local_session->zoom_sample) -
1837                                         mwindow->edl->local_session->view_start[pane->number];
1838
1839                                 double paste_position = -1.;
1840                                 if(dest->data_type == TRACK_AUDIO) {
1841                                         if(indexable && current_atrack < indexable->get_audio_channels()) {
1842 //printf("TrackCanvas::draw_paste_destination %d %d\n", __LINE__, current_atrack);
1843                                                 w = Units::to_int64((double)indexable->get_audio_samples() /
1844                                                         indexable->get_sample_rate() *
1845                                                         mwindow->edl->session->sample_rate /
1846                                                         mwindow->edl->local_session->zoom_sample);
1847                                                 paste_position = paste_audio_position;
1848                                         }
1849                                         else if(clip && current_atrack < clip->tracks->total_audio_tracks()) {
1850                                                 w = Units::to_int64((double)clip->tracks->total_length() *
1851                                                         mwindow->edl->session->sample_rate /
1852                                                         mwindow->edl->local_session->zoom_sample);
1853                                                 paste_position = paste_audio_position;
1854 //printf("draw_paste_destination %d\n", x);
1855                                         }
1856                                         else if(mwindow->session->current_operation == DRAG_EDIT &&
1857                                                 current_aedit < mwindow->session->drag_edits->total) {
1858                                                 Edit *edit;
1859                                                 while(current_aedit < mwindow->session->drag_edits->total &&
1860                                                         mwindow->session->drag_edits->values[current_aedit]->track->data_type != TRACK_AUDIO)
1861                                                         current_aedit++;
1862
1863                                                 if(current_aedit < mwindow->session->drag_edits->total) {
1864                                                         edit = mwindow->session->drag_edits->values[current_aedit];
1865                                                         w = Units::to_int64(edit->length / mwindow->edl->local_session->zoom_sample);
1866                                                         paste_position = mwindow->session->track_highlighted->
1867                                                                 from_units(drop_edit_position(&insertion,
1868                                                                         mwindow->session->drag_edit,
1869                                                                         mwindow->session->drag_edit->length));
1870                                                         current_aedit++;
1871                                                 }
1872                                         }
1873                                         if( paste_position >= 0 ) {
1874                                                 position = paste_position;
1875                                                 current_atrack++;
1876                                                 //draw_box = 1;
1877                                         }
1878                                         else
1879                                                 w = -1;
1880                                 }
1881                                 else if(dest->data_type == TRACK_VIDEO) {
1882 //printf("draw_paste_destination 1\n");
1883                                         if(indexable && current_vtrack < indexable->get_video_layers())
1884                                         {
1885                                                 w = Units::to_int64((double)indexable->get_video_frames() /
1886                                                         indexable->get_frame_rate() *
1887                                                         mwindow->edl->session->sample_rate /
1888                                                         mwindow->edl->local_session->zoom_sample);
1889                                                 paste_position = paste_video_position;
1890                                         }
1891                                         else if(clip && current_vtrack < clip->tracks->total_video_tracks()) {
1892                                                 w = Units::to_int64(clip->tracks->total_length() *
1893                                                         mwindow->edl->session->sample_rate /
1894                                                         mwindow->edl->local_session->zoom_sample);
1895                                                 paste_position = paste_video_position;
1896                                         }
1897                                         else if(mwindow->session->current_operation == DRAG_EDIT &&
1898                                                 current_vedit < mwindow->session->drag_edits->total) {
1899                                                 Edit *edit;
1900                                                 while(current_vedit < mwindow->session->drag_edits->total &&
1901                                                         mwindow->session->drag_edits->values[current_vedit]->track->data_type != TRACK_VIDEO)
1902                                                         current_vedit++;
1903
1904                                                 if(current_vedit < mwindow->session->drag_edits->total) {
1905                                                         edit = mwindow->session->drag_edits->values[current_vedit];
1906                                                         w = Units::to_int64(edit->track->from_units(edit->length) *
1907                                                                 mwindow->edl->session->sample_rate /
1908                                                                 mwindow->edl->local_session->zoom_sample);
1909                                                         paste_position = mwindow->session->track_highlighted->
1910                                                                 from_units(drop_edit_position(&insertion,
1911                                                                         mwindow->session->drag_edit,
1912                                                                         mwindow->session->drag_edit->length));
1913                                                         current_vedit++;
1914                                                 }
1915                                         }
1916                                         if( paste_position >= 0 ) {
1917                                                 position = paste_position;
1918                                                 current_vtrack++;
1919                                                 //draw_box = 1;
1920                                         }
1921                                         else
1922                                                 w = -1;
1923                                 }
1924
1925                                 if(w >= 0) {
1926 // Get the x coordinate
1927                                         x = Units::to_int64(position *
1928                                                 mwindow->edl->session->sample_rate /
1929                                                 mwindow->edl->local_session->zoom_sample) -
1930                                                 mwindow->edl->local_session->view_start[pane->number];
1931                                         int y = dest->y_pixel - mwindow->edl->local_session->track_start[pane->number];
1932                                         int h = dest->vertical_span(mwindow->theme);
1933
1934 //printf("TrackCanvas::draw_paste_destination 2 %d %d %d %d\n", x, y, w, h);
1935                                         if(x < -BC_INFINITY) {
1936                                                 w -= -BC_INFINITY - x;
1937                                                 x += -BC_INFINITY - x;
1938                                         }
1939                                         w = MIN(65535, w);
1940 // if(pane->number == TOP_RIGHT_PANE)
1941 // printf("TrackCanvas::draw_paste_destination %d %d %d %d %d\n",
1942 // __LINE__, x, y, w, h);
1943                                         if (insertion)
1944                                                 draw_highlight_insertion(x, y, w, h);
1945                                         else
1946                                                 draw_highlight_rectangle(x, y, w, h);
1947                                 }
1948                         }
1949                 }
1950         }
1951 }
1952
1953 void TrackCanvas::plugin_dimensions(Plugin *plugin, int64_t &x, int64_t &y, int64_t &w, int64_t &h)
1954 {
1955         x = Units::round(plugin->track->from_units(plugin->startproject) *
1956                 mwindow->edl->session->sample_rate /
1957                 mwindow->edl->local_session->zoom_sample -
1958                 mwindow->edl->local_session->view_start[pane->number]);
1959         w = Units::round(plugin->track->from_units(plugin->length) *
1960                 mwindow->edl->session->sample_rate /
1961                 mwindow->edl->local_session->zoom_sample);
1962         y = plugin->track->y_pixel
1963                 - mwindow->edl->local_session->track_start[pane->number];
1964         if( plugin->track->show_titles() )
1965                 y += mwindow->theme->get_image("title_bg_data")->get_h();
1966         if( plugin->track->show_assets() )
1967                 y += plugin->track->data_h;
1968         y += plugin->plugin_set->get_number() *
1969                         mwindow->theme->get_image("plugin_bg_data")->get_h();
1970         h = mwindow->theme->get_image("plugin_bg_data")->get_h();
1971 }
1972
1973
1974 void TrackCanvas::draw_highlight_rectangle(int x, int y, int w, int h)
1975 {
1976
1977 // if we have to draw a highlighted rectangle completely on the left or completely on the right of the viewport,
1978 // just draw arrows, so user has indication that something is there
1979 // FIXME: get better colors
1980
1981         if (x + w <= 0)
1982         {
1983                 draw_triangle_left(0, y + h /6,
1984                         h * 2/3, h * 2/3, BLACK, GREEN, YELLOW, RED, BLUE);
1985                 return;
1986         } else
1987         if (x >= get_w())
1988         {
1989                 draw_triangle_right(get_w() - h * 2/3, y + h /6,
1990                         h * 2/3, h * 2/3, BLACK, GREEN, YELLOW, RED, BLUE);
1991                 return;
1992         }
1993
1994 // Fix bug in heroines & cvs version as of 22.8.2005:
1995 // If we grab when zoomed in and zoom out while dragging, when edit gets really narrow strange things start happening
1996         if (w >= 0 && w < 3) {x -= w /2; w = 3;};
1997         if(x < -10) { w += x - -10;  x = -10; }
1998         if(y < -10) { h += y - -10;  y = -10; }
1999
2000         w = MIN(w, get_w() + 20);
2001         h = MIN(h, get_h() + 20);
2002         if(w > 0 && h > 0)
2003         {
2004                 set_color(mwindow->preferences->highlight_inverse);
2005                 set_inverse();
2006                 //draw_rectangle(x, y, w, h);
2007                 draw_rectangle(x + 1, y + 1, w - 2, h - 2);
2008                 draw_rectangle(x + 2, y + 2, w - 4, h - 4);
2009                 set_opaque();
2010                 draw_rectangle(x, y, w, h);
2011         }
2012 //if(pane->number == TOP_RIGHT_PANE)
2013 //printf("TrackCanvas::draw_highlight_rectangle %d %d %d %d %d\n", __LINE__, x, y, w, h);
2014 }
2015
2016 void TrackCanvas::draw_highlight_insertion(int x, int y, int w, int h)
2017 {
2018
2019 // if we have to draw a highlighted rectangle completely on the left or completely on the right of the viewport,
2020 // just draw arrows, so user has indication that something is there
2021 // FIXME: get better colors
2022
2023
2024
2025         int h1 = h / 8;
2026         int h2 = h / 4;
2027
2028         set_inverse();
2029
2030 /* these don't look so good
2031
2032         draw_line(x, y, x, y+h);
2033         draw_line(x - h2 * 2, y + h1*2,   x - h2, y+h1*2);
2034         draw_line(x - h2 * 2, y + h1*2+1, x - h2, y+h1*2+1);
2035         draw_line(x - h2 * 2, y + h1*6,   x - h2, y+h1*6);
2036         draw_line(x - h2 * 2, y + h1*6+1, x - h2, y+h1*6+1);
2037 */
2038         draw_triangle_right(x - h2, y + h1, h2, h2, BLACK, GREEN, YELLOW, RED, BLUE);
2039         draw_triangle_right(x - h2, y + h1*5, h2, h2, BLACK, GREEN, YELLOW, RED, BLUE);
2040
2041 /*      draw_line(x + h2 * 2, y + h1*2,   x + h2, y+h1*2);
2042         draw_line(x + h2 * 2, y + h1*2+1, x + h2, y+h1*2+1);
2043         draw_line(x + h2 * 2, y + h1*6,   x + h2, y+h1*6);
2044         draw_line(x - h2 * 2, y + h1*6+1, x + h2, y+h1*6+1);
2045 */
2046         draw_triangle_left(x, y + h1, h2, h2, BLACK, GREEN, YELLOW, RED, BLUE);
2047         draw_triangle_left(x, y + h1*5, h2, h2, BLACK, GREEN, YELLOW, RED, BLUE);
2048
2049 // draw the box centred around x
2050         x -= w / 2;
2051 // Fix bug in heroines & cvs version as of 22.8.2005:
2052 // If we grab when zoomed in and zoom out while dragging, when edit gets really narrow strange things start happening
2053         if (w >= 0 && w < 3) {x -= w /2; w = 3;};
2054         if(x < -10) { w += x - -10;  x = -10; }
2055         if(y < -10) { h += y - -10;  y = -10; }
2056         w = MIN(w, get_w() + 20);
2057         h = MIN(h, get_h() + 20);
2058         set_color(mwindow->preferences->highlight_inverse);
2059         set_inverse();
2060         draw_rectangle(x, y, w, h);
2061         draw_rectangle(x + 1, y + 1, w - 2, h - 2);
2062         set_opaque();
2063 //printf("TrackCanvas::draw_highlight_insertion %d %d %d %d\n", x, y, w, h);
2064 }
2065
2066 void TrackCanvas::draw_playback_cursor()
2067 {
2068 // Called before playback_cursor exists
2069 //      if(mwindow->playback_cursor && mwindow->playback_cursor->visible)
2070 //      {
2071 //              mwindow->playback_cursor->visible = 0;
2072 //              mwindow->playback_cursor->draw();
2073 //      }
2074 }
2075
2076 void TrackCanvas::get_handle_coords(Edit *edit, int64_t &x, int64_t &y, int64_t &w, int64_t &h, int side)
2077 {
2078         int handle_w = mwindow->theme->edithandlein_data[0]->get_w();
2079         int handle_h = mwindow->theme->edithandlein_data[0]->get_h();
2080
2081         edit_dimensions(edit, x, y, w, h);
2082
2083         if( edit->track->show_titles() )
2084                 y += mwindow->theme->get_image("title_bg_data")->get_h();
2085
2086         if(side == EDIT_OUT)
2087                 x += w - handle_w;
2088
2089         h = handle_h;
2090         w = handle_w;
2091 }
2092
2093 void TrackCanvas::get_transition_coords(Edit *edit,
2094                 int64_t &x, int64_t &y, int64_t &w, int64_t &h)
2095 {
2096         int transition_w = xS(30), transition_h = yS(30);
2097         int has_titles = edit->track->show_titles();
2098         int has_assets = edit->track->show_assets();
2099         double title_bg_h = mwindow->theme->get_image("title_bg_data")->get_h();
2100         int data_h = edit->track->data_h;
2101         double ys = has_assets ? data_h : has_titles ? title_bg_h : 0;
2102         double dy = has_titles ?
2103                 ( has_assets ? title_bg_h + data_h/2 : title_bg_h/2 ) :
2104                 ( has_assets ? data_h/2 : 0) ;
2105         double title_h = mwindow->theme->title_h;
2106         if( dy < title_h / 2 ) { ys = title_h;  dy = ys / 2; }
2107         y += dy;
2108
2109         x -= transition_w / 2;
2110         y -= transition_h / 2;
2111         w = transition_w;
2112         h = transition_h;
2113 }
2114
2115 void TrackCanvas::draw_highlighting()
2116 {
2117         int64_t x, y, w, h;
2118         int draw_box = 0;
2119
2120         switch(mwindow->session->current_operation)
2121         {
2122                 case DRAG_ATRANSITION:
2123                 case DRAG_VTRANSITION:
2124 //printf("TrackCanvas::draw_highlighting 1 %p %p\n",
2125 //      mwindow->session->track_highlighted, mwindow->session->edit_highlighted);
2126                         if(mwindow->session->edit_highlighted) {
2127                                 if((mwindow->session->current_operation == DRAG_ATRANSITION &&
2128                                         mwindow->session->track_highlighted->data_type == TRACK_AUDIO) ||
2129                                         (mwindow->session->current_operation == DRAG_VTRANSITION &&
2130                                         mwindow->session->track_highlighted->data_type == TRACK_VIDEO)) {
2131                                         edit_dimensions(mwindow->session->edit_highlighted,
2132                                                 x, y, w, h);
2133                                         if(MWindowGUI::visible(x, x + w, 0, get_w()) &&
2134                                                 MWindowGUI::visible(y, y + h, 0, get_h())) {
2135                                                 draw_box = 1;
2136                                                 get_transition_coords(mwindow->session->edit_highlighted,
2137                                                         x, y, w, h);
2138                                         }
2139                                 }
2140                         }
2141                         break;
2142
2143 // Dragging a new effect from the Resource window
2144                 case DRAG_AEFFECT:
2145                 case DRAG_VEFFECT:
2146                         if(mwindow->session->track_highlighted &&
2147                                 ((mwindow->session->current_operation == DRAG_AEFFECT &&
2148                                   mwindow->session->track_highlighted->data_type == TRACK_AUDIO) ||
2149                                  (mwindow->session->current_operation == DRAG_VEFFECT &&
2150                                   mwindow->session->track_highlighted->data_type == TRACK_VIDEO))) {
2151 // Put it before another plugin
2152                                 if(mwindow->session->plugin_highlighted) {
2153                                         plugin_dimensions(mwindow->session->plugin_highlighted,
2154                                                 x, y, w, h);
2155 //printf("TrackCanvas::draw_highlighting 1 %d %d\n", x, w);
2156                                 }
2157 // Put it after a plugin set
2158                                 else if( mwindow->session->pluginset_highlighted &&
2159                                          mwindow->session->pluginset_highlighted->last ) {
2160                                         plugin_dimensions((Plugin*)mwindow->session->pluginset_highlighted->last,
2161                                                 x, y, w, h);
2162 //printf("TrackCanvas::draw_highlighting 1 %d %d\n", x, w);
2163                                         int64_t track_x, track_y, track_w, track_h;
2164                                         track_dimensions(mwindow->session->track_highlighted,
2165                                                 track_x, track_y, track_w, track_h);
2166
2167                                         x += w;
2168                                         w = Units::round(
2169                                                         mwindow->session->track_highlighted->get_length() *
2170                                                         mwindow->edl->session->sample_rate /
2171                                                         mwindow->edl->local_session->zoom_sample -
2172                                                         mwindow->edl->local_session->view_start[pane->number]) -
2173                                                 x;
2174 //printf("TrackCanvas::draw_highlighting 2 %d\n", w);
2175                                         if(w <= 0) w = track_w;
2176                                 }
2177                                 else {
2178                                         track_dimensions(mwindow->session->track_highlighted,
2179                                                 x, y, w, h);
2180
2181 //printf("TrackCanvas::draw_highlighting 1 %d %d %d %d\n", x, y, w, h);
2182 // Put it in a new plugin set determined by the selected range
2183                                         if(mwindow->edl->local_session->get_selectionend() >
2184                                                 mwindow->edl->local_session->get_selectionstart()) {
2185                                                 x = Units::to_int64(mwindow->edl->local_session->get_selectionstart() *
2186                                                         mwindow->edl->session->sample_rate /
2187                                                         mwindow->edl->local_session->zoom_sample -
2188                                                         mwindow->edl->local_session->view_start[pane->number]);
2189                                                 w = Units::to_int64((mwindow->edl->local_session->get_selectionend() -
2190                                                         mwindow->edl->local_session->get_selectionstart()) *
2191                                                         mwindow->edl->session->sample_rate /
2192                                                         mwindow->edl->local_session->zoom_sample);
2193                                         }
2194 // Put it in a new plugin set determined by an edit boundary
2195                                         else if(mwindow->session->edit_highlighted) {
2196                                                 int64_t temp_y, temp_h;
2197                                                 edit_dimensions(mwindow->session->edit_highlighted,
2198                                                         x, temp_y, w, temp_h);
2199                                         }
2200 // Put it at the beginning of the track in a new plugin set
2201                                 }
2202
2203                                 if( MWindowGUI::visible(x, x + w, 0, get_w()) &&
2204                                     MWindowGUI::visible(y, y + h, 0, get_h()) ) {
2205 //printf("TrackCanvas::draw_highlighting 1\n");
2206                                         draw_box = 1;
2207                                 }
2208                         }
2209                         break;
2210
2211                 case DRAG_ASSET:
2212                         if(mwindow->session->track_highlighted) {
2213 //                              track_dimensions(mwindow->session->track_highlighted, x, y, w, h);
2214
2215 //                              if(MWindowGUI::visible(y, y + h, 0, get_h()))
2216 //                              {
2217                                         draw_paste_destination();
2218 //                              }
2219                         }
2220                         break;
2221
2222                 case DRAG_EDIT:
2223                         if(mwindow->session->track_highlighted) {
2224 //                              track_dimensions(mwindow->session->track_highlighted, x, y, w, h);
2225 //
2226 //                              if(MWindowGUI::visible(y, y + h, 0, get_h())) {
2227                                         draw_paste_destination();
2228 //                              }
2229                         }
2230                         break;
2231                 case DRAG_GROUP:
2232                         if( mwindow->session->track_highlighted && mwindow->session->drag_group &&
2233                             mwindow->session->drag_group_edit && mwindow->session->drag_group_first_track ) {
2234                                 Track *track = mwindow->session->track_highlighted;
2235                                 EDL *drag_group = mwindow->session->drag_group;
2236                                 Track *edit_track = mwindow->session->drag_group_edit->track;
2237                                 Track *drag_track = mwindow->session->drag_group_first_track;
2238                                 while( track && edit_track && edit_track != drag_track ) {
2239                                         edit_track = edit_track->previous;
2240                                         track = track->previous;
2241                                 }
2242                                 int cx = get_cursor_x();
2243                                 double cur_pos = mwindow->session->drag_group_position;
2244                                 double new_pos = mwindow->edl->get_cursor_position(cx, pane->number);
2245                                 new_pos -= mwindow->session->drag_position - cur_pos;
2246                                 new_pos = mwindow->edl->align_to_frame(new_pos, 0);
2247                                 double drop_position = new_pos;
2248                                 int color = GREEN;
2249                                 int ret = test_track_group(drag_group, track, new_pos);
2250                                 if( ret < 0 )
2251                                         color = ORANGE;
2252                                 else if( !ret ) {
2253                                         if( new_pos != drop_position ) {
2254                                                 double pos = new_pos;
2255                                                 pos += mwindow->session->drag_position - cur_pos;
2256                                                 cx = mwindow->edl->get_position_cursorx(pos, pane->number);
2257                                                 ret = test_track_group(drag_group, track, new_pos);
2258                                         }
2259                                         color = ret > 0 ? YELLOW : shift_down() ? RED : BLUE;
2260                                 }
2261                                 track_dimensions(mwindow->session->track_highlighted, x, y, w, h);
2262                                 int dx = cx - mwindow->session->drag_origin_x;
2263                                 int dy = y - mwindow->session->drag_origin_y;
2264                                 draw_selected_edits(mwindow->edl, dx, dy, color, -1);
2265                         }
2266                         break;
2267 // Dragging an effect from the timeline
2268                 case DRAG_AEFFECT_COPY:
2269                 case DRAG_VEFFECT_COPY:
2270                         if( (mwindow->session->plugin_highlighted || mwindow->session->track_highlighted) &&
2271                             ((mwindow->session->current_operation == DRAG_AEFFECT_COPY &&
2272                               mwindow->session->track_highlighted->data_type == TRACK_AUDIO) ||
2273                              (mwindow->session->current_operation == DRAG_VEFFECT_COPY &&
2274                               mwindow->session->track_highlighted->data_type == TRACK_VIDEO))) {
2275
2276                                 int64_t plugin_position = -1;
2277                                 Plugin *drag_plugin = mwindow->session->drag_plugin;
2278                                 PluginSet *hi_plugin_set = mwindow->session->pluginset_highlighted;
2279                                 Track *hi_track = mwindow->session->track_highlighted;
2280                                 Edit *hi_edit = mwindow->session->edit_highlighted;
2281 // Put it into a silence zone
2282                                 if( hi_plugin_set && hi_plugin_set->last &&
2283                                     hi_plugin_set->track == drag_plugin->track ) {
2284                                         plugin_dimensions((Plugin *)hi_plugin_set->last, x, y, w, h);
2285                                         plugin_position = drop_plugin_position(hi_plugin_set, drag_plugin);
2286                                         hi_track = drag_plugin->track;
2287                                 }
2288                                 else if( hi_track ) {
2289 // Put it in a new plugin set determined by an edit boundary, or at the start of the track
2290                                         track_dimensions(hi_track, x, y, w, h);
2291                                         plugin_position = hi_edit ? hi_edit->startproject : 0;
2292                                 }
2293
2294                                 if( plugin_position < 0 ) break;
2295
2296 // Calculate length of plugin based on data type of track and units
2297                                 double zoom_scale = (double)mwindow->edl->session->sample_rate /
2298                                                 mwindow->edl->local_session->zoom_sample;
2299                                 x = Units::round(hi_track->from_units(plugin_position) * zoom_scale) -
2300                                                 mwindow->edl->local_session->view_start[pane->number];
2301                                 w = Units::round(hi_track->from_units(drag_plugin->length) * zoom_scale);
2302
2303                                 if( MWindowGUI::visible(x, x + w, 0, get_w()) &&
2304                                     MWindowGUI::visible(y, y + h, 0, get_h()) ) {
2305                                         draw_box = 1;
2306                                 }
2307                         }
2308                         break;
2309
2310                 case DRAG_PLUGINKEY:
2311                         if(mwindow->session->plugin_highlighted &&
2312                            mwindow->session->current_operation == DRAG_PLUGINKEY)
2313                         {
2314 // Just highlight the plugin
2315                                 plugin_dimensions(mwindow->session->plugin_highlighted, x, y, w, h);
2316
2317                                 if(MWindowGUI::visible(x, x + w, 0, get_w()) &&
2318                                         MWindowGUI::visible(y, y + h, 0, get_h()))
2319                                 {
2320                                         draw_box = 1;
2321                                 }
2322                         }
2323                         break;
2324
2325                 case DRAG_SPEED:
2326                         draw_speed_highlight();
2327                         break;
2328         }
2329
2330         if( draw_box )
2331                 draw_highlight_rectangle(x, y, w, h);
2332
2333         draw_selected_edits(mwindow->edl, 0, 0, GREEN+BLUE, RED);
2334 }
2335
2336 void TrackCanvas::draw_speed_highlight()
2337 {
2338         FloatAuto *drag_speed = (FloatAuto*)mwindow->session->drag_auto;
2339         if( !drag_speed ) return;
2340         draw_speed_track(drag_speed->autos->track);
2341         ArrayList<Auto*> &speed_gang = *mwindow->session->drag_auto_gang;
2342         for( int i=0, sz=speed_gang.size(); i<sz; ++i ) {
2343                 Track *track = speed_gang[i]->autos->track;
2344                 if( track->is_hidden() ) continue;
2345                 draw_speed_track(track);
2346         }
2347 }
2348
2349 void TrackCanvas::draw_speed_track(Track *track)
2350 {
2351         for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
2352                 int64_t x, y, w, h;
2353                 edit_dimensions(edit, x, y, w, h);
2354                 if( !MWindowGUI::visible(x, x + w, 0, get_w()) ) continue;
2355                 if( !MWindowGUI::visible(y, y + h, 0, get_h()) ) continue;
2356                 int color = 0xc00cc0;
2357                 set_color(color);
2358                 set_opaque();
2359                 draw_selected(x, y, w, h);
2360         }
2361 }
2362
2363 // x does not reliably draw a really big rectangle
2364 void TrackCanvas::draw_selected(int x, int y, int w, int h)
2365 {
2366         int x1 = bmax(x, 0), x2 = bmin(x+w, get_w());
2367         if( x1 > x2 ) return;
2368         int y1 = bmax(y, 0), y2 = bmin(y+h, get_h());
2369         if( y1 > y2 ) return;
2370         if( x   >= 0 && x   < get_w() ) draw_line(x,y1,   x,y2);
2371         if( x+w >= 0 && x+w < get_w() ) draw_line(x+w,y1, x+w,y2);
2372         if( y   >= 0 && y   < get_h() ) draw_line(x1,y,   x2,y);
2373         if( y+h >= 0 && y+h < get_h() ) draw_line(x1,y+h, x2,y+h);
2374 }
2375
2376 void TrackCanvas::draw_selected_edits(EDL *edl, int dx, int dy, int color0, int color1)
2377 {
2378         int dropping = 0;
2379         for( Track *track=edl->tracks->first; track; track=track->next ) {
2380                 if( track->is_hidden() ) continue;
2381                 if( !track->is_armed() && color1 < 0 ) {
2382                         if( dropping )
2383                                 dy -= track->vertical_span(mwindow->theme);
2384                         continue;
2385                 }
2386                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
2387                         if( !edit->is_selected ) continue;
2388                         dropping = 1;
2389                         int64_t x, y, w, h;
2390                         edit_dimensions(edit, x, y, w, h);
2391                         x += dx;  y += dy;
2392                         if( !MWindowGUI::visible(x, x + w, 0, get_w()) ) continue;
2393                         if( !MWindowGUI::visible(y, y + h, 0, get_h()) ) continue;
2394                         set_opaque();
2395                         int inner = color1 < 0 ? color0 : !edit->group_id ? color0 :
2396                                 mwindow->get_group_color(edit->group_id);
2397                         int outer = color1 < 0 ? color0 : !edit->group_id ? color1 : inner;
2398                         set_color(track->is_armed() ? inner : outer);
2399                         draw_selected(x, y, w, h);
2400                         set_color(track->is_armed() ? outer : inner);
2401                         draw_selected(x-1, y-1, w+2, h+2);
2402                         draw_selected(x-2, y-2, w+1, h+1);
2403                 }
2404         }
2405 }
2406
2407 void TrackCanvas::draw_plugins()
2408 {
2409         char string[BCTEXTLEN];
2410         int current_on = 0;
2411         int current_show = 0;
2412         int current_preset = 0;
2413
2414         for(int i = 0; i < plugin_on_toggles.total; i++)
2415                 plugin_on_toggles.values[i]->in_use = 0;
2416         for(int i = 0; i < plugin_show_toggles.total; i++)
2417                 plugin_show_toggles.values[i]->in_use = 0;
2418         for(int i = 0; i < preset_edit_buttons.total; i++)
2419                 plugin_show_toggles.values[i]->in_use = 0;
2420
2421
2422         for(Track *track = mwindow->edl->tracks->first;
2423                 track;
2424                 track = track->next)
2425         {
2426                 if( track->is_hidden() ) continue;
2427                 if(track->expand_view)
2428                 {
2429                         for(int i = 0; i < track->plugin_set.total; i++)
2430                         {
2431                                 PluginSet *pluginset = track->plugin_set.values[i];
2432
2433                                 for(Plugin *plugin = (Plugin*)pluginset->first; plugin; plugin = (Plugin*)plugin->next)
2434                                 {
2435                                         int64_t total_x, y, total_w, h;
2436                                         plugin_dimensions(plugin, total_x, y, total_w, h);
2437
2438                                         if(MWindowGUI::visible(total_x, total_x + total_w, 0, get_w()) &&
2439                                                 MWindowGUI::visible(y, y + h, 0, get_h()) &&
2440                                                 plugin->plugin_type != PLUGIN_NONE)
2441                                         {
2442                                                 int x = total_x, w = total_w, left_margin = 5;
2443                                                 int right_margin = 5;
2444                                                 if(x < 0)
2445                                                 {
2446                                                         w -= -x;
2447                                                         x = 0;
2448                                                 }
2449                                                 if(w + x > get_w()) w -= (w + x) - get_w();
2450
2451                                                 draw_3segmenth(x,
2452                                                         y,
2453                                                         w,
2454                                                         total_x,
2455                                                         total_w,
2456                                                         mwindow->theme->get_image("plugin_bg_data"),
2457                                                         0);
2458                                                 set_color(mwindow->theme->title_color);
2459                                                 set_font(mwindow->theme->title_font);
2460                                                 plugin->calculate_title(string, 0);
2461
2462 // Truncate string to int64_test visible in background
2463                                                 int len = strlen(string), j;
2464                                                 for(j = len; j >= 0; j--)
2465                                                 {
2466                                                         if(left_margin + get_text_width(mwindow->theme->title_font, string) > w)
2467                                                         {
2468                                                                 string[j] = 0;
2469                                                         }
2470                                                         else
2471                                                                 break;
2472                                                 }
2473
2474 // Justify the text on the left boundary of the edit if it is visible.
2475 // Otherwise justify it on the left side of the screen.
2476                                                 int64_t text_x = total_x + left_margin;
2477                                                 int64_t text_w = get_text_width(mwindow->theme->title_font, string, strlen(string));
2478                                                 text_x = MAX(left_margin, text_x);
2479                                                 draw_text(text_x,
2480                                                         y + get_text_ascent(mwindow->theme->title_font) + 2,
2481                                                         string,
2482                                                         strlen(string),
2483                                                         0);
2484                                                 int64_t min_x = total_x + text_w;
2485
2486
2487 // Update plugin toggles
2488                                                 int toggle_x = total_x + total_w;
2489                                                 int toggle_y = y;
2490                                                 toggle_x = MIN(get_w() - right_margin, toggle_x);
2491
2492 // On toggle
2493                                                 toggle_x -= PluginOn::calculate_w(mwindow) + xS(10);
2494                                                 if(toggle_x > min_x)
2495                                                 {
2496                                                         if(current_on >= plugin_on_toggles.total)
2497                                                         {
2498                                                                 PluginOn *plugin_on = new PluginOn(mwindow, toggle_x, toggle_y, plugin);
2499                                                                 add_subwindow(plugin_on);
2500                                                                 plugin_on_toggles.append(plugin_on);
2501                                                         }
2502                                                         else
2503                                                         {
2504                                                                 plugin_on_toggles.values[current_on]->update(toggle_x, toggle_y, plugin);
2505                                                         }
2506                                                         current_on++;
2507                                                 }
2508
2509 // Toggles for standalone plugins only
2510                                                 if(plugin->plugin_type == PLUGIN_STANDALONE)
2511                                                 {
2512 // Show
2513                                                         toggle_x -= PluginShow::calculate_w(mwindow) + xS(10);
2514                                                         if(toggle_x > min_x)
2515                                                         {
2516                                                                 if(current_show >= plugin_show_toggles.total)
2517                                                                 {
2518                                                                         PluginShow *plugin_show = new PluginShow(mwindow, toggle_x, toggle_y, plugin);
2519                                                                         add_subwindow(plugin_show);
2520                                                                         plugin_show_toggles.append(plugin_show);
2521                                                                 }
2522                                                                 else
2523                                                                 {
2524                                                                         plugin_show_toggles.values[current_show]->update(toggle_x, toggle_y, plugin);
2525                                                                 }
2526                                                                 current_show++;
2527                                                         }
2528                                                         toggle_x -= PluginPresetEdit::calculate_w(mwindow) + xS(10);
2529                                                         if(toggle_x > min_x)
2530                                                         {
2531                                                                 if(current_preset >= preset_edit_buttons.total)
2532                                                                 {
2533                                                                         PluginPresetEdit *preset_edit = new PluginPresetEdit(mwindow, toggle_x, toggle_y, plugin);
2534                                                                         add_subwindow(preset_edit);
2535                                                                         preset_edit_buttons.append(preset_edit);
2536                                                                 }
2537                                                                 else
2538                                                                 {
2539                                                                         preset_edit_buttons.values[current_preset]->update(toggle_x, toggle_y, plugin);
2540                                                                 }
2541                                                                 current_preset++;
2542                                                         }
2543                                                 }
2544                                         }
2545                                 }
2546                         }
2547                 }
2548         }
2549
2550 // Remove unused toggles
2551
2552         while(current_preset < preset_edit_buttons.total)
2553         {
2554                 preset_edit_buttons.remove_object_number(current_preset);
2555         }
2556         while(current_show < plugin_show_toggles.total)
2557         {
2558                 plugin_show_toggles.remove_object_number(current_show);
2559         }
2560
2561         while(current_on < plugin_on_toggles.total)
2562         {
2563                 plugin_on_toggles.remove_object_number(current_on);
2564         }
2565 }
2566
2567 void TrackCanvas::refresh_plugintoggles()
2568 {
2569         for(int i = 0; i < plugin_on_toggles.total; i++)
2570         {
2571                 PluginOn *on = plugin_on_toggles.values[i];
2572                 on->reposition_window(on->get_x(), on->get_y());
2573         }
2574         for(int i = 0; i < plugin_show_toggles.total; i++)
2575         {
2576                 PluginShow *show = plugin_show_toggles.values[i];
2577                 show->reposition_window(show->get_x(), show->get_y());
2578         }
2579         for(int i = 0; i < preset_edit_buttons.total; i++)
2580         {
2581                 PluginPresetEdit *preset_edit = preset_edit_buttons.values[i];
2582                 preset_edit->reposition_window(preset_edit->get_x(), preset_edit->get_y());
2583         }
2584 }
2585
2586 void TrackCanvas::draw_hard_edges()
2587 {
2588         if( !mwindow->edl->session->auto_conf->hard_edges )
2589                 return;
2590         int64_t x, y, w, h;
2591
2592         for(Track *track = mwindow->edl->tracks->first; track; track = track->next) {
2593                 if( track->is_hidden() ) continue;
2594                 for(Edit *edit = track->edits->first; edit; edit = edit->next) {
2595                         if( !edit->hard_left && !edit->hard_right ) continue;
2596                         edit_dimensions(edit, x, y, w, h);
2597                         if( !MWindowGUI::visible(x, x + w, 0, get_w()) ||
2598                             !MWindowGUI::visible(y, y + h, 0, get_h()) ) continue;
2599                         set_color(GREEN);
2600                         set_opaque();
2601                         int y1 = y;
2602                         if( track->show_titles() )
2603                                 y1 += mwindow->theme->get_image("title_bg_data")->get_h();
2604                         if( track->show_assets() )
2605                                 y1 += track->data_h;
2606                         if( y1 == y )
2607                                 y1 += mwindow->theme->title_h;
2608                         if( edit->hard_left ) {
2609                                 ArrayList<int> xpt, ypt;
2610                                 xpt.append(x);              ypt.append(y1);
2611                                 xpt.append(x+HANDLE_W);     ypt.append(y1);
2612                                 xpt.append(x);              ypt.append(y1-HANDLE_H);
2613                                 fill_polygon(&xpt, &ypt);
2614                         }
2615                         if( edit->hard_right ) {
2616                                 ArrayList<int> xpt, ypt;   int x1 = x+w-1;
2617                                 xpt.append(x1);            ypt.append(y1);
2618                                 xpt.append(x1-HANDLE_W);   ypt.append(y1);
2619                                 xpt.append(x1);            ypt.append(y1-HANDLE_H);
2620                                 fill_polygon(&xpt, &ypt);
2621                         }
2622                 }
2623         }
2624 }
2625
2626 void TrackCanvas::draw_inout_points()
2627 {
2628 }
2629
2630
2631 void TrackCanvas::draw_drag_handle()
2632 {
2633         if( mwindow->session->current_operation != DRAG_EDITHANDLE2 &&
2634             mwindow->session->current_operation != DRAG_PLUGINHANDLE2 &&
2635             mwindow->session->current_operation != DRAG_TRANSNHANDLE2 ) return;
2636         int64_t pixel1 = Units::round(mwindow->session->drag_position *
2637                 mwindow->edl->session->sample_rate /
2638                 mwindow->edl->local_session->zoom_sample -
2639                 mwindow->edl->local_session->view_start[pane->number]);
2640         set_color(!snapped ? GREEN : (snapped=0, YELLOW));
2641         set_inverse();
2642         draw_line(pixel1, 0, pixel1, get_h());
2643         set_opaque();
2644
2645         if( mwindow->session->current_operation != DRAG_EDITHANDLE2 ) return;
2646         if( !mwindow->session->drag_edit ) return;
2647         int group_id = mwindow->session->drag_edit->group_id;
2648         if( !group_id ) return;
2649         int64_t dx, dy, dw, dh;
2650         edit_dimensions(mwindow->session->drag_edit, dx, dy, dw, dh);
2651         int drag_handle = mwindow->session->drag_handle;
2652         int64_t pixel0 = !drag_handle ? dx : dx + dw;
2653         int delta = pixel1 - pixel0;
2654         set_color(LTPURPLE);
2655         set_line_width(3);
2656
2657         for( Track *track=mwindow->edl->tracks->first; track; track=track->next ) {
2658                 if( track->is_hidden() ) continue;
2659                 Edit *left = 0, *right = 0;
2660                 double start = DBL_MAX, end = DBL_MIN;
2661                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
2662                         if( edit->group_id != group_id ) continue;
2663                         double edit_start = edit->track->from_units(edit->startproject);
2664                         if( edit_start < start ) { start = edit_start;  left = edit; }
2665                         double edit_end = edit->track->from_units(edit->startproject+edit->length);
2666                         if( edit_end > end ) { end = edit_end;  right = edit; }
2667                 }
2668                 Edit *edit = !drag_handle ? left : right;
2669                 if( !edit ) continue;
2670                 Indexable *idxbl = edit->asset;
2671                 if( !idxbl ) idxbl = edit->nested_edl;
2672                 int can_drag = idxbl ? 1 : 0;
2673                 if( drag_handle ) {
2674                         int64_t source_len = !idxbl ? -1 :
2675                                 edit->track->data_type == TRACK_AUDIO ?
2676                                         idxbl->get_audio_samples() :
2677                                 edit->track->data_type == TRACK_VIDEO ?
2678                                         idxbl->get_video_frames() : -1;
2679                         int64_t speed_start = edit->startproject;
2680                         int64_t speed_end = speed_start + edit->length;
2681                         int64_t speed_length = track->speed_length(speed_start, speed_end);
2682                         if( edit->startsource + speed_length >= source_len )
2683                                 can_drag = 0;
2684                 }
2685                 else if( !edit->startsource )
2686                         can_drag = 0;
2687                 int64_t x, y, w, h;
2688                 edit_dimensions(edit, x, y, w, h);
2689                 if( y+h < 0 || y >= get_h() ) continue;
2690                 int edge_x = !drag_handle ? x : x + w;
2691                 int edge_y = y + h/2, xs10 = xS(10), ys10 = yS(10);
2692                 if( edge_x >= 0 && edge_x < get_w() ) {
2693                         if( !can_drag ) {
2694                                 draw_line(edge_x-xs10,edge_y-ys10, edge_x+xs10,edge_y+ys10);
2695                                 draw_line(edge_x-xs10,edge_y+ys10, edge_x+xs10,edge_y-ys10);
2696                         }
2697                         else if( !drag_handle ) {
2698                                 draw_line(edge_x+xs10,edge_y-ys10, edge_x,edge_y);
2699                                 draw_line(edge_x+xs10,edge_y+ys10, edge_x,edge_y);
2700                         }
2701                         else {
2702                                 draw_line(edge_x,edge_y, edge_x-xs10,edge_y-ys10);
2703                                 draw_line(edge_x,edge_y, edge_x-xs10,edge_y+ys10);
2704                         }
2705                 }
2706                 edge_x += delta;
2707                 if( edge_x >= 0 && edge_x < get_w() ) {
2708                         draw_line(edge_x, y, edge_x, y+h);
2709                 }
2710         }
2711         set_line_width(1);
2712 }
2713
2714
2715 void TrackCanvas::draw_transitions()
2716 {
2717         int64_t x, y, w, h;
2718         if( !mwindow->edl->session->auto_conf->transitions ) return;
2719
2720         for(Track *track = mwindow->edl->tracks->first; track; track = track->next) {
2721                 if( track->is_hidden() ) continue;
2722                 if( !track->show_transitions() ) continue;
2723
2724                 for(Edit *edit = track->edits->first; edit; edit = edit->next) {
2725                         if(!edit->transition) continue;
2726                         edit_dimensions(edit, x, y, w, h);
2727                         int strip_x = x, edit_y = y;
2728                         get_transition_coords(edit, x, y, w, h);
2729                         int strip_y = y - mwindow->theme->get_image("plugin_bg_data")->get_h();
2730                         if( track->show_assets() && track->show_titles() )
2731                                 edit_y += mwindow->theme->get_image("title_bg_data")->get_h();
2732                         if( strip_y < edit_y ) strip_y = edit_y;
2733
2734                         int strip_w = Units::round(edit->track->from_units(edit->transition->length) *
2735                                 mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample);
2736                         if( MWindowGUI::visible(x, x + w, 0, get_w()) &&
2737                             MWindowGUI::visible(y, y + h, 0, get_h()) ) {
2738                                 PluginServer *server = mwindow->scan_plugindb(edit->transition->title,
2739                                                 track->data_type);
2740                                 if( !server ) continue;
2741                                 VFrame *picon = server->get_picon();
2742                                 if( !picon ) continue;
2743                                 int picon_w = picon->get_w(), picon_h = picon->get_h();
2744                                 int track_h = edit->track->vertical_span(mwindow->theme);
2745                                 if( picon_h > track_h ) picon_h = track_h;
2746                                 draw_vframe(picon, x, y, w, h, 0, 0, picon_w, picon_h);
2747                         }
2748                         if(MWindowGUI::visible(strip_x, strip_x + strip_w, 0, get_w()) &&
2749                                 MWindowGUI::visible(strip_y, strip_y + h, 0, get_h())) {
2750                                 int x = strip_x, w = strip_w;
2751                                 if( x < 0 ) {  w -= -x;  x = 0; }
2752                                 if( w + x > get_w() ) w -= (w + x) - get_w();
2753
2754                                 draw_3segmenth( x, strip_y, w, strip_x, strip_w,
2755                                         mwindow->theme->get_image("plugin_bg_data"), 0);
2756                         }
2757                 }
2758         }
2759 }
2760
2761 void TrackCanvas::draw_loop_points()
2762 {
2763 //printf("TrackCanvas::draw_loop_points 1\n");
2764         if(mwindow->edl->local_session->loop_playback)
2765         {
2766 //printf("TrackCanvas::draw_loop_points 2\n");
2767                 int64_t x = Units::round(mwindow->edl->local_session->loop_start *
2768                         mwindow->edl->session->sample_rate /
2769                         mwindow->edl->local_session->zoom_sample -
2770                         mwindow->edl->local_session->view_start[pane->number]);
2771 //printf("TrackCanvas::draw_loop_points 3\n");
2772
2773                 if(MWindowGUI::visible(x, x + 1, 0, get_w()))
2774                 {
2775                         set_color(GREEN);
2776                         draw_line(x, 0, x, get_h());
2777                 }
2778 //printf("TrackCanvas::draw_loop_points 4\n");
2779
2780                 x = Units::round(mwindow->edl->local_session->loop_end *
2781                         mwindow->edl->session->sample_rate /
2782                         mwindow->edl->local_session->zoom_sample -
2783                         mwindow->edl->local_session->view_start[pane->number]);
2784 //printf("TrackCanvas::draw_loop_points 5\n");
2785
2786                 if(MWindowGUI::visible(x, x + 1, 0, get_w()))
2787                 {
2788                         set_color(GREEN);
2789                         draw_line(x, 0, x, get_h());
2790                 }
2791 //printf("TrackCanvas::draw_loop_points 6\n");
2792         }
2793 //printf("TrackCanvas::draw_loop_points 7\n");
2794 }
2795
2796 void TrackCanvas::draw_brender_range()
2797 {
2798         if( !mwindow->preferences->use_brender || !mwindow->brender_active ) return;
2799         if( mwindow->edl->session->brender_start >= mwindow->edl->session->brender_end ) return;
2800         if( mwindow->edl->session->brender_end > 0 )
2801         {
2802                 int64_t x1 = Units::round(mwindow->edl->session->brender_start *
2803                         mwindow->edl->session->sample_rate /
2804                         mwindow->edl->local_session->zoom_sample -
2805                         mwindow->edl->local_session->view_start[pane->number]);
2806                 if(MWindowGUI::visible(x1, x1 + 1, 0, get_w()))
2807                 {
2808                         set_color(RED);
2809                         draw_line(x1, 0, x1, get_h());
2810                 }
2811                 int64_t x2 = Units::round(mwindow->edl->session->brender_end *
2812                         mwindow->edl->session->sample_rate /
2813                         mwindow->edl->local_session->zoom_sample -
2814                         mwindow->edl->local_session->view_start[pane->number]);
2815
2816                 if(MWindowGUI::visible(x2, x2 + 1, 0, get_w()))
2817                 {
2818                         set_color(RED);
2819                         draw_line(x2, 0, x2, get_h());
2820                 }
2821         }
2822 }
2823
2824 // The operations which correspond to each automation type
2825 int TrackCanvas::auto_operations[AUTOMATION_TOTAL] =
2826 {
2827         DRAG_MUTE,
2828         DRAG_CAMERA_X,
2829         DRAG_CAMERA_Y,
2830         DRAG_CAMERA_Z,
2831         DRAG_PROJECTOR_X,
2832         DRAG_PROJECTOR_Y,
2833         DRAG_PROJECTOR_Z,
2834         DRAG_FADE,
2835         DRAG_PAN,
2836         DRAG_MODE,
2837         DRAG_MASK,
2838         DRAG_SPEED
2839 };
2840
2841 // The buttonpress operations, so nothing changes unless the mouse moves
2842 // a certain amount.  This allows the keyframe to be used to position the
2843 // insertion point without moving itself.
2844 static int pre_auto_operations[AUTOMATION_TOTAL] =
2845 {
2846         DRAG_MUTE,
2847         DRAG_CAMERA_X,
2848         DRAG_CAMERA_Y,
2849         DRAG_CAMERA_Z,
2850         DRAG_PROJECTOR_X,
2851         DRAG_PROJECTOR_Y,
2852         DRAG_PROJECTOR_Z,
2853         DRAG_FADE,
2854         DRAG_PAN_PRE,
2855         DRAG_MODE_PRE,
2856         DRAG_MASK_PRE,
2857         DRAG_SPEED
2858 };
2859
2860
2861 int TrackCanvas::do_keyframes(int cursor_x,
2862         int cursor_y,
2863         int draw,
2864         int buttonpress,
2865         int &new_cursor,
2866         int &update_cursor,
2867         int &rerender)
2868 {
2869 // Note: button 3 (right mouse button) is not eaten to allow
2870 // track context menu to appear
2871         int result = 0;
2872         EDLSession *session = mwindow->edl->session;
2873         int gang = mwindow->edl->local_session->gang_tracks != GANG_NONE ||
2874                         get_double_click() ? 1 : 0;
2875
2876         static BC_Pixmap *auto_pixmaps[AUTOMATION_TOTAL] =
2877         {
2878                 0, 0, 0, 0, 0, 0, 0, 0,
2879                 pankeyframe_pixmap,
2880                 modekeyframe_pixmap,
2881                 maskkeyframe_pixmap,
2882                 0,
2883         };
2884
2885
2886
2887         for(Track *track = mwindow->edl->tracks->first;
2888                 track && !result;
2889                 track = track->next) {
2890                 if( track->is_hidden() ) continue;
2891                 Auto *auto_keyframe = 0;
2892                 Automation *automation = track->automation;
2893
2894
2895 // Handle keyframes in reverse drawing order if a button press
2896                 int start = 0;
2897                 int end = AUTOMATION_TOTAL;
2898                 int step = 1;
2899                 if(buttonpress)
2900                 {
2901                         start = AUTOMATION_TOTAL - 1;
2902                         end = -1;
2903                         step = -1;
2904                 }
2905                 for(int i = start; i != end && !result; i += step)
2906                 {
2907 // Event not trapped and automation visible
2908                         Autos *autos = automation->autos[i];
2909                         if(!result && session->auto_conf->autos[i] && autos) {
2910                                 switch(i) {
2911                                 case AUTOMATION_MODE:
2912                                 case AUTOMATION_PAN:
2913                                 case AUTOMATION_MASK:
2914                                         result = do_autos(track, automation->autos[i],
2915                                                 cursor_x, cursor_y, draw,
2916                                                 buttonpress, auto_pixmaps[i],
2917                                                 auto_keyframe, rerender);
2918                                                 break;
2919
2920                                 default: {
2921                                         switch(autos->get_type()) {
2922                                         case Autos::AUTOMATION_TYPE_FLOAT: {
2923                                                 Automation automation(0, track);
2924                                                 int grouptype = automation.autogrouptype(i, track);
2925                                                 if( buttonpress && i == AUTOMATION_SPEED ) {
2926                                                         mwindow->speed_before();
2927                                                 }
2928
2929                                                 if(draw) // Do dropshadow
2930                                                         result = do_float_autos(track, autos,
2931                                                                 cursor_x, cursor_y, draw,
2932                                                                 buttonpress, 1, 1, MDGREY,
2933                                                                 auto_keyframe, grouptype);
2934                                                 result = do_float_autos(track, autos,
2935                                                         cursor_x, cursor_y, draw, buttonpress,
2936                                                         0, 0, GWindowGUI::auto_colors[i],
2937                                                         auto_keyframe, grouptype);
2938
2939                                                 if( !result && buttonpress && i == AUTOMATION_SPEED )
2940                                                         mwindow->speed_after(-1, 0);
2941                                                 if( result && buttonpress ) {
2942                                                         int color = GWindowGUI::auto_colors[i];
2943                                                         mwindow->gui->zoombar->update_autozoom(grouptype, color);
2944                                                 }
2945                                                 break; }
2946
2947                                         case Autos::AUTOMATION_TYPE_INT: {
2948                                                 if(draw) // Do dropshadow
2949                                                         result = do_int_autos(track, autos,
2950                                                                 cursor_x, cursor_y, draw,
2951                                                                 buttonpress, 1, 1, MDGREY,
2952                                                                 auto_keyframe);
2953                                                 result = do_int_autos(track, autos,
2954                                                         cursor_x, cursor_y, draw, buttonpress,
2955                                                         0, 0, GWindowGUI::auto_colors[i],
2956                                                         auto_keyframe);
2957                                                 break; }
2958                                         }
2959                                         break; }
2960                                 }
2961
2962                                 if(result)
2963                                 {
2964                                         if(mwindow->session->current_operation == auto_operations[i])
2965                                                 rerender = 1;
2966
2967 // printf("TrackCanvas::do_keyframes %d %d %d\n",
2968 // __LINE__,
2969 // mwindow->session->current_operation,
2970 // auto_operations[i]);
2971                                         if(buttonpress)
2972                                         {
2973                                                 if (buttonpress == 2 && auto_keyframe )
2974                                                 {
2975                                                         double position = track->from_units(auto_keyframe->position);
2976                                                         mwindow->edl->local_session->set_selectionstart(position);
2977                                                         mwindow->edl->local_session->set_selectionend(position);
2978                                                 }
2979                                                 if (buttonpress != 3)
2980                                                 {
2981                                                         if(i == AUTOMATION_FADE || i == AUTOMATION_SPEED)
2982                                                                 fill_ganged_autos(gang, 0, track,
2983                                                                         (FloatAuto*)mwindow->session->drag_auto);
2984                                                         mwindow->session->current_operation = pre_auto_operations[i];
2985                                                         update_drag_caption();
2986                                                         rerender = 1;
2987                                                 }
2988                                                 else if( auto_keyframe )
2989                                                 {
2990                                                         gui->keyframe_menu->update(automation,
2991                                                                 autos,
2992                                                                 auto_keyframe);
2993                                                         gui->keyframe_menu->activate_menu();
2994                                                         rerender = 1; // the position changes
2995                                                 }
2996                                                 else if( autos )
2997                                                 {
2998                                                         gui->keyframe_hide->update(autos);
2999                                                         gui->keyframe_hide->activate_menu();
3000                                                         rerender = 1; // the position changes
3001                                                 }
3002                                                 if(buttonpress == 1 && ctrl_down() &&
3003                                                    AUTOMATION_TYPE_FLOAT == autos->get_type())
3004                                                         rerender = 1; // special case: curve mode changed
3005                                         }
3006                                 }
3007                         }
3008                 }
3009
3010                 if(!result && session->auto_conf->plugins) {
3011                         Plugin *plugin;
3012                         KeyFrame *keyframe;
3013                         result = do_plugin_autos(track, cursor_x, cursor_y,
3014                                 draw, buttonpress, plugin, keyframe);
3015                         if(result && mwindow->session->current_operation == DRAG_PLUGINKEY) {
3016                                 rerender = 1;
3017                         }
3018                         if(result && (buttonpress == 1)) {
3019                                 mwindow->session->current_operation = DRAG_PLUGINKEY_PRE;
3020                                 update_drag_caption();
3021                                 rerender = 1;
3022                         }
3023                         else if (result && (buttonpress == 3)) {
3024                                 gui->keyframe_menu->update(plugin, keyframe);
3025                                 gui->keyframe_menu->activate_menu();
3026                                 rerender = 1; // the position changes
3027                         }
3028                 }
3029         }
3030
3031 // Final pass to trap event
3032         for(int i = 0; i < AUTOMATION_TOTAL; i++) {
3033                 if(mwindow->session->current_operation == pre_auto_operations[i] ||
3034                         mwindow->session->current_operation == auto_operations[i]) {
3035                         result = 1;
3036                         break;
3037                 }
3038         }
3039
3040         if(mwindow->session->current_operation == DRAG_PLUGINKEY ||
3041                 mwindow->session->current_operation == DRAG_PLUGINKEY_PRE) {
3042                 result = 1;
3043         }
3044
3045         update_cursor = 1;
3046         if(result) {
3047                 new_cursor = UPRIGHT_ARROW_CURSOR;
3048         }
3049
3050         return result;
3051 }
3052
3053 void TrackCanvas::draw_keyframe_reticle()
3054 {
3055         int keyframe_hairline = mwindow->preferences->keyframe_reticle;
3056         if( keyframe_hairline == HAIRLINE_NEVER ) return;
3057
3058         int current_op = mwindow->session->current_operation, dragging = 0;
3059         for( int i=0; !dragging && i<AUTOMATION_TOTAL; ++i )
3060                 if( current_op == auto_operations[i] ) dragging = 1;
3061         if( dragging && !mwindow->session->drag_auto ) dragging = 0;
3062
3063         int autoidx = dragging && keyframe_hairline != HAIRLINE_ALWAYS ?
3064                 mwindow->session->drag_auto->autos->autoidx : -1;
3065
3066         if( get_buttonpress() == LEFT_BUTTON && dragging &&
3067             keyframe_hairline == HAIRLINE_DRAGGING ) {
3068                 draw_hairline(mwindow->session->drag_auto, RED, 1);
3069                 return;
3070         }
3071
3072         if( keyframe_hairline == HAIRLINE_ALWAYS || ( get_buttonpress() == MIDDLE_BUTTON &&
3073             keyframe_hairline == HAIRLINE_DRAGGING && dragging ) ) {
3074                 int show = dragging || keyframe_hairline == HAIRLINE_ALWAYS ? 1 : 0;
3075                 for( Track *track = mwindow->edl->tracks->first; track; track=track->next ) {
3076                         if( track->is_hidden() ) continue;
3077                         Automation *automation = track->automation;
3078                         for( int i=0; i<AUTOMATION_TOTAL; ++i ) {
3079                                 if( !mwindow->edl->session->auto_conf->autos[i] ) continue;
3080                                 // automation visible
3081                                 Autos *autos = automation->autos[i];
3082                                 if( !autos ) continue;
3083                                 if( autoidx >= 0 && autos->autoidx != autoidx ) continue;
3084                                 for( Auto *auto_keyframe=autos->first; auto_keyframe;
3085                                      auto_keyframe = auto_keyframe->next ) {
3086                                         draw_hairline(auto_keyframe, BLUE, show);
3087                                 }
3088                         }
3089                 }
3090
3091                 if( dragging )
3092                         draw_hairline(mwindow->session->drag_auto, RED, 1);
3093         }
3094 }
3095
3096 void TrackCanvas::draw_auto(Auto *current, int x, int y,
3097                 int center_pixel, int data_h)
3098 {
3099         int x1, y1, x2, y2;
3100
3101         x1 = x - HANDLE_W / 2;
3102         x2 = x + HANDLE_W / 2;
3103         y1 = center_pixel + y - HANDLE_W / 2;
3104         y2 = center_pixel + y + HANDLE_W / 2;
3105
3106         if( y1 < center_pixel - data_h/2 ) y1 = center_pixel - data_h/2;
3107         if( y2 > center_pixel + data_h/2 ) y2 = center_pixel + data_h/2;
3108
3109         draw_box(x1, y1, x2 - x1, y2 - y1);
3110 }
3111
3112
3113 // This draws lines for bezier in & out controls
3114 void TrackCanvas::draw_cropped_line(int x1,
3115         int y1,
3116         int x2,
3117         int y2,
3118         int min_y,
3119         int max_y)
3120 {
3121
3122
3123 // Don't care about x since it is clipped by the window.
3124 // Put y coords in ascending order
3125         if(y2 < y1) {
3126                 y2 ^= y1; y1 ^= y2; y2 ^= y1;
3127                 x2 ^= x1; x1 ^= x2; x2 ^= x1;
3128         }
3129
3130
3131
3132         double slope = (double)(x2 - x1) / (y2 - y1);
3133 //printf("TrackCanvas::draw_cropped_line %d %d %d %d %d\n", __LINE__, x1, y1, x2, y2);
3134         if(y1 < min_y) {
3135                 x1 = (int)(x1 + (min_y - y1) * slope);
3136                 y1 = min_y;
3137         }
3138         else if(y1 >= max_y) {
3139                 x1 = (int)(x1 + (max_y - 1 - y1) * slope);
3140                 y1 = max_y - 1;
3141         }
3142
3143         if(y2 >= max_y) {
3144                 x2 = (int)(x2 + (max_y - 1 - y2) * slope);
3145                 y2 = max_y - 1;
3146         }
3147         else if(y2 < min_y) {
3148                 x2 = (int)(x2 + (min_y - y2) * slope);
3149                 y1 = min_y;
3150         }
3151
3152
3153 //printf("TrackCanvas::draw_cropped_line %d %d %d %d %d\n", __LINE__, x1, y1, x2, y2);
3154         if( y1 >= min_y && y1 < max_y &&
3155             y2 >= min_y && y2 < max_y )
3156                 draw_line(x1, y1, x2, y2);
3157 }
3158
3159
3160 void TrackCanvas::draw_floatauto(FloatAuto *current,
3161                 int x_offset, int center_pixel, int data_h, int color,
3162                 double unit_start, double zoom_units, double yscale,
3163                 int autogrouptype)
3164 {
3165         double xx, yy;
3166         calculate_auto_position(1, &xx, &yy, 0, 0, 0, 0,
3167                 current, unit_start, zoom_units, yscale, autogrouptype);
3168         int x = (int)xx + x_offset;
3169         int x1 = x - HANDLE_W / 2; // Center
3170         int x2 = x + HANDLE_W / 2;
3171         int y = (int)yy + center_pixel;
3172         int y1 = y - HANDLE_H / 2;
3173         int y2 = y + HANDLE_H / 2;
3174         int ymin = center_pixel - data_h / 2;
3175         int ymax = center_pixel + data_h / 2;
3176         CLAMP(y1, ymin, ymax);
3177         CLAMP(y2, ymin, ymax);
3178
3179         if( y2-1 > y1 ) {
3180                 if( current->curve_mode == FloatAuto::LINEAR ) {
3181                         draw_box(x1, y1, x2 - x1, y2 - y1);
3182                 }
3183                 else {
3184                         ArrayList<int> polygon_x;
3185                         ArrayList<int> polygon_y;
3186                         polygon_x.append((x1 + x2) / 2 + 1);
3187                         polygon_y.append(y1 + 1);
3188                         polygon_x.append(x2 + 1);
3189                         polygon_y.append((y1 + y2) / 2 + 1);
3190                         polygon_x.append((x1 + x2) / 2 + 1);
3191                         polygon_y.append(y2 + 1);
3192                         polygon_x.append(x1 + 1);
3193                         polygon_y.append((y1 + y2) / 2 + 1);
3194                         fill_polygon(&polygon_x, &polygon_y);
3195                 }
3196         }
3197
3198 // show bezier control points (only) if this
3199 // floatauto doesn't adjust it's tangents automatically
3200         if(current->curve_mode != FloatAuto::FREE &&
3201            current->curve_mode != FloatAuto::TFREE &&
3202            current->curve_mode != FloatAuto::BUMP)
3203                 return;
3204         double in_xx, in_yy, out_xx, out_yy;
3205         calculate_auto_position(1, &xx, &yy, &in_xx, &in_yy, 0, 0,
3206                 current, unit_start, zoom_units, yscale, autogrouptype);
3207         int ix = xx, iy = yy, in_x = in_xx, in_y = in_yy;
3208         if( in_x != ix )
3209                 draw_floatauto_ctrlpoint(ix, iy, in_x, in_y, center_pixel, data_h, color);
3210         calculate_auto_position(0, &xx, &yy, 0, 0, &out_xx, &out_yy,
3211                 current, unit_start, zoom_units, yscale, autogrouptype);
3212         int ox = xx, oy = yy, out_x = out_xx, out_y = out_yy;
3213         if( out_x != ox )
3214                 draw_floatauto_ctrlpoint(ox, oy, out_x, out_y, center_pixel, data_h, color);
3215         if( current->curve_mode == FloatAuto::BUMP && iy != oy )
3216                 draw_bline(ix, iy+center_pixel, ox, oy+center_pixel);
3217 }
3218
3219 inline int quantize(float f) { return (int)floor(f + 0.5); }
3220
3221 inline void TrackCanvas::draw_floatauto_ctrlpoint(
3222                 int x, int y, int cp_x, int cp_y, int center_pixel,
3223                 int data_h, int color)
3224 // draw the tangent and a handle for given bézier ctrl point
3225 {
3226         bool handle_visible = (abs(cp_y) <= data_h/2);
3227
3228         float slope = (float)(cp_y - y)/(cp_x - x);
3229         CLAMP(cp_y, -data_h/2, data_h/2);
3230         if(slope != 0)
3231                 cp_x = x + quantize((cp_y - y) / slope);
3232
3233         y    += center_pixel;
3234         cp_y += center_pixel;
3235
3236         // drawing the tangent as a dashed line...
3237         int const dash = HANDLE_W;
3238         int const gap  = HANDLE_W / 2;
3239         float sx = 3 * (cp_x - x) / 4.;
3240         float ex = 0;
3241
3242         // q is the x displacement for a unit line of slope
3243         float q = (sx > 0 ? 1 : -1) / sqrt(1 + slope * slope);
3244
3245         float dist = 1/q * sx;
3246         if( dist > dash )
3247                 ex = sx - q * dash;
3248
3249         set_color(color);
3250         do {
3251                 float sy = slope * sx, ey = slope * ex;
3252                 draw_line(quantize(sx + x), quantize(sy + y), quantize(ex + x), quantize(ey + y));
3253                 sx = ex - q * gap;
3254                 ex = sx - q * dash;
3255         } while(q*ex > 0);
3256
3257         if(handle_visible)
3258         {
3259                 int r = HANDLE_W / 2;
3260                 int cp_x1 = cp_x - r;
3261                 int cp_y1 = cp_y - r;
3262                 set_color(BLACK);
3263                 draw_disc(cp_x1, cp_y1, 2 * r, 2 * r);
3264                 set_color(color);
3265                 draw_circle(cp_x1, cp_y1, 2 * r, 2 * r);
3266         }
3267 }
3268
3269
3270 int TrackCanvas::test_auto(Auto *current,
3271         int x, int y, int center_pixel, int data_h,
3272         int cursor_x, int cursor_y, int buttonpress)
3273 {
3274         int x1, y1, x2, y2;
3275         int result = 0;
3276
3277         x1 = x - HANDLE_W / 2;
3278         x2 = x + HANDLE_W / 2;
3279         y1 = center_pixel + y - HANDLE_H / 2;
3280         y2 = center_pixel + y + HANDLE_H / 2;
3281         int ymin = center_pixel - data_h/2;
3282         int ymax = center_pixel + data_h/2;
3283         CLAMP(y1, ymin, ymax);
3284         CLAMP(y2, ymin, ymax);
3285
3286         if(cursor_x >= x1 && cursor_x < x2 && cursor_y >= y1 && cursor_y < y2)
3287         {
3288                 if(buttonpress && buttonpress != 3)
3289                 {
3290                         mwindow->session->drag_auto = current;
3291                         mwindow->session->drag_start_percentage = (float)((IntAuto*)current)->value;
3292                         // Toggle Autos don't respond to vertical zoom, they always show up
3293                         // with "on" == 100% == line on top
3294                         mwindow->session->drag_start_position = current->position;
3295                         mwindow->session->drag_origin_x = cursor_x;
3296                         mwindow->session->drag_origin_y = cursor_y;
3297                 }
3298                 result = 1;
3299         }
3300
3301         if(buttonpress && buttonpress != 3 && result)
3302         {
3303 //printf("TrackCanvas::test_auto %d\n", __LINE__);
3304                 mwindow->undo->update_undo_before();
3305         }
3306
3307         return result;
3308 }
3309
3310 // some Helpers for test_floatauto(..)
3311 // and for dragging the tangents/ctrl points
3312 inline float test_curve_line( int x0, int y0, int ctrl_x, int ctrl_y,
3313         float cursor_x, float cursor_y)
3314 {
3315 // Control point switched off?
3316         if( x0 == ctrl_x ) return 0.0;
3317         double x1 = ctrl_x-x0, y1 = ctrl_y-y0;
3318         double x = cursor_x-x0, y = cursor_y-y0;
3319 // wrong side of ctrl handle
3320         if( x*x1 < 0 ) return 0.0;
3321 // outside handle radius
3322         if( (x*x + y*y) > (x1*x1 + y1*y1) ) return 0;
3323         double xx1 = x1*x1, yy1 = y1*y1;
3324         double xx = x*x, yy = y*y;
3325 // distance squared from cursor to cursor projected to line from ctrl to handle
3326 //   along a line perpendicular to line from ctrl to handle (closest approach)
3327 // (x**2*y1**2 - 2*x*x1*y*y1 + x1**2*y**2)/(x1**2 + y1**2)
3328         double dist2 = (xx*yy1 - 2*x*x1*y*y1 + xx1*yy)/(xx1 + yy1);
3329         return dist2 < (HANDLE_W*HANDLE_W)/4. ? x/x1 : 0.;
3330 }
3331
3332
3333 inline
3334 float levered_position(float position, float ref_pos)
3335 {
3336         if( 1e-6 > fabs(ref_pos) || isnan(ref_pos))
3337                 return 0.0;
3338         return ref_pos / position;
3339 }
3340
3341
3342 float TrackCanvas::value_to_percentage(float auto_value, int autogrouptype)
3343 // transforms automation value into current display coords,
3344 // dependant on current automation display range for the given kind of automation
3345 {
3346         if(!mwindow || !mwindow->edl) return 0;
3347         float automation_min = mwindow->edl->local_session->automation_mins[autogrouptype];
3348         float automation_max = mwindow->edl->local_session->automation_maxs[autogrouptype];
3349         float automation_range = automation_max - automation_min;
3350         if( 0 >= automation_range || isnan(auto_value) || isinf(auto_value) )
3351                 return 0;
3352         return (auto_value - automation_min) / automation_range;
3353 }
3354
3355
3356
3357 int TrackCanvas::test_floatauto(FloatAuto *current, int buttonpress,
3358                 int center_pixel, int data_h, int cursor_x, int cursor_y,
3359                 double unit_start, double zoom_units, double yscale,
3360                 int autogrouptype)
3361 {
3362         int result = 0;
3363         double xx, yy, in_xx, in_yy, out_xx, out_yy;
3364         calculate_auto_position(1, &xx, &yy, &in_xx, &in_yy, 0, 0,
3365                 current, unit_start, zoom_units, yscale, autogrouptype);
3366         int ix = xx, iy = yy, in_x = in_xx, in_y = in_yy;
3367
3368         int x1 = ix - HANDLE_W / 2;
3369         int x2 = ix + HANDLE_W / 2;
3370         int y1 = center_pixel + iy - HANDLE_W / 2;
3371         int y2 = center_pixel + iy + HANDLE_W / 2;
3372         int ymin = center_pixel - data_h/2;
3373         int ymax = center_pixel + data_h/2;
3374         CLAMP(y1, ymin, ymax);
3375         CLAMP(y2, ymin, ymax);
3376
3377         int in_x1 = in_x - HANDLE_W / 2;
3378         int in_x2 = in_x + HANDLE_W / 2;
3379         int in_y1 = center_pixel + in_y - HANDLE_W / 2;
3380         int in_y2 = center_pixel + in_y + HANDLE_W / 2;
3381         CLAMP(in_y1, ymin, ymax);
3382         CLAMP(in_y2, ymin, ymax);
3383
3384         calculate_auto_position(0, &xx, &yy, 0, 0, &out_xx, &out_yy,
3385                 current, unit_start, zoom_units, yscale, autogrouptype);
3386         int ox = xx, oy = yy, out_x = out_xx, out_y = out_yy;
3387         int out_x1 = out_x - HANDLE_W / 2;
3388         int out_x2 = out_x + HANDLE_W / 2;
3389         int out_y1 = center_pixel + out_y - HANDLE_W / 2;
3390         int out_y2 = center_pixel + out_y + HANDLE_W / 2;
3391         CLAMP(out_y1, ymin, ymax);
3392         CLAMP(out_y2, ymin, ymax);
3393
3394
3395 //printf("TrackCanvas::test_floatauto %d %d %d %d %d %d\n", cursor_x, cursor_y, x1, x2, y1, y2);
3396 // buttonpress could be the start of a drag operation
3397 #define INIT_DRAG(POS,VAL) \
3398                 mwindow->session->drag_auto = current;      \
3399                 mwindow->session->drag_origin_x = cursor_x;  \
3400                 mwindow->session->drag_origin_y = cursor_y;   \
3401                 mwindow->session->drag_start_position = (POS); \
3402                 mwindow->session->drag_start_percentage = (VAL);
3403
3404 #define WITHIN(X1,X2,Y1,Y2) (cursor_x >=(X1) && cursor_x <(X2) && cursor_y >=(Y1) && cursor_y <(Y2) )
3405
3406
3407 // without modifier we are manipulating the automation node
3408 // with ALT it's about dragging only the value of the node
3409 // with SHIFT the value snaps to the value of neighbouring nodes
3410 // CTRL indicates we are rather manipulating the tangent(s) of the node
3411
3412         if(!ctrl_down())
3413         {
3414                 if( WITHIN(x1,x2,y1,y2))
3415                 {       // cursor hits node
3416                         result = 1;
3417
3418                         if(buttonpress && (buttonpress != 3))
3419                         {
3420                                 INIT_DRAG(current->position, value_to_percentage(current->get_value(), autogrouptype))
3421                                 mwindow->session->drag_handle = 0;
3422                 }       }
3423         }
3424         else // ctrl_down()
3425         {
3426                 if( WITHIN(x1,x2,y1,y2))
3427                 {
3428                         result = 1;
3429                         if(buttonpress && (buttonpress != 3))
3430                         {
3431                                 // could be ctrl-click or ctrl-drag
3432                                 // click would cycle through tangent modes
3433                                 ((FloatAuto*)current)->toggle_curve_mode();
3434
3435                                 // drag will start dragging the tangent, if applicable
3436                                 INIT_DRAG(current->position, value_to_percentage(current->get_value(), autogrouptype))
3437                                 mwindow->session->drag_handle = 0;
3438                         }
3439                 }
3440
3441                 float lever = 0.0; // we use the tangent as a draggable lever. 1.0 is at the ctrl point
3442
3443 // Test in control
3444                 if( in_x != ix && current->position > 0 &&
3445                         (FloatAuto::FREE == current->curve_mode ||
3446                          FloatAuto::TFREE == current->curve_mode ||
3447                          FloatAuto::BUMP == current->curve_mode))
3448 // act on in control handle only if
3449 // tangent is significant and is editable (not automatically choosen)
3450                 {
3451                         lever = test_curve_line(ix, iy, in_x, in_y, cursor_x, cursor_y-center_pixel);
3452  // either cursor in ctrl-point handle or cursor on tangent line
3453                         if( WITHIN(in_x1,in_x2,in_y1,in_y2) || lever > 0.0 ) {
3454                                 result = 1;
3455                                 if(buttonpress && (buttonpress != 3)) {
3456                                         if(lever == 0.0) lever=1.0; // we entered by dragging the handle...
3457 //                                      lever = 1.0;
3458                                         mwindow->session->drag_handle = 1;
3459                                         float new_invalue = current->get_value() + lever * current->get_control_in_value();
3460                                         INIT_DRAG(current->position + (int64_t)(lever * current->get_control_in_position()),
3461                                                   value_to_percentage(new_invalue, autogrouptype))
3462                                 }
3463                         }
3464                 }
3465
3466 // Test out control
3467                 if(out_x != ox &&
3468                         (FloatAuto::FREE == current->curve_mode ||
3469                          FloatAuto::TFREE == current->curve_mode ||
3470                          FloatAuto::BUMP == current->curve_mode))
3471 // act on out control only if tangent is significant and is editable
3472                 {
3473                         lever = test_curve_line(ox, oy, out_x, out_y, cursor_x, cursor_y-center_pixel);
3474                         if(WITHIN(out_x1,out_x2,out_y1,out_y2) || lever > 0.0 ) {
3475                                 result = 1;
3476                                 if(buttonpress && (buttonpress != 3)) {
3477                                         if(lever == 0.0) lever=1.0;
3478 //                                      lever = 1.0;
3479                                         mwindow->session->drag_handle = 2;
3480                                         float new_outvalue = current->get_value() + lever * current->get_control_out_value();
3481                                         INIT_DRAG(current->position + (int64_t)(lever * current->get_control_out_position()),
3482                                                   value_to_percentage(new_outvalue, autogrouptype))
3483                                 }
3484                         }
3485                 }
3486         } // end ctrl_down()
3487
3488 #undef WITHIN
3489 #undef INIT_DRAG
3490
3491 // if(buttonpress)
3492 // printf("TrackCanvas::test_floatauto 2 drag_handle=%d ctrl_down=%d cursor_x=%d cursor_y=%d x1=%d x2=%d y1=%d y2=%d\n",
3493 // mwindow->session->drag_handle,
3494 // ctrl_down(),
3495 // cursor_x,
3496 // cursor_y,
3497 // x1, x2, y1, y2);
3498         if(buttonpress && (buttonpress != 3) && result)
3499         {
3500                 mwindow->undo->update_undo_before();
3501                 double position = current->autos->track->from_units(current->position);
3502                 mwindow->edl->local_session->set_selectionstart(position);
3503                 mwindow->edl->local_session->set_selectionend(position);
3504         }
3505
3506         return result;
3507 }
3508
3509 static int is_linear(FloatAuto *prev, FloatAuto *next)
3510 {
3511         if( !prev || !next ) return 0;
3512         if( prev->curve_mode == FloatAuto::LINEAR ) return 1;
3513         int64_t ipos = prev->get_control_in_position();
3514         int64_t opos = prev->get_control_out_position();
3515         if( !ipos && !opos ) return 1;
3516         if( !ipos || !opos ) return 0;
3517         float ival = next->get_control_in_value();
3518         float oval = prev->get_control_out_value();
3519         float cval = prev->get_value(0), nval = next->get_value(1);
3520         if( !ival && !oval && EQUIV(cval, nval) ) return 1;
3521         float ig = ival / ipos, og = oval / opos;
3522         int64_t cpos = prev->position, npos = next->position;
3523         float g = (nval - cval) / (npos - cpos);
3524         if( !EQUIV(g, 0) && EQUIV(ig, g) && EQUIV(og, g) ) return 1;
3525         return 0;
3526 }
3527
3528 // Get the float value & y for position x on the canvas
3529 #define X_TO_FLOATLINE(x) \
3530         double position = unit_start + x * zoom_units; \
3531         int64_t position1 = (int64_t)position, position2 = position1 + 1; \
3532 /* Call by reference fails for some reason here */ \
3533         float value1 = autos->get_value(position1, PLAY_FORWARD, previous1, next1); \
3534         float value2 = autos->get_value(position2, PLAY_FORWARD, previous1, next1); \
3535         double value = value1 + (value2 - value1) * (position - position1); \
3536         AUTOMATIONCLAMPS(value, autogrouptype); \
3537         int y = center_pixel + \
3538                 (int)(((value - automation_min) / automation_range - 0.5) * -yscale);
3539
3540
3541 void TrackCanvas::draw_floatline(int center_pixel,
3542         FloatAuto *previous, FloatAuto *next, FloatAutos *autos,
3543         double unit_start, double zoom_units, double yscale,
3544         int x1, int y1, int x2, int y2,
3545         int color, int autogrouptype)
3546 {
3547         int ytop = center_pixel - yscale / 2;
3548         int ybot = center_pixel + yscale / 2 - 1;
3549         y1 += center_pixel;  y2 += center_pixel;
3550         if( (y1 < ytop && y1 >= ybot) && (y2 < ytop || y2 >= ybot) ) return;
3551 // check for line draw
3552         if( is_linear(previous, next) ) {
3553                 if( previous && previous->curve_mode == FloatAuto::BUMP ) {
3554                         double ax, ay;
3555                         calculate_auto_position(0, &ax, &ay, 0, 0, 0, 0, previous,
3556                                 unit_start, zoom_units, yscale, autogrouptype);
3557                         x1 = ax;  y1 = ay;  y1 += center_pixel;
3558                 }
3559                 draw_line(x1, y1, x2, y2);
3560                 return;
3561         }
3562
3563 // Solve bezier equation for either every pixel or a certain large number of
3564 // points.
3565
3566 // Not using slope intercept
3567         x1 = MAX(0, x1);
3568         draw_pixel(x1, y1);
3569         int prev_y = y1;
3570
3571 // Call by reference fails for some reason here
3572         FloatAuto *previous1 = previous, *next1 = next;
3573         float automation_min = mwindow->edl->local_session->automation_mins[autogrouptype];
3574         float automation_max = mwindow->edl->local_session->automation_maxs[autogrouptype];
3575         float automation_range = automation_max - automation_min;
3576         if( autogrouptype == AUTOGROUPTYPE_SPEED && automation_range < SPEED_MIN )
3577                 automation_range = SPEED_MIN;
3578         for( int x=x1; x<x2; ++x ) {
3579 // Interpolate value between frames
3580                 X_TO_FLOATLINE(x)
3581
3582                 if( /* x > x1 && */ y >= ytop && y < ybot ) {
3583                         int x1 = x-1, y1 = prev_y, x2 = x, y2 = y;
3584                         if( abs(y2-y1) < 2 )
3585                                 draw_pixel(x2, y2);
3586                         else
3587                                 draw_bline(x1, y1, x2, y2);
3588                 }
3589                 prev_y = y;
3590         }
3591 }
3592
3593
3594 int TrackCanvas::test_floatline(int center_pixel,
3595                 FloatAutos *autos,
3596                 double unit_start,
3597                 double zoom_units,
3598                 double yscale,
3599                 int x1,
3600                 int x2,
3601                 int cursor_x,
3602                 int cursor_y,
3603                 int buttonpress,
3604                 int autogrouptype)
3605 {
3606         int result = 0;
3607
3608
3609         float automation_min = mwindow->edl->local_session->automation_mins[autogrouptype];
3610         float automation_max = mwindow->edl->local_session->automation_maxs[autogrouptype];
3611         float automation_range = automation_max - automation_min;
3612         if( autogrouptype == AUTOGROUPTYPE_SPEED && automation_range < SPEED_MIN )
3613                 automation_range = SPEED_MIN;
3614         FloatAuto *previous1 = 0, *next1 = 0;
3615         X_TO_FLOATLINE(cursor_x);
3616
3617         if(cursor_x >= x1 &&
3618                 cursor_x < x2 &&
3619                 cursor_y >= y - HANDLE_W / 2 &&
3620                 cursor_y < y + HANDLE_W / 2 &&
3621                 !ctrl_down())
3622         {
3623                 result = 1;
3624
3625 // Menu
3626                 if(buttonpress == 3)
3627                 {
3628                 }
3629                 else
3630 // Create keyframe
3631                 if(buttonpress)
3632                 {
3633                         Auto *current;
3634                         mwindow->undo->update_undo_before();
3635                         double position = autos->track->from_units(position1);
3636                         position = mwindow->edl->align_to_frame(position, 0);
3637                         int64_t new_position = autos->track->to_units(position,0);
3638                         current = mwindow->session->drag_auto = autos->insert_auto(new_position);
3639                         ((FloatAuto*)current)->set_value(value);
3640                         mwindow->session->drag_start_percentage = value_to_percentage(value, autogrouptype);
3641                         mwindow->session->drag_start_position = current->position;
3642                         mwindow->session->drag_origin_x = cursor_x;
3643                         mwindow->session->drag_origin_y = cursor_y;
3644                         mwindow->session->drag_handle = 0;
3645                 }
3646         }
3647
3648
3649         return result;
3650 }
3651
3652 // gang=-1 for keyframepopup update, all tracks where fautos exist
3653 // gang=0 for trackcanvas drag update, all gang matching tracks, create new fautos if needed
3654 // gang=1 for trackcanvas drag update, all gang tracks, create new fautos if needed
3655 void TrackCanvas::fill_ganged_autos(int gang, float change, Track *skip, FloatAuto *fauto)
3656 {
3657         if( !skip->is_ganged() ) return;
3658 // Handles the special case of modifying a fadeauto
3659 // when there are ganged faders on several tracks
3660         double position = skip->from_units(fauto->position);
3661         int autoidx = fauto->autos->autoidx;
3662         PatchGUI *patch = gang < 0 ? pane->patchbay->get_patch_of(skip) : 0;
3663         int edge = patch ? patch->edge : 0;
3664         int span = patch ? patch->span : 0;
3665         for(Track *current = mwindow->edl->tracks->first; current; current = NEXT) {
3666                 if( current == skip || !current->is_armed() ) continue;
3667                 if( !gang && current->data_type != skip->data_type ) continue;
3668                 if( skip->armed_gang(current) || get_double_click() ) {
3669                         FloatAutos *fade_autos = (FloatAutos*)current->automation->autos[autoidx];
3670                         FloatAuto *keyframe = (FloatAuto*)fade_autos->get_auto_at_position(position);
3671                         int64_t current_position = current->to_units(position, 1);
3672                         if( keyframe ) {
3673 // keyframe exists, just change it
3674                                 keyframe->bump_update(current_position, change, edge, span);
3675                         }
3676                         else if( gang >= 0 ) {
3677 // create keyframe on neighbouring track at the point in time given by fauto
3678                                 FloatAuto *previous = 0, *next = 0;
3679                                 float value = fade_autos->get_value(current_position, PLAY_FORWARD, previous, next);
3680                                 float new_value = value + change;
3681                                 float auto_min = mwindow->edl->local_session->automation_mins[fade_autos->autogrouptype];
3682                                 float auto_max = mwindow->edl->local_session->automation_maxs[fade_autos->autogrouptype];
3683                                 bclamp(new_value, auto_min, auto_max);
3684                                 keyframe = (FloatAuto*)fade_autos->insert_auto(current_position);
3685                                 keyframe->set_value(new_value, edge);
3686                         }
3687                         else
3688                                 continue;
3689                         mwindow->session->drag_auto_gang->append((Auto *)keyframe);
3690                 }
3691         }
3692 }
3693
3694 void TrackCanvas::update_ganged_autos(float change, Track *skip, FloatAuto *fauto)
3695 {
3696         double position = skip->from_units(fauto->position);
3697 // Move the gangs
3698         for (int i = 0; i < mwindow->session->drag_auto_gang->total; i++) {
3699                 FloatAuto *keyframe = (FloatAuto *)mwindow->session->drag_auto_gang->values[i];
3700                 int64_t keyframe_position = keyframe->autos->track->to_units(position, 1);
3701                 keyframe->bump_update(keyframe_position, change, 0, 0);
3702                 keyframe->set_control_in_value(fauto->get_control_in_value());
3703                 keyframe->set_control_out_value(fauto->get_control_out_value());
3704         }
3705 }
3706
3707 void TrackCanvas::clear_ganged_autos()
3708 {
3709 // remove the gangs
3710         for (int i = 0; i < mwindow->session->drag_auto_gang->total; i++) {
3711                 FloatAuto *keyframe = (FloatAuto *)mwindow->session->drag_auto_gang->values[i];
3712                 keyframe->autos->remove_nonsequential(keyframe);
3713         }
3714         mwindow->session->drag_auto_gang->remove_all();
3715 }
3716
3717
3718 void TrackCanvas::draw_toggleline(int center_pixel,
3719         int x1,
3720         int y1,
3721         int x2,
3722         int y2)
3723 {
3724         draw_line(x1, center_pixel + y1, x2, center_pixel + y1);
3725
3726         if(y2 != y1)
3727         {
3728                 draw_line(x2, center_pixel + y1, x2, center_pixel + y2);
3729         }
3730 }
3731
3732 int TrackCanvas::test_toggleline(Autos *autos,
3733         int center_pixel,
3734         int x1,
3735         int y1,
3736         int x2,
3737         int y2,
3738         int cursor_x,
3739         int cursor_y,
3740         int buttonpress)
3741 {
3742         int result = 0;
3743         if(cursor_x >= x1 && cursor_x < x2)
3744         {
3745                 int miny = center_pixel + y1 - HANDLE_W / 2;
3746                 int maxy = center_pixel + y1 + HANDLE_W / 2;
3747                 if(cursor_y >= miny && cursor_y < maxy)
3748                 {
3749                         result = 1;
3750
3751 // Menu
3752                         if(buttonpress == 3)
3753                         {
3754                         }
3755                         else
3756 // Insert keyframe
3757                         if(buttonpress)
3758                         {
3759                                 Auto *current;
3760                                 double position = (double)(cursor_x +
3761                                                 mwindow->edl->local_session->view_start[pane->number]) *
3762                                         mwindow->edl->local_session->zoom_sample /
3763                                         mwindow->edl->session->sample_rate;
3764                                 int64_t unit_position = autos->track->to_units(position, 0);
3765                                 int new_value = (int)((IntAutos*)autos)->get_automation_constant(unit_position, unit_position);
3766
3767                                 mwindow->undo->update_undo_before();
3768
3769                                 current = mwindow->session->drag_auto = autos->insert_auto(unit_position);
3770                                 ((IntAuto*)current)->value = new_value;
3771                                 // Toggle Autos don't respond to vertical zoom, they always show up
3772                                 // with "on" == 100% == line on top
3773                                 mwindow->session->drag_start_percentage = (float)new_value;
3774                                 mwindow->session->drag_start_position = current->position;
3775                                 mwindow->session->drag_origin_x = cursor_x;
3776                                 mwindow->session->drag_origin_y = cursor_y;
3777                         }
3778                 }
3779         };
3780
3781         return result;
3782 }
3783
3784 void TrackCanvas::calculate_viewport(Track *track,
3785         double &view_start,   // Seconds
3786         double &unit_start,
3787         double &view_end,     // Seconds
3788         double &unit_end,
3789         double &yscale,
3790         int &center_pixel,
3791         double &zoom_sample,
3792         double &zoom_units)
3793 {
3794
3795         view_start = (double)mwindow->edl->local_session->view_start[pane->number] *
3796                 mwindow->edl->local_session->zoom_sample /
3797                 mwindow->edl->session->sample_rate;
3798         unit_start = track->to_doubleunits(view_start);
3799         view_end = (double)(mwindow->edl->local_session->view_start[pane->number] +
3800                 get_w()) *
3801                 mwindow->edl->local_session->zoom_sample /
3802                 mwindow->edl->session->sample_rate;
3803         unit_end = track->to_doubleunits(view_end);
3804
3805         int y = track->y_pixel
3806                         - mwindow->edl->local_session->track_start[pane->number];
3807         int has_titles = track->show_titles();
3808         int has_assets = track->show_assets();
3809         double title_bg_h = mwindow->theme->get_image("title_bg_data")->get_h();
3810         double title_h = mwindow->theme->title_h;
3811         double data_h = track->data_h;
3812         double ys = has_assets ? data_h : has_titles ? title_bg_h : 0;
3813         double dy = has_titles ?
3814                 ( has_assets ? title_bg_h + data_h/2 : title_bg_h/2) :
3815                 ( has_assets ? data_h/2 : 0) ;
3816         if( dy < title_h/2 ) { ys = title_h;  dy = ys / 2; }
3817         yscale = ys;
3818         center_pixel = y + dy;
3819
3820         zoom_sample = mwindow->edl->local_session->zoom_sample;
3821         zoom_units = track->to_doubleunits(zoom_sample / mwindow->edl->session->sample_rate);
3822 }
3823
3824 float TrackCanvas::percentage_to_value(float percentage,
3825         int is_toggle,
3826         Auto *reference,
3827         int autogrouptype)
3828 {
3829         float result;
3830         if(is_toggle)
3831         {
3832                 if(percentage > 0.5)
3833                         result = 1;
3834                 else
3835                         result = 0;
3836         }
3837         else
3838         {
3839                 float automation_min = mwindow->edl->local_session->automation_mins[autogrouptype];
3840                 float automation_max = mwindow->edl->local_session->automation_maxs[autogrouptype];
3841                 float automation_range = automation_max - automation_min;
3842
3843                 result = percentage * automation_range + automation_min;
3844                 if(reference)
3845                 {
3846                         FloatAuto *ptr = (FloatAuto*)reference;
3847                         result -= ptr->get_value();
3848                 }
3849 //printf("TrackCanvas::percentage_to_value %d %f\n", __LINE__, result);
3850         }
3851         return result;
3852 }
3853
3854
3855 void TrackCanvas::calculate_auto_position(int edge, double *x, double *y,
3856         double *in_x, double *in_y, double *out_x, double *out_y,
3857         Auto *current, double unit_start, double zoom_units, double yscale,
3858         int autogrouptype)
3859 {
3860         float automation_min = mwindow->edl->local_session->automation_mins[autogrouptype];
3861         float automation_max = mwindow->edl->local_session->automation_maxs[autogrouptype];
3862         float automation_range = automation_max - automation_min;
3863         if( autogrouptype == AUTOGROUPTYPE_SPEED && automation_range < SPEED_MIN )
3864                 automation_range = SPEED_MIN;
3865         FloatAuto *ptr = (FloatAuto*)current;
3866         *x = (double)(ptr->position - unit_start) / zoom_units;
3867         *y = ((ptr->get_value(edge) - automation_min) / automation_range - 0.5) * -yscale;
3868
3869         if(in_x) {
3870 //              *in_x = EQUIV(ptr->control_in_value, 0.0) ? *x : *x - mwindow->theme->control_pixels;
3871                 *in_x = (double)(ptr->position + ptr->get_control_in_position() - unit_start) / zoom_units;
3872         }
3873
3874         if(in_y) {
3875                 *in_y = (((ptr->get_value(edge) + ptr->get_control_in_value()) -
3876                                 automation_min) / automation_range - 0.5) * -yscale;
3877         }
3878
3879         if(out_x) {
3880 //              *out_x = EQUIV(ptr->control_out_value, 0.0) ? *x : *x + mwindow->theme->control_pixels;
3881                 *out_x = (double)(ptr->position + ptr->get_control_out_position() - unit_start) / zoom_units;
3882         }
3883
3884         if(out_y) {
3885                 *out_y = (((ptr->get_value(edge) + ptr->get_control_out_value()) -
3886                                  automation_min) / automation_range - 0.5) * -yscale;
3887         }
3888 }
3889
3890
3891 int TrackCanvas::do_float_autos(Track *track, Autos *autos, int cursor_x, int cursor_y,
3892                 int draw, int buttonpress, int x_offset, int y_offset, int color,
3893                 Auto* &auto_instance, int autogrouptype)
3894 {
3895         int result = 0;
3896         int center_pixel, draw_auto;
3897         double view_start, unit_start;
3898         double view_end, unit_end, yscale;
3899         double zoom_sample, zoom_units;
3900         double slope;
3901         //int skip = 0;
3902
3903         auto_instance = 0;
3904
3905         if(draw) set_color(color);
3906
3907         calculate_viewport(track, view_start, unit_start, view_end, unit_end,
3908                         yscale, center_pixel, zoom_sample, zoom_units);
3909
3910 // Get first auto before start
3911         Auto *current = 0, *previous = 0;
3912
3913         for( current = autos->last;
3914                 current && current->position >= unit_start;
3915                 current = PREVIOUS ) ;
3916
3917         Auto *first_auto = current ? current :
3918                  autos->first ? autos->first : autos->default_auto;
3919
3920         double ax = 0, ay = 0, ax2 = 0, ay2 = 0;
3921         if( first_auto ) {
3922                 calculate_auto_position(0, &ax, &ay, 0, 0, 0, 0,
3923                         first_auto, unit_start, zoom_units, yscale, autogrouptype);
3924         }
3925         if( current )
3926                 current = NEXT;
3927         else {
3928                 current = autos->first;
3929                 ax = 0;
3930         }
3931
3932         do {
3933                 //skip = 0;
3934                 draw_auto = 1;
3935
3936                 if(current) {
3937                         calculate_auto_position(1, &ax2, &ay2, 0, 0, 0, 0,
3938                                 current, unit_start, zoom_units, yscale, autogrouptype);
3939                 }
3940                 else {
3941                         ax2 = get_w();
3942                         ay2 = ay;
3943                         //skip = 1;
3944                 }
3945
3946                 slope = ax2 > ax ? (ay2 - ay) / (ax2 - ax) : 0;
3947
3948                 if(ax2 > get_w()) {
3949                         draw_auto = 0;
3950                         ax2 = get_w();
3951                         ay2 = ay + slope * (get_w() - ax);
3952                 }
3953
3954                 if(ax < 0) {
3955                         ay = ay + slope * (0 - ax);
3956                         ax = 0;
3957                 }
3958
3959 // Draw or test handle
3960                 if( current && !result && current != autos->default_auto ) {
3961                         if( !draw && track->is_armed() ) {
3962                                 result = test_floatauto((FloatAuto*)current, buttonpress,
3963                                         (int)center_pixel, (int)yscale, cursor_x, cursor_y,
3964                                         unit_start, zoom_units, yscale, autogrouptype);
3965                                 if( result )
3966                                         auto_instance = current;
3967                         }
3968                         if( draw && draw_auto ) {
3969                                 draw_floatauto((FloatAuto*)current, x_offset,
3970                                         (int)center_pixel + y_offset, (int)yscale, color,
3971                                         unit_start, zoom_units, yscale, autogrouptype);
3972                         }
3973                 }
3974
3975 // Draw or test joining line
3976                 if( !draw && !result && track->is_armed() /* && buttonpress != 3 */ ) {
3977                         result = test_floatline(center_pixel,
3978                                 (FloatAutos*)autos, unit_start, zoom_units, yscale,
3979 // Exclude auto coverage from the end of the line.  The auto overlaps
3980                                 (int)ax, (int)ax2 - HANDLE_W / 2, cursor_x, cursor_y,
3981                                 buttonpress, autogrouptype);
3982                 }
3983                 if( draw )
3984                         draw_floatline(center_pixel, (FloatAuto*)previous, (FloatAuto*)current,
3985                                 (FloatAutos*)autos, unit_start, zoom_units, yscale,
3986                                 (int)ax, (int)ay, (int)ax2, (int)ay2,
3987                                 color, autogrouptype);
3988
3989                 if( current ) {
3990                         previous = current;
3991                         calculate_auto_position(0, &ax2, &ay2, 0, 0, 0, 0, previous,
3992                                 unit_start, zoom_units, yscale, autogrouptype);
3993                         current = NEXT;
3994                 }
3995                 ax = ax2;  ay = ay2;
3996         } while( current && current->position <= unit_end && !result );
3997
3998         if( ax < get_w() && !result ) {
3999                 ax2 = get_w();  ay2 = ay;
4000                 if(!draw && track->is_armed() /* && buttonpress != 3 */ ) {
4001                         result = test_floatline(center_pixel,
4002                                 (FloatAutos*)autos, unit_start, zoom_units, yscale,
4003                                 (int)ax, (int)ax2, cursor_x, cursor_y,
4004                                 buttonpress, autogrouptype);
4005                 }
4006                 if( draw )
4007                         draw_floatline(center_pixel,
4008                                 (FloatAuto*)previous, (FloatAuto*)current,
4009                                 (FloatAutos*)autos, unit_start, zoom_units, yscale,
4010                                 (int)ax, (int)ay, (int)ax2, (int)ay2,
4011                                 color, autogrouptype);
4012         }
4013
4014         return result;
4015 }
4016
4017
4018 int TrackCanvas::do_int_autos(Track *track,
4019                 Autos *autos,
4020                 int cursor_x,
4021                 int cursor_y,
4022                 int draw,
4023                 int buttonpress,
4024                 int x_offset,
4025                 int y_offset,
4026                 int color,
4027                 Auto *&auto_instance)
4028 {
4029         int result = 0;
4030         double view_start;
4031         double unit_start;
4032         double view_end;
4033         double unit_end;
4034         double yscale;
4035         int center_pixel;
4036         double zoom_sample;
4037         double zoom_units;
4038         double ax, ay, ax2, ay2;
4039
4040         auto_instance = 0;
4041
4042         if(draw) set_color(color);
4043
4044         calculate_viewport(track,
4045                 view_start,
4046                 unit_start,
4047                 view_end,
4048                 unit_end,
4049                 yscale,
4050                 center_pixel,
4051                 zoom_sample,
4052                 zoom_units);
4053
4054
4055         double high = -yscale * 0.8 / 2;
4056         double low = yscale * 0.8 / 2;
4057
4058 // Get first auto before start
4059         Auto *current;
4060         for(current = autos->last; current && current->position >= unit_start; current = PREVIOUS)
4061                 ;
4062
4063         if(current)
4064         {
4065                 ax = 0;
4066                 ay = ((IntAuto*)current)->value > 0 ? high : low;
4067                 current = NEXT;
4068         }
4069         else
4070         {
4071                 current = autos->first ? autos->first : autos->default_auto;
4072                 if(current)
4073                 {
4074                         ax = 0;
4075                         ay = ((IntAuto*)current)->value > 0 ? high : low;
4076                 }
4077                 else
4078                 {
4079                         ax = 0;
4080                         ay = yscale;
4081                 }
4082         }
4083
4084         do
4085         {
4086                 if(current)
4087                 {
4088                         ax2 = (double)(current->position - unit_start) / zoom_units;
4089                         ay2 = ((IntAuto*)current)->value > 0 ? high : low;
4090                 }
4091                 else
4092                 {
4093                         ax2 = get_w();
4094                         ay2 = ay;
4095                 }
4096
4097                 if(ax2 > get_w()) ax2 = get_w();
4098
4099                 if(current && !result)
4100                 {
4101                         if(current != autos->default_auto)
4102                         {
4103                                 if(!draw)
4104                                 {
4105                                         if(track->is_armed())
4106                                         {
4107                                                 result = test_auto(current,
4108                                                         (int)ax2,
4109                                                         (int)ay2,
4110                                                         (int)center_pixel,
4111                                                         (int)yscale,
4112                                                         cursor_x,
4113                                                         cursor_y,
4114                                                         buttonpress);
4115                                                 if (result)
4116                                                         auto_instance = current;
4117                                         }
4118                                 }
4119                                 else
4120                                         draw_auto(current,
4121                                                 (int)ax2 + x_offset,
4122                                                 (int)ay2 + y_offset,
4123                                                 (int)center_pixel,
4124                                                 (int)yscale);
4125                         }
4126
4127                         current = NEXT;
4128                 }
4129
4130                 if(!draw)
4131                 {
4132                         if(!result)
4133                         {
4134                                 if(track->is_armed() /* && buttonpress != 3 */)
4135                                 {
4136                                         result = test_toggleline(autos,
4137                                                 center_pixel,
4138                                                 (int)ax,
4139                                                 (int)ay,
4140                                                 (int)ax2,
4141                                                 (int)ay2,
4142                                                 cursor_x,
4143                                                 cursor_y,
4144                                                 buttonpress);
4145                                 }
4146                         }
4147                 }
4148                 else
4149                         draw_toggleline(center_pixel + y_offset,
4150                                 (int)ax,
4151                                 (int)ay,
4152                                 (int)ax2,
4153                                 (int)ay2);
4154
4155                 ax = ax2;
4156                 ay = ay2;
4157         }while(current && current->position <= unit_end && !result);
4158
4159         if(ax < get_w() && !result)
4160         {
4161                 ax2 = get_w();
4162                 ay2 = ay;
4163                 if(!draw)
4164                 {
4165                         if(track->is_armed() /* && buttonpress != 3 */)
4166                         {
4167                                 result = test_toggleline(autos,
4168                                         center_pixel,
4169                                         (int)ax,
4170                                         (int)ay,
4171                                         (int)ax2,
4172                                         (int)ay2,
4173                                         cursor_x,
4174                                         cursor_y,
4175                                         buttonpress);
4176                         }
4177                 }
4178                 else
4179                         draw_toggleline(center_pixel + y_offset,
4180                                 (int)ax,
4181                                 (int)ay,
4182                                 (int)ax2,
4183                                 (int)ay2);
4184         }
4185         return result;
4186 }
4187
4188 int TrackCanvas::do_autos(Track *track,
4189                 Autos *autos,
4190                 int cursor_x,
4191                 int cursor_y,
4192                 int draw,
4193                 int buttonpress,
4194                 BC_Pixmap *pixmap,
4195                 Auto * &auto_instance,
4196                 int &rerender)
4197 {
4198         int result = 0;
4199
4200         double view_start;
4201         double unit_start;
4202         double view_end;
4203         double unit_end;
4204         double yscale;
4205         int center_pixel;
4206         double zoom_sample;
4207         double zoom_units;
4208
4209         calculate_viewport(track,
4210                 view_start,
4211                 unit_start,
4212                 view_end,
4213                 unit_end,
4214                 yscale,
4215                 center_pixel,
4216                 zoom_sample,
4217                 zoom_units);
4218
4219         Auto *current;
4220         auto_instance = 0;
4221
4222         for(current = autos->first; current && !result; current = NEXT)
4223         {
4224                 if(current->position >= unit_start && current->position < unit_end)
4225                 {
4226                         int64_t x, y;
4227                         x = (int64_t)((double)(current->position - unit_start) /
4228                                 zoom_units - (pixmap->get_w() / 2.0 + 0.5));
4229                         y = center_pixel - pixmap->get_h() / 2;
4230
4231                         if(!draw)
4232                         {
4233                                 if(cursor_x >= x && cursor_y >= y &&
4234                                         cursor_x < x + pixmap->get_w() &&
4235                                         cursor_y < y + pixmap->get_h())
4236                                 {
4237                                         result = 1;
4238                                         auto_instance = current;
4239
4240                                         if(buttonpress && (buttonpress != 3))
4241                                         {
4242                                                 mwindow->session->drag_auto = current;
4243                                                 mwindow->session->drag_start_position = current->position;
4244                                                 mwindow->session->drag_origin_x = cursor_x;
4245                                                 mwindow->session->drag_origin_y = cursor_y;
4246
4247                                                 double position = autos->track->from_units(current->position);
4248                                                 double center = (mwindow->edl->local_session->get_selectionstart(1) +
4249                                                         mwindow->edl->local_session->get_selectionend(1)) /
4250                                                         2;
4251
4252                                                 if(!shift_down())
4253                                                 {
4254                                                         mwindow->edl->local_session->set_selectionstart(position);
4255                                                         mwindow->edl->local_session->set_selectionend(position);
4256                                                 }
4257                                                 else
4258                                                 if(position < center)
4259                                                 {
4260                                                         mwindow->edl->local_session->set_selectionstart(position);
4261                                                 }
4262                                                 else
4263                                                         mwindow->edl->local_session->set_selectionend(position);
4264
4265                                                 rerender = 1;
4266                                         }
4267                                 }
4268                         }
4269                         else
4270                                 draw_pixmap(pixmap, x, y);
4271                 }
4272         }
4273
4274
4275         return result;
4276 }
4277
4278 // so this means it is always >0 when keyframe is found
4279 int TrackCanvas::do_plugin_autos(Track *track, int cursor_x, int cursor_y,
4280                 int draw, int buttonpress, Plugin* &keyframe_plugin,
4281                 KeyFrame* &keyframe_instance)
4282 {
4283         int result = 0;
4284
4285         double view_start;
4286         double unit_start;
4287         double view_end;
4288         double unit_end;
4289         double yscale;
4290         int center_pixel;
4291         double zoom_sample;
4292         double zoom_units;
4293
4294         if(!track->expand_view) return 0;
4295
4296         calculate_viewport(track,
4297                 view_start,
4298                 unit_start,
4299                 view_end,
4300                 unit_end,
4301                 yscale,
4302                 center_pixel,
4303                 zoom_sample,
4304                 zoom_units);
4305
4306
4307
4308         for(int i = 0; i < track->plugin_set.total && !result; i++)
4309         {
4310                 PluginSet *plugin_set = track->plugin_set.values[i];
4311                 int center_pixel = track->y_pixel -
4312                         mwindow->edl->local_session->track_start[pane->number];
4313                 if( track->show_titles() )
4314                         center_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
4315                 if( track->show_assets() )
4316                         center_pixel += track->data_h;
4317                 center_pixel += (i + 0.5) * mwindow->theme->get_image("plugin_bg_data")->get_h();
4318
4319                 for(Plugin *plugin = (Plugin*)plugin_set->first;
4320                         plugin && !result;
4321                         plugin = (Plugin*)plugin->next)
4322                 {
4323                         for(KeyFrame *keyframe = (KeyFrame*)plugin->keyframes->first;
4324                                 keyframe && !result;
4325                                 keyframe = (KeyFrame*)keyframe->next)
4326                         {
4327 //printf("TrackCanvas::draw_plugin_autos 3 %d\n", keyframe->position);
4328                                 if(keyframe->position >= unit_start && keyframe->position < unit_end)
4329                                 {
4330                                         int64_t x = (int64_t)((keyframe->position - unit_start) / zoom_units);
4331                                         int y = center_pixel - keyframe_pixmap->get_h() / 2;
4332
4333 //printf("TrackCanvas::draw_plugin_autos 4 %d %d\n", x, center_pixel);
4334                                         if(!draw)
4335                                         {
4336                                                 if(cursor_x >= x && cursor_y >= y &&
4337                                                         cursor_x < x + keyframe_pixmap->get_w() &&
4338                                                         cursor_y < y + keyframe_pixmap->get_h())
4339                                                 {
4340                                                         result = 1;
4341                                                         keyframe_plugin = plugin;
4342                                                         keyframe_instance = keyframe;
4343
4344                                                         if(buttonpress)
4345                                                         {
4346                                                                 mwindow->session->drag_auto = keyframe;
4347                                                                 mwindow->session->drag_start_position = keyframe->position;
4348                                                                 mwindow->session->drag_origin_x = cursor_x;
4349                                                                 mwindow->session->drag_origin_y = cursor_y;
4350
4351                                                                 double position = track->from_units(keyframe->position);
4352                                                                 double center = (mwindow->edl->local_session->get_selectionstart(1) +
4353                                                                         mwindow->edl->local_session->get_selectionend(1)) /
4354                                                                         2;
4355
4356                                                                 if(!shift_down())
4357                                                                 {
4358                                                                         mwindow->edl->local_session->set_selectionstart(position);
4359                                                                         mwindow->edl->local_session->set_selectionend(position);
4360                                                                 }
4361                                                                 else
4362                                                                 if(position < center)
4363                                                                 {
4364                                                                         mwindow->edl->local_session->set_selectionstart(position);
4365                                                                 }
4366                                                                 else
4367                                                                         mwindow->edl->local_session->set_selectionend(position);
4368                                                         }
4369                                                 }
4370                                         }
4371                                         else
4372                                                 draw_pixmap(keyframe_pixmap,
4373                                                         x,
4374                                                         y);
4375                                 }
4376                         }
4377                 }
4378         }
4379
4380
4381
4382 //      if(buttonpress && buttonpress != 3 && result)
4383 //      {
4384 //              mwindow->undo->update_undo_before();
4385 //      }
4386
4387         return result;
4388 }
4389
4390 int TrackCanvas::draw_hairline(Auto *auto_keyframe, int color, int show)
4391 {
4392         Track *track = auto_keyframe->autos->track;
4393         int autogrouptype = auto_keyframe->autos->autogrouptype;
4394
4395         int center_pixel;
4396         double view_start, unit_start;
4397         double view_end, unit_end, yscale;
4398         double zoom_sample, zoom_units;
4399
4400         calculate_viewport(track, view_start, unit_start, view_end, unit_end,
4401                         yscale, center_pixel, zoom_sample, zoom_units);
4402         if( auto_keyframe->position < unit_start ||
4403             auto_keyframe->position >= unit_end )
4404                 return 0;
4405
4406         double ax = 0, ay = 0;
4407         calculate_auto_position(0, &ax, &ay, 0, 0, 0, 0,
4408                 auto_keyframe, unit_start, zoom_units, yscale, autogrouptype);
4409
4410         set_color(color);
4411         draw_line(ax, 0, ax, get_h());
4412
4413         char text[BCSTRLEN];
4414         if( show ) {
4415                 if( auto_keyframe->is_floatauto() ) {
4416                         FloatAuto *float_auto = (FloatAuto *)auto_keyframe;
4417                         sprintf(text, "%0.2f", float_auto->get_value());
4418                 }
4419                 else if( auto_keyframe->is_intauto() ) {
4420                         IntAuto *int_auto = (IntAuto *)auto_keyframe;
4421                         sprintf(text, "%d", int_auto->value);
4422                 }
4423                 else
4424                         show = 0;
4425         }
4426         if( show ) {
4427                 int font = MEDIUMFONT;
4428                 int tw = get_text_width(font, text)  + xS(TOOLTIP_MARGIN) * 2;
4429                 int th = get_text_height(font, text) + yS(TOOLTIP_MARGIN) * 2;
4430                 set_color(get_resources()->tooltip_bg_color);
4431                 ax += HANDLE_W/2;
4432                 ay += center_pixel + HANDLE_W/2;
4433                 draw_box(ax, ay, tw, th);
4434                 set_color(BLACK);
4435                 draw_rectangle(ax, ay, tw, th);
4436                 set_font(font);
4437                 ax += xS(TOOLTIP_MARGIN);
4438                 ay += yS(TOOLTIP_MARGIN) + get_text_ascent(font);
4439                 draw_text(ax, ay, text);
4440         }
4441         return 0;
4442 }
4443
4444 void TrackCanvas::draw_overlays()
4445 {
4446         int new_cursor, update_cursor, rerender;
4447
4448 // Move background pixmap to foreground pixmap
4449         draw_pixmap(background_pixmap, 0, 0, get_w(), get_h(), 0, 0);
4450
4451 // In/Out points
4452         draw_inout_points();
4453
4454 // Transitions
4455         draw_transitions();
4456
4457 // Plugins
4458         draw_plugins();
4459         draw_hard_edges();
4460
4461 // Loop points
4462         draw_loop_points();
4463         draw_brender_range();
4464
4465 // Highlighted areas
4466         draw_highlighting();
4467
4468 // Automation
4469         do_keyframes(0, 0, 1, 0, new_cursor, update_cursor, rerender);
4470
4471 // Selection cursor
4472         if(pane->cursor) pane->cursor->restore(1);
4473
4474 // Handle dragging
4475         draw_drag_handle();
4476
4477 // Playback cursor
4478         draw_playback_cursor();
4479
4480         draw_keyframe_reticle();
4481
4482         show_window(0);
4483 }
4484
4485 int TrackCanvas::activate()
4486 {
4487         if(!active)
4488         {
4489 //printf("TrackCanvas::activate %d %d\n", __LINE__, pane->number);
4490 //BC_Signals::dump_stack();
4491                 get_top_level()->deactivate();
4492                 active = 1;
4493                 set_active_subwindow(this);
4494                 pane->cursor->activate();
4495                 gui->focused_pane = pane->number;
4496         }
4497         return 0;
4498 }
4499
4500 int TrackCanvas::deactivate()
4501 {
4502         if(active)
4503         {
4504                 active = 0;
4505                 pane->cursor->deactivate();
4506         }
4507         return 0;
4508 }
4509
4510
4511 void TrackCanvas::update_drag_handle()
4512 {
4513         double new_position;
4514         int cursor_x = get_cursor_x();
4515
4516         new_position = mwindow->edl->get_cursor_position(cursor_x, pane->number);
4517         new_position = mwindow->edl->align_to_frame(new_position, 0);
4518
4519         if( ctrl_down() && alt_down() ) {
4520 #define snapper(v) do { \
4521         double pos = (v); \
4522         if( pos < 0 ) break; \
4523         double dist = fabs(new_position - pos); \
4524         if( dist >= snap_min ) break; \
4525         snap_position = pos;  snap_min = dist; \
4526 } while(0)
4527                 double snap_position = new_position;
4528                 double snap_min = DBL_MAX;
4529                 if( mwindow->edl->local_session->inpoint_valid() )
4530                         snapper(mwindow->edl->local_session->get_inpoint());
4531                 if( mwindow->edl->local_session->outpoint_valid() )
4532                         snapper(mwindow->edl->local_session->get_outpoint());
4533                 snapper(mwindow->edl->prev_edit(new_position));
4534                 snapper(mwindow->edl->next_edit(new_position));
4535                 Label *prev_label = mwindow->edl->labels->prev_label(new_position);
4536                 if( prev_label ) snapper(prev_label->position);
4537                 Label *next_label = mwindow->edl->labels->next_label(new_position);
4538                 if( next_label ) snapper(next_label->position);
4539                 int snap_x = snap_position * mwindow->edl->session->sample_rate /
4540                         mwindow->edl->local_session->zoom_sample -
4541                         mwindow->edl->local_session->view_start[pane->number];
4542                 if( abs(snap_x - cursor_x) < HANDLE_W ) {
4543                         snapped = 1;
4544                         new_position = snap_position;
4545                 }
4546 #undef snapper
4547         }
4548
4549         if(new_position != mwindow->session->drag_position)
4550         {
4551                 mwindow->session->drag_position = new_position;
4552                 gui->mainclock->update(new_position);
4553                 timebar_position = new_position;
4554                 gui->update_timebar(0);
4555
4556                 EDL *edl = new EDL;
4557                 edl->create_objects();
4558                 edl->copy_all(mwindow->edl);
4559                 MainSession *session = mwindow->session;
4560                 int handle_mode = mwindow->edl->session->edit_handle_mode[session->drag_button];
4561                 edl->modify_edithandles(session->drag_start,
4562                         session->drag_position, session->drag_handle, handle_mode,
4563                         edl->session->labels_follow_edits,
4564                         edl->session->plugins_follow_edits,
4565                         edl->session->autos_follow_edits,
4566                         !session->drag_edit ? 0 : session->drag_edit->group_id);
4567
4568                 double position = -1;
4569                 int show_edge = !session->drag_handle ? 1 : 2;
4570                 switch( handle_mode ) {
4571                 case MOVE_RIPPLE:
4572                 case MOVE_EDGE:
4573                         position = session->drag_handle ?
4574                                 session->drag_position : session->drag_start;
4575                         show_edge = 3 - show_edge;
4576                         break;
4577                 case MOVE_ROLL:
4578                 case MOVE_SLIDE:
4579                         position = session->drag_position;
4580                         break;
4581                 case MOVE_SLIP:
4582                         position = session->drag_start;
4583                         show_edge = 3 - show_edge;
4584                         break;
4585                 }
4586
4587                 if( position < 0 ) position = 0;
4588                 Track *track = session->drag_handle_track();
4589                 int64_t pos = track->to_units(position, 0);
4590                 render_handle_frame(edl, pos, shift_down() ? 0 : show_edge);
4591                 edl->remove_user();
4592         }
4593 }
4594
4595 int TrackCanvas::render_handle_frame(EDL *edl, int64_t pos, int mode)
4596 {
4597         int result = 0;
4598         int64_t left = pos-1;
4599         if( left < 0 ) left = 0;
4600         switch( mode ) {
4601         case 0: {
4602                 VFrame vlt(edl->get_w(), edl->get_h(), edl->session->color_model);
4603                 VFrame vrt(edl->get_w(), edl->get_h(), edl->session->color_model);
4604                 TransportCommand command;
4605                 command.command = CURRENT_FRAME;
4606                 command.get_edl()->copy_all((EDL *)edl);
4607                 command.change_type = CHANGE_ALL;
4608                 command.realtime = 0;
4609                 Preferences *preferences = mwindow->preferences;
4610                 RenderEngine *render_engine = new RenderEngine(0, preferences, 0, 0);
4611                 CICache *video_cache = new CICache(preferences);
4612                 render_engine->set_vcache(video_cache);
4613                 render_engine->arm_command(&command);
4614                 VRender *vrender = render_engine->vrender;
4615                 result = vrender &&
4616                         !vrender->process_buffer(&vlt, left, 0) &&
4617                         !vrender->process_buffer(&vrt, pos , 0) ? 0 : 1;
4618                 delete render_engine;
4619                 video_cache->remove_user();
4620                 mwindow->cwindow->gui->lock_window("TrackCanvas::render_handle_frame 0");
4621                 Canvas *canvas = mwindow->cwindow->gui->canvas;
4622                 float ox1, oy1, ox2, oy2, cx1, cy1, cx2, cy2;
4623                 canvas->get_transfers(edl, ox1, oy1, ox2, oy2, cx1, cy1, cx2, cy2);
4624                 float cw = canvas->w/2, ch = canvas->h/2;
4625                 float cdx = (cx2 - cx1)/2, cdy = (cy2 - cy1)/2;
4626                 int cx = cx1/2, cy = cy1/2 + ch/2;
4627                 int ow = ox2 - ox2, oh = oy2 - oy1;
4628                 BC_WindowBase *window = canvas->get_canvas();
4629                 window->set_color(BLACK);
4630                 window->clear_box(0,0, window->get_w(),window->get_h());
4631                 window->draw_vframe(&vlt, cx,cy, cdx,cdy, ox1,oy1, ow,oh);  cx += cw;
4632                 window->draw_vframe(&vrt, cx,cy, cdx,cdy, ox1,oy1, ow,oh);
4633                 window->flash(1);
4634                 mwindow->cwindow->gui->unlock_window();
4635                 break; }
4636         case 1:
4637         case 2: {
4638                 Track *track = mwindow->session->drag_handle_track();
4639                 double position = track->from_units(mode == 1 ? left : pos);
4640                 if( position < 0 ) position = 0;
4641                 PlaybackEngine *playback_engine = mwindow->cwindow->playback_engine;
4642                 if( playback_engine->is_playing_back )
4643                         playback_engine->stop_playback(1);
4644                 edl->local_session->set_selectionstart(position);
4645                 edl->local_session->set_selectionend(position);
4646                 mwindow->cwindow->playback_engine->refresh_frame(CHANGE_EDL, edl, 0);
4647                 break; }
4648         }
4649         return result;
4650 }
4651
4652 int TrackCanvas::update_drag_edit()
4653 {
4654         int result = 0;
4655
4656
4657
4658         return result;
4659 }
4660
4661 int TrackCanvas::get_drag_values(float *percentage,
4662         int64_t *position,
4663         int do_clamp,
4664         int cursor_x,
4665         int cursor_y,
4666         Auto *current)
4667 {
4668         //int x = cursor_x - mwindow->session->drag_origin_x;
4669         //int y = cursor_y - mwindow->session->drag_origin_y;
4670         *percentage = 0;
4671         *position = 0;
4672
4673         if(!current->autos->track->is_armed()) return 1;
4674         double view_start;
4675         double unit_start;
4676         double view_end;
4677         double unit_end;
4678         double yscale;
4679         int center_pixel;
4680         double zoom_sample;
4681         double zoom_units;
4682
4683         calculate_viewport(current->autos->track,
4684                 view_start,
4685                 unit_start,
4686                 view_end,
4687                 unit_end,
4688                 yscale,
4689                 center_pixel,
4690                 zoom_sample,
4691                 zoom_units);
4692
4693         *percentage = (float)(mwindow->session->drag_origin_y - cursor_y) /
4694                 yscale +
4695                 mwindow->session->drag_start_percentage;
4696         if(do_clamp) CLAMP(*percentage, 0, 1);
4697
4698         *position = Units::to_int64(zoom_units *
4699                 (cursor_x - mwindow->session->drag_origin_x) +
4700                 mwindow->session->drag_start_position + 0.5);
4701
4702         if((do_clamp) && *position < 0) *position = 0;
4703         return 0;
4704 }
4705
4706
4707 #define UPDATE_DRAG_HEAD(do_clamp) \
4708         int result = 0, center_pixel; \
4709         if(!current->autos->track->is_armed()) return 0; \
4710         double view_start, unit_start, view_end, unit_end; \
4711         double yscale, zoom_sample, zoom_units; \
4712  \
4713         calculate_viewport(current->autos->track,  \
4714                 view_start, unit_start, view_end, unit_end, \
4715                 yscale, center_pixel, zoom_sample, zoom_units); \
4716  \
4717         float percentage = (float)(mwindow->session->drag_origin_y - cursor_y) / \
4718                 yscale +  mwindow->session->drag_start_percentage; \
4719         if(do_clamp) CLAMP(percentage, 0, 1); \
4720  \
4721         int64_t position = Units::to_int64(zoom_units * \
4722                 (cursor_x - mwindow->session->drag_origin_x) + \
4723                 mwindow->session->drag_start_position); \
4724         if((do_clamp) && position < 0) position = 0;
4725
4726
4727 int TrackCanvas::update_drag_floatauto(int cursor_x, int cursor_y)
4728 {
4729         FloatAuto *current = (FloatAuto*)mwindow->session->drag_auto;
4730
4731         UPDATE_DRAG_HEAD(mwindow->session->drag_handle == 0);
4732         int x = cursor_x - mwindow->session->drag_origin_x;
4733         int y = cursor_y - mwindow->session->drag_origin_y;
4734         float value, old_value;
4735
4736         if( mwindow->session->drag_handle == 0 && (ctrl_down() && !shift_down()) ) {
4737 // not really editing the node, rather start editing the curve
4738 // tangent is editable and drag movement is significant
4739                 if( (FloatAuto::FREE == current->curve_mode ||
4740                      FloatAuto::TFREE==current->curve_mode ||
4741                      FloatAuto::BUMP==current->curve_mode) &&
4742                     (fabs(x) > HANDLE_W / 2 || fabs(y) > HANDLE_W / 2))
4743                         mwindow->session->drag_handle = x < 0 ? 1 : 2;
4744         }
4745
4746         switch(mwindow->session->drag_handle) {
4747         case 0: // Center
4748 // Snap to nearby values
4749                 old_value = current->get_value();
4750                 if(shift_down()) {
4751                         double distance1=-1, distance2=-1;
4752                         if(current->previous) {
4753                                 int autogrouptype = current->previous->autos->autogrouptype;
4754                                 value = percentage_to_value(percentage, 0, 0, autogrouptype);
4755                                 double value1 = ((FloatAuto*)current->previous)->get_value(0);
4756                                 distance1 = fabs(value - value1);
4757                                 current->set_value(value1, 1);
4758                         }
4759
4760                         if(current->next) {
4761                                 int autogrouptype = current->next->autos->autogrouptype;
4762                                 value = percentage_to_value(percentage, 0, 0, autogrouptype);
4763                                 double value2 = ((FloatAuto*)current->next)->get_value(1);
4764                                 distance2 = fabs(value - value2);
4765                                 if(!current->previous || distance2 < distance1) {
4766                                         current->set_value(value2, 0);
4767                                 }
4768                         }
4769
4770                         if(!current->previous && !current->next) {
4771                                 current->set_value(((FloatAutos*)current->autos)->default_, 0);
4772                         }
4773                         value = current->get_value();
4774                 }
4775                 else {
4776                         int autogrouptype = current->autos->autogrouptype;
4777                         value = percentage_to_value(percentage, 0, 0, autogrouptype);
4778                 }
4779
4780                 if(alt_down() && !shift_down())
4781 // ALT constrains movement: fixed position, only changing the value
4782                         position = mwindow->session->drag_start_position;
4783
4784                 if(value != old_value || position != current->position) {
4785                         result = 1;
4786                         float change = value - old_value;
4787                         current->adjust_to_new_coordinates(position, value);
4788                         update_ganged_autos(change, current->autos->track, current);
4789                         int color = GWindowGUI::auto_colors[current->autos->autoidx];
4790                         show_message(current, color, ", %.2f", current->get_value());
4791                 }
4792                 break;
4793
4794 // In control
4795         case 1: {
4796                 int autogrouptype = current->autos->autogrouptype;
4797                 value = percentage_to_value(percentage, 0, current, autogrouptype);
4798                 if(value != current->get_control_in_value())
4799                 {
4800                         result = 1;
4801                         // note: (position,value) need not be at the location of the ctrl point,
4802                         // but could be somewhere in between on the curve (or even outward or
4803                         // on the opposit side). We set the new control point such as
4804                         // to point the curve through (position,value)
4805                         current->set_control_in_value(
4806                                 value * levered_position(position - current->position,
4807                                                          current->get_control_in_position()));
4808                         update_ganged_autos(0, current->autos->track, current);
4809                         int color = GWindowGUI::auto_colors[current->autos->autoidx];
4810                         show_message(current, color, ", %.2f", current->get_control_in_value());
4811                 }
4812                 break; }
4813
4814 // Out control
4815         case 2: {
4816                 int autogrouptype = current->autos->autogrouptype;
4817                 value = percentage_to_value(percentage, 0, current, autogrouptype);
4818                 if(value != current->get_control_out_value()) {
4819                         result = 1;
4820                         current->set_control_out_value(
4821                                 value * levered_position(position - current->position,
4822                                                          current->get_control_out_position()));
4823                         update_ganged_autos(0, current->autos->track, current);
4824                         int color = GWindowGUI::auto_colors[current->autos->autoidx];
4825                         show_message(current, color, ", %.2f", current->get_control_out_value());
4826                 }
4827                 break; }
4828         }
4829
4830         return result;
4831 }
4832
4833 int TrackCanvas::update_drag_toggleauto(int cursor_x, int cursor_y)
4834 {
4835         IntAuto *current = (IntAuto*)mwindow->session->drag_auto;
4836
4837         UPDATE_DRAG_HEAD(1);
4838         int value = (int)percentage_to_value(percentage, 1, 0, AUTOGROUPTYPE_INT255);
4839
4840         if(value != current->value || position != current->position)
4841         {
4842                 result = 1;
4843                 current->value = value;
4844                 current->position = position;
4845                 show_message(current, -1, ", %d", current->value);
4846         }
4847
4848         return result;
4849 }
4850
4851 // Autos which can't change value through dragging.
4852
4853 int TrackCanvas::update_drag_auto(int cursor_x, int cursor_y)
4854 {
4855         Auto *current = (Auto*)mwindow->session->drag_auto;
4856
4857         UPDATE_DRAG_HEAD(1)
4858         if(position != current->position)
4859         {
4860                 result = 1;
4861                 current->position = position;
4862                 show_message(current, -1, "");
4863
4864                 double position_f = current->autos->track->from_units(current->position);
4865                 double center_f = (mwindow->edl->local_session->get_selectionstart(1) +
4866                         mwindow->edl->local_session->get_selectionend(1)) /
4867                         2;
4868                 if(!shift_down())
4869                 {
4870                         mwindow->edl->local_session->set_selectionstart(position_f);
4871                         mwindow->edl->local_session->set_selectionend(position_f);
4872                 }
4873                 else
4874                 if(position_f < center_f)
4875                 {
4876                         mwindow->edl->local_session->set_selectionstart(position_f);
4877                 }
4878                 else
4879                         mwindow->edl->local_session->set_selectionend(position_f);
4880         }
4881
4882
4883         return result;
4884 }
4885
4886 int TrackCanvas::update_drag_pluginauto(int cursor_x, int cursor_y)
4887 {
4888         KeyFrame *current = (KeyFrame*)mwindow->session->drag_auto;
4889
4890         UPDATE_DRAG_HEAD(1)
4891         if(position != current->position)
4892         {
4893 //      printf("uida: autos: %p, track: %p ta: %p\n", current->autos, current->autos->track, current->autos->track->automation);
4894                 Track *track = current->autos->track;
4895                 //PluginAutos *pluginautos = (PluginAutos *)current->autos;
4896                 PluginSet *pluginset;
4897                 Plugin *plugin = 0;
4898 // figure out the correct pluginset & correct plugin
4899                 int found = 0;
4900                 for(int i = 0; i < track->plugin_set.total; i++)
4901                 {
4902                         pluginset = track->plugin_set.values[i];
4903                         for(plugin = (Plugin *)pluginset->first; plugin; plugin = (Plugin *)plugin->next)
4904                         {
4905                                 KeyFrames *keyframes = plugin->keyframes;
4906                                 for(KeyFrame *currentkeyframe = (KeyFrame *)keyframes->first;
4907                                         currentkeyframe;
4908                                         currentkeyframe = (KeyFrame *) currentkeyframe->next)
4909                                 {
4910                                         if (currentkeyframe == current)
4911                                         {
4912                                                 found = 1;
4913                                                 break;
4914                                         }
4915
4916                                 }
4917                                 if (found) break;
4918                         }
4919                         if (found) break;
4920                 }
4921
4922                 mwindow->session->plugin_highlighted = plugin;
4923                 mwindow->session->track_highlighted = track;
4924                 result = 1;
4925                 current->position = position;
4926                 show_message(current, -1, "");
4927
4928                 double position_f = current->autos->track->from_units(current->position);
4929                 double center_f = (mwindow->edl->local_session->get_selectionstart(1) +
4930                         mwindow->edl->local_session->get_selectionend(1)) /
4931                         2;
4932                 if(!shift_down())
4933                 {
4934                         mwindow->edl->local_session->set_selectionstart(position_f);
4935                         mwindow->edl->local_session->set_selectionend(position_f);
4936                 }
4937                 else
4938                 if(position_f < center_f)
4939                 {
4940                         mwindow->edl->local_session->set_selectionstart(position_f);
4941                 }
4942                 else
4943                         mwindow->edl->local_session->set_selectionend(position_f);
4944         }
4945
4946
4947         return result;
4948 }
4949
4950 void TrackCanvas::update_drag_caption()
4951 {
4952         switch(mwindow->session->current_operation)
4953         {
4954                 case DRAG_FADE:
4955
4956                         break;
4957         }
4958 }
4959
4960
4961 void TrackCanvas::drag_edit_select(Edit *over_edit, int select, int draw)
4962 {
4963         if( !over_edit ) return;
4964         if( !mwindow->session->drag_edit ) return;
4965         Track *drag_track = mwindow->session->drag_edit->track;
4966         Track *over_track = over_edit->track;
4967         if( !drag_track || !over_track ) return;
4968         if( drag_track->number_of() > over_track->number_of() ) {
4969                 Track *trk = drag_track;
4970                 drag_track = over_track;
4971                 over_track = trk;
4972         }
4973         double start_pos = mwindow->session->drag_start;
4974         double end_pos = mwindow->session->drag_position;
4975         if( start_pos > end_pos ) {
4976                 double pos = start_pos;
4977                 start_pos = end_pos;
4978                 end_pos = pos;
4979         }
4980         int done = 0, do_flash = 0;
4981         for( Track *track=drag_track; !done; track=track->next ) {
4982                 if( !track->is_armed() ) continue;
4983                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
4984                         int64_t pos = edit->startproject;
4985                         int64_t end = pos + edit->length;
4986                         double edit_start = track->from_units(pos);
4987                         double edit_end = track->from_units(end);
4988                         if( start_pos >= edit_end ) continue;
4989                         if( edit_start > end_pos ) continue;
4990                         if( select > 0 )
4991                                 edit->is_selected = 1;
4992                         else if( select < 0 )
4993                                 edit->is_selected = 0;
4994                         if( draw ) {
4995                                 int64_t edit_x, edit_y, edit_w, edit_h;
4996                                 edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h);
4997                                 set_color(YELLOW);
4998                                 draw_rectangle(edit_x, edit_y, edit_w, edit_h);
4999                                 do_flash = 1;
5000                         }
5001                 }
5002                 if( track == over_track ) done = 1;
5003         }
5004         if( do_flash )
5005                 flash();
5006 }
5007
5008
5009 int TrackCanvas::cursor_update(int in_motion)
5010 {
5011         int result = 0;
5012         int cursor_x = 0;
5013         int cursor_y = 0;
5014         int update_clock = 0;
5015         int update_zoom = 0;
5016         int update_scroll = 0;
5017         int update_overlay = 0;
5018         int update_cursor = 0;
5019         int rerender = 0;
5020         double position = 0.;
5021 //printf("TrackCanvas::cursor_update %d %d,%d\n", __LINE__, get_cursor_x(), get_cursor_y());
5022
5023 // Default cursor
5024         int new_cursor =
5025                 arrow_mode() ? ARROW_CURSOR :
5026                 ibeam_mode() ? IBEAM_CURSOR : 0;
5027
5028         switch(mwindow->session->current_operation)
5029         {
5030                 case DRAG_EDITHANDLE1:
5031 // Outside threshold.  Upgrade status
5032                         if( !active ) break;
5033                         if( labs(get_cursor_x() - mwindow->session->drag_origin_x) > HANDLE_W ) {
5034                                 mwindow->session->current_operation = DRAG_EDITHANDLE2;
5035                                 update_overlay = 1;
5036                         }
5037                         break;
5038                 case DRAG_EDITHANDLE2:
5039                         if( !active ) break;
5040                         update_drag_handle();
5041                         update_overlay = 1;
5042                         break;
5043
5044                 case DRAG_PLUGINHANDLE1:
5045                         if( !active ) break;
5046                         if( labs(get_cursor_x() - mwindow->session->drag_origin_x) > HANDLE_W ) {
5047                                 mwindow->session->current_operation = DRAG_PLUGINHANDLE2;
5048                                 update_overlay = 1;
5049                         }
5050                         break;
5051                 case DRAG_PLUGINHANDLE2:
5052                         if( !active ) break;
5053                         update_drag_handle();
5054                         update_overlay = 1;
5055                         break;
5056
5057                 case DRAG_TRANSNHANDLE1:
5058                         if( !active ) break;
5059                         if( labs(get_cursor_x() - mwindow->session->drag_origin_x) > HANDLE_W ) {
5060                                 mwindow->session->current_operation = DRAG_TRANSNHANDLE2;
5061                                 update_overlay = 1;
5062                         }
5063                         break;
5064                 case DRAG_TRANSNHANDLE2: {
5065                         if( !active ) break;
5066                         position = mwindow->edl->get_cursor_position(get_cursor_x(), pane->number);
5067                         position = mwindow->edl->align_to_frame(position, 1);
5068                         drag_transition_handle(position);
5069                         rerender = 1;
5070                         update_overlay = 1;
5071                         break; }
5072
5073 // Rubber band curves
5074                 case DRAG_FADE:
5075                 case DRAG_SPEED:
5076                 case DRAG_CZOOM:
5077                 case DRAG_PZOOM:
5078                 case DRAG_CAMERA_X:
5079                 case DRAG_CAMERA_Y:
5080                 case DRAG_CAMERA_Z:
5081                 case DRAG_PROJECTOR_X:
5082                 case DRAG_PROJECTOR_Y:
5083                 case DRAG_PROJECTOR_Z:
5084                         if(active) rerender = update_overlay =
5085                                 update_drag_floatauto(get_cursor_x(), get_cursor_y());
5086                         if( rerender && mwindow->session->current_operation == DRAG_SPEED )
5087                                 mwindow->speed_after(!in_motion ? 1 : 0, 0);
5088                         break;
5089
5090                 case DRAG_PLAY:
5091                         if(active) rerender = update_overlay =
5092                                 update_drag_toggleauto(get_cursor_x(), get_cursor_y());
5093                         break;
5094
5095                 case DRAG_MUTE:
5096                         if(active) rerender = update_overlay =
5097                                 update_drag_toggleauto(get_cursor_x(), get_cursor_y());
5098                         break;
5099
5100 // Keyframe icons are sticky
5101                 case DRAG_PAN_PRE:
5102                 case DRAG_MASK_PRE:
5103                 case DRAG_MODE_PRE:
5104                 case DRAG_PLUGINKEY_PRE:
5105                         if(active) {
5106                                 if(labs(get_cursor_x() - mwindow->session->drag_origin_x) > HANDLE_W) {
5107                                         mwindow->session->current_operation++;
5108                                         update_overlay = 1;
5109
5110                                         mwindow->undo->update_undo_before();
5111                                 }
5112                         }
5113                         break;
5114
5115                 case DRAG_PAN:
5116                 case DRAG_MASK:
5117                 case DRAG_MODE:
5118                         if(active) rerender = update_overlay =
5119                                 update_drag_auto(get_cursor_x(), get_cursor_y());
5120                         break;
5121
5122                 case DRAG_PLUGINKEY:
5123                         if(active) rerender = update_overlay =
5124                                 update_drag_pluginauto(get_cursor_x(), get_cursor_y());
5125                         break;
5126
5127                 case SELECT_REGION:
5128                         if(active) {
5129                                 cursor_x = get_cursor_x();
5130                                 cursor_y = get_cursor_y();
5131                                 position = (double)(cursor_x + mwindow->edl->local_session->view_start[pane->number]) *
5132                                         mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate;
5133
5134                                 position = mwindow->edl->align_to_frame(position, 0);
5135                                 position = MAX(position, 0);
5136
5137                                 double start = mwindow->edl->local_session->get_selectionstart(1);
5138                                 double end = mwindow->edl->local_session->get_selectionend(1);
5139                                 if(position < selection_midpoint) {
5140                                         mwindow->edl->local_session->set_selectionend(selection_midpoint);
5141                                         mwindow->edl->local_session->set_selectionstart(position);
5142                                 }
5143                                 else {
5144                                         mwindow->edl->local_session->set_selectionstart(selection_midpoint);
5145                                         mwindow->edl->local_session->set_selectionend(position);
5146                                 }
5147         // Que the CWindow
5148                                 gui->unlock_window();
5149                                 int dir =
5150                                         start != mwindow->edl->local_session->get_selectionstart(1) ? 1 :
5151                                         end != mwindow->edl->local_session->get_selectionend(1) ? -1 : 0;
5152                                 mwindow->cwindow->update(dir, 0, 0, 0, 1);
5153                                 gui->lock_window("TrackCanvas::cursor_update 1");
5154         // Update the faders
5155                                 mwindow->update_plugin_guis();
5156                                 gui->update_patchbay();
5157
5158                                 timebar_position = mwindow->edl->local_session->get_selectionend(1);
5159
5160                                 gui->hide_cursor(0);
5161                                 gui->draw_cursor(1);
5162                                 gui->update_timebar(0);
5163                                 gui->flash_canvas(1);
5164                                 result = 1;
5165                                 update_clock = 1;
5166                                 update_zoom = 1;
5167                                 update_scroll = 1;
5168                         }
5169                         break;
5170
5171                 case DROP_TARGETING:
5172                         new_cursor = GRABBED_CURSOR;
5173                         result = 1;
5174                         break;
5175
5176                 default:
5177                         if(is_event_win() && cursor_inside()) {
5178 // Update clocks
5179                                 cursor_x = get_cursor_x();
5180                                 position = mwindow->edl->get_cursor_position(cursor_x, pane->number);
5181                                 position = mwindow->edl->align_to_frame(position, 0);
5182                                 update_clock = 1;
5183 // set all timebars
5184                                 for(int i = 0; i < TOTAL_PANES; i++)
5185                                         if(gui->pane[i]) gui->pane[i]->canvas->timebar_position = position;
5186
5187 //printf("TrackCanvas::cursor_update %d %d %p %p\n", __LINE__, pane->number, pane, pane->timebar);
5188                                 gui->update_timebar(0);
5189 // Update cursor
5190                                 if(do_transitions(get_cursor_x(), get_cursor_y(),
5191                                                 0, new_cursor, update_cursor)) break;
5192                                 if(do_keyframes(get_cursor_x(), get_cursor_y(),
5193                                         0, 0, new_cursor, update_cursor, rerender)) break;
5194                                 if(do_transition_handles(get_cursor_x(), get_cursor_y(),
5195                                         0, rerender, update_overlay, new_cursor, update_cursor)) break;
5196                                 if(do_edit_handles(get_cursor_x(), get_cursor_y(),
5197                                         0, rerender, update_overlay, new_cursor, update_cursor)) break;
5198 // Plugin boundaries
5199                                 if(do_plugin_handles(get_cursor_x(), get_cursor_y(),
5200                                         0, rerender, update_overlay, new_cursor, update_cursor)) break;
5201                                 if(do_edits(get_cursor_x(), get_cursor_y(),
5202                                         0, 0, update_overlay, rerender, new_cursor, update_cursor)) break;
5203                         }
5204                         break;
5205         }
5206
5207 //printf("TrackCanvas::cursor_update 1\n");
5208         if(update_cursor && new_cursor != get_cursor())
5209         {
5210                 set_cursor(new_cursor, 0, 1);
5211         }
5212
5213 //printf("TrackCanvas::cursor_update 1 %d\n", rerender);
5214         if(rerender && render_timer->get_difference() > 0.25 ) {
5215                 render_timer->update();
5216                 mwindow->restart_brender();
5217                 mwindow->sync_parameters(CHANGE_PARAMS);
5218                 mwindow->update_plugin_guis();
5219                 gui->unlock_window();
5220                 mwindow->cwindow->update(1, 0, 0, 0, 1);
5221                 gui->lock_window("TrackCanvas::cursor_update 2");
5222         }
5223         if(rerender) {
5224 // Update faders
5225                 gui->update_patchbay();
5226         }
5227
5228
5229         if(update_clock) {
5230                 if(!mwindow->cwindow->playback_engine->is_playing_back)
5231                         gui->mainclock->update(position);
5232         }
5233
5234         if(update_zoom) {
5235                 gui->zoombar->update();
5236         }
5237
5238         if(update_scroll) {
5239                 if(!drag_scroll &&
5240                         (cursor_x >= get_w() || cursor_x < 0 || cursor_y >= get_h() || cursor_y < 0))
5241                         start_dragscroll();
5242                 else
5243                 if(drag_scroll &&
5244                         (cursor_x < get_w() && cursor_x >= 0 && cursor_y < get_h() && cursor_y >= 0))
5245                         stop_dragscroll();
5246         }
5247
5248         if(update_overlay) {
5249                 gui->draw_overlays(1);
5250         }
5251
5252 //printf("TrackCanvas::cursor_update %d\n", __LINE__);
5253         return result;
5254 }
5255
5256 int TrackCanvas::cursor_motion_event()
5257 {
5258         return cursor_update(1);
5259 }
5260
5261 void TrackCanvas::start_dragscroll()
5262 {
5263         if(!drag_scroll) {
5264                 drag_scroll = 1;
5265                 set_repeat(BC_WindowBase::get_resources()->scroll_repeat);
5266 //printf("TrackCanvas::start_dragscroll 1\n");
5267         }
5268 }
5269
5270 void TrackCanvas::stop_dragscroll()
5271 {
5272         if(drag_scroll) {
5273                 drag_scroll = 0;
5274                 unset_repeat(BC_WindowBase::get_resources()->scroll_repeat);
5275 //printf("TrackCanvas::stop_dragscroll 1\n");
5276         }
5277 }
5278
5279 int TrackCanvas::repeat_event(int64_t duration)
5280 {
5281         if(!drag_scroll) return 0;
5282         if(duration != BC_WindowBase::get_resources()->scroll_repeat) return 0;
5283
5284         int sample_movement = 0;
5285         int track_movement = 0;
5286         int64_t x_distance = 0;
5287         int64_t y_distance = 0;
5288         double position = 0;
5289         int result = 0;
5290
5291         switch(mwindow->session->current_operation) {
5292                 case DRAG_EDIT_SELECT:
5293                 case SELECT_REGION:
5294 //printf("TrackCanvas::repeat_event 1 %d\n", mwindow->edl->local_session->view_start);
5295                 if(get_cursor_x() > get_w()) {
5296                                 x_distance = get_cursor_x() - get_w();
5297                                 sample_movement = 1;
5298                 }
5299                 else if(get_cursor_x() < 0) {
5300                         x_distance = get_cursor_x();
5301                         sample_movement = 1;
5302                 }
5303
5304                 if(get_cursor_y() > get_h()) {
5305                         y_distance = get_cursor_y() - get_h();
5306                         track_movement = 1;
5307                 }
5308                 else if(get_cursor_y() < 0) {
5309                         y_distance = get_cursor_y();
5310                         track_movement = 1;
5311                 }
5312                 result = 1;
5313                 break;
5314         }
5315
5316         if(sample_movement) {
5317                 position = (double)(get_cursor_x() +
5318                         mwindow->edl->local_session->view_start[pane->number] +
5319                         x_distance) *
5320                         mwindow->edl->local_session->zoom_sample /
5321                         mwindow->edl->session->sample_rate;
5322                 position = mwindow->edl->align_to_frame(position, 0);
5323                 position = MAX(position, 0);
5324
5325 //printf("TrackCanvas::repeat_event 1 %f\n", position);
5326                 switch(mwindow->session->current_operation) {
5327                 case SELECT_REGION:
5328                         if(position < selection_midpoint) {
5329                                 mwindow->edl->local_session->set_selectionend(selection_midpoint);
5330                                 mwindow->edl->local_session->set_selectionstart(position);
5331 // Que the CWindow
5332                                 gui->unlock_window();
5333                                 mwindow->cwindow->update(1, 0, 0);
5334                                 gui->lock_window("TrackCanvas::repeat_event");
5335 // Update the faders
5336                                 mwindow->update_plugin_guis();
5337                                 gui->update_patchbay();
5338                         }
5339                         else {
5340                                 mwindow->edl->local_session->set_selectionstart(selection_midpoint);
5341                                 mwindow->edl->local_session->set_selectionend(position);
5342 // Don't que the CWindow
5343                         }
5344                         break;
5345                 }
5346
5347                 mwindow->samplemovement(
5348                         mwindow->edl->local_session->view_start[pane->number] + x_distance,
5349                         pane->number);
5350         }
5351
5352         if(track_movement) {
5353                 mwindow->trackmovement(y_distance, pane->number);
5354         }
5355
5356         return result;
5357 }
5358
5359 int TrackCanvas::button_release_event()
5360 {
5361         int redraw = -1, update_overlay = 0;
5362         int result = 0, load_flags = 0;
5363
5364 // printf("TrackCanvas::button_release_event %d\n",
5365 // mwindow->session->current_operation);
5366         if(active) {
5367                 switch(mwindow->session->current_operation) {
5368                 case DRAG_EDITHANDLE2:
5369                         mwindow->session->current_operation = NO_OPERATION;
5370                         drag_scroll = 0;
5371                         result = 1;
5372
5373                         end_edithandle_selection();
5374                         break;
5375
5376                 case DRAG_EDITHANDLE1:
5377                         mwindow->session->current_operation = NO_OPERATION;
5378                         drag_scroll = 0;
5379                         result = 1;
5380                         break;
5381
5382                 case DRAG_PLUGINHANDLE2:
5383                         mwindow->session->current_operation = NO_OPERATION;
5384                         drag_scroll = 0;
5385                         result = 1;
5386
5387                         end_pluginhandle_selection();
5388                         break;
5389
5390                 case DRAG_PLUGINHANDLE1:
5391                         mwindow->session->current_operation = NO_OPERATION;
5392                         drag_scroll = 0;
5393                         result = 1;
5394                         break;
5395
5396                 case DRAG_TRANSNHANDLE2:
5397                         mwindow->session->current_operation = NO_OPERATION;
5398                         drag_scroll = 0;
5399                         result = 1;
5400
5401                         end_transnhandle_selection();
5402                         break;
5403
5404                 case DRAG_TRANSNHANDLE1:
5405                         mwindow->session->current_operation = NO_OPERATION;
5406                         drag_scroll = 0;
5407                         result = 1;
5408                         break;
5409
5410                 case DRAG_SPEED:
5411                         redraw = FORCE_REDRAW;
5412                         load_flags |= LOAD_EDITS | LOAD_TIMEBAR;
5413                 case DRAG_FADE:
5414 // delete the drag_auto_gang first and remove out of order keys
5415                         clear_ganged_autos();
5416                 case DRAG_CZOOM:
5417                 case DRAG_PZOOM:
5418                 case DRAG_PLAY:
5419                 case DRAG_MUTE:
5420                 case DRAG_MASK:
5421                 case DRAG_MODE:
5422                 case DRAG_PAN:
5423                 case DRAG_CAMERA_X:
5424                 case DRAG_CAMERA_Y:
5425                 case DRAG_CAMERA_Z:
5426                 case DRAG_PROJECTOR_X:
5427                 case DRAG_PROJECTOR_Y:
5428                 case DRAG_PROJECTOR_Z:
5429                 case DRAG_PLUGINKEY:
5430                         load_flags |= LOAD_AUTOMATION;
5431                         mwindow->session->current_operation = NO_OPERATION;
5432                         mwindow->session->drag_handle = 0;
5433 // Remove any out-of-order keyframe
5434                         if(mwindow->session->drag_auto) {
5435                                 mwindow->session->drag_auto->autos->remove_nonsequential(
5436                                                 mwindow->session->drag_auto);
5437 //                              mwindow->session->drag_auto->autos->optimize();
5438                                 update_overlay = 1;
5439                         }
5440
5441                         mwindow->undo->update_undo_after(_("keyframe"), load_flags);
5442                         result = 1;
5443                         break;
5444
5445                 case DRAG_EDIT:
5446                 case DRAG_GROUP:
5447                 case DRAG_AEFFECT_COPY:
5448                 case DRAG_VEFFECT_COPY:
5449 // Trap in drag stop
5450                         break;
5451
5452                 case DROP_TARGETING: {
5453                         int cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
5454                         Track *track=0;  Edit *edit=0;  PluginSet *pluginset=0;  Plugin *plugin=0;
5455                         drag_cursor_motion(cursor_x, cursor_y,
5456                                 &track, &edit, &pluginset, &plugin);
5457                         double position =
5458                                 mwindow->edl->get_cursor_position(cursor_x, pane->number);
5459                         gui->edit_menu->activate_menu(track, edit, pluginset, plugin, position);
5460                         mwindow->session->current_operation = NO_OPERATION;
5461                         result = 1;
5462                         break; }
5463
5464                 case GROUP_TOGGLE: {
5465                         Edit *edit = mwindow->session->drag_edit;
5466                         if( edit ) {
5467                                 if( shift_down() && edit->is_selected ) {
5468                                         if( edit->group_id > 0 ) {
5469                                                 mwindow->edl->tracks->clear_selected_edits();
5470                                                 mwindow->edl->tracks->del_group(edit->group_id);
5471                                         }
5472                                         else {
5473                                                 int id = mwindow->session->group_number++;
5474                                                 mwindow->edl->tracks->new_group(id);
5475                                         }
5476                                         redraw = 0;
5477                                 }
5478                                 else {
5479                                         if( mwindow->preferences->ctrl_toggle && !ctrl_down() )
5480                                                 mwindow->edl->tracks->clear_selected_edits();
5481                                         edit->set_selected(-1);
5482                                 }
5483                         }
5484                         mwindow->session->current_operation = NO_OPERATION;
5485                         update_overlay = 1;
5486                         result = 1;
5487                         drag_scroll = 0;
5488                         break; }
5489
5490                 case DRAG_EDIT_SELECT:
5491                         //result = 0;
5492                         break;
5493
5494                 default:
5495                         if( mwindow->session->current_operation ) {
5496 //                              if(mwindow->session->current_operation == SELECT_REGION) {
5497 //                                      mwindow->undo->update_undo_after(_("select"), LOAD_SESSION, 0, 0);
5498 //                              }
5499
5500                                 mwindow->session->current_operation = NO_OPERATION;
5501                                 drag_scroll = 0;
5502                                 //result = 0;
5503                                 break;
5504                         }
5505                         break;
5506                 }
5507         }
5508
5509         if( mwindow->edl->local_session->zoombar_showautocolor >= 0 )
5510                 mwindow->gui->zoombar->update_autozoom(-1);
5511
5512         if (result)
5513                 cursor_update(0);
5514
5515         if(update_overlay) {
5516                 gui->draw_overlays(1);
5517         }
5518         if(redraw >= 0) {
5519                 gui->draw_canvas(redraw, 0);
5520                 gui->flash_canvas(1);
5521         }
5522         return result;
5523 }
5524
5525 int TrackCanvas::do_edit_handles(int cursor_x, int cursor_y, int button_press,
5526         int &rerender, int &update_overlay, int &new_cursor, int &update_cursor)
5527 {
5528         Edit *edit_result = 0;
5529         int handle_result = -1;
5530         int result = 0;
5531
5532         for( Track *track=mwindow->edl->tracks->first; track && !result; track=track->next) {
5533                 if( track->is_hidden() ) continue;
5534                 for( Edit *edit=track->edits->first; edit && !result; edit=edit->next ) {
5535                         int64_t edit_x, edit_y, edit_w, edit_h;
5536                         edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h);
5537
5538                         if( cursor_x >= edit_x && cursor_x <= edit_x + edit_w &&
5539                                 cursor_y >= edit_y && cursor_y < edit_y + edit_h ) {
5540                                 if( cursor_x < edit_x + HANDLE_W ) {
5541                                         edit_result = edit;
5542                                         handle_result = 0;
5543                                         if( cursor_y >= edit_y+edit_h - HANDLE_W &&
5544                                             track->show_assets() ) {
5545                                                 new_cursor = DOWNLEFT_RESIZE;
5546                                                 if( button_press == LEFT_BUTTON )
5547                                                         result = -1;
5548                                         }
5549                                         else
5550                                                 result = 1;
5551                                 }
5552                                 else if( cursor_x >= edit_x + edit_w - HANDLE_W ) {
5553                                         edit_result = edit;
5554                                         handle_result = 1;
5555                                         if( cursor_y >= edit_y+edit_h - HANDLE_W &&
5556                                             track->show_assets() ) {
5557                                                 new_cursor = DOWNRIGHT_RESIZE;
5558                                                 if( button_press == LEFT_BUTTON )
5559                                                         result = -1;
5560                                         }
5561                                         else
5562                                                 result = 1;
5563                                 }
5564                         }
5565                 }
5566         }
5567
5568         if( result > 0 ) {
5569                 int group_id = edit_result->group_id;
5570                 if( group_id > 0 ) {
5571                         Track *track = edit_result->track;
5572                         Edit *left = 0, *right = 0;
5573                         double start = DBL_MAX, end = DBL_MIN;
5574                         for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
5575                                 if( edit->group_id != group_id ) continue;
5576                                 double edit_start = edit->track->from_units(edit->startproject);
5577                                 if( edit_start < start ) { start = edit_start;  left = edit; }
5578                                 double edit_end = edit->track->from_units(edit->startproject+edit->length);
5579                                 if( edit_end > end ) { end = edit_end;  right = edit; }
5580                         }
5581                         Edit *edit = !handle_result ? left : right;
5582                         if( edit != edit_result )
5583                                 result = 0;
5584                 }
5585         }
5586
5587         update_cursor = 1;
5588         if( result > 0 ) {
5589                 double position = 0;
5590                 if( handle_result == 0 ) {
5591                         position = edit_result->track->from_units(edit_result->startproject);
5592                         new_cursor = LEFT_CURSOR;
5593                 }
5594                 else if( handle_result == 1 ) {
5595                         position = edit_result->track->from_units(edit_result->startproject + edit_result->length);
5596                         new_cursor = RIGHT_CURSOR;
5597                 }
5598
5599 // Reposition cursor
5600                 if( button_press ) {
5601                         mwindow->session->drag_edit = edit_result;
5602                         mwindow->session->drag_handle = handle_result;
5603                         mwindow->session->drag_button = get_buttonpress() - 1;
5604                         mwindow->session->drag_position = position;
5605                         mwindow->session->current_operation = DRAG_EDITHANDLE1;
5606                         mwindow->session->drag_origin_x = get_cursor_x();
5607                         mwindow->session->drag_origin_y = get_cursor_y();
5608                         mwindow->session->drag_start = position;
5609
5610                         rerender = start_selection(position);
5611                         update_overlay = 1;
5612                 }
5613         }
5614         else if( result < 0 && !edit_result->silence() ) {
5615                 mwindow->undo->update_undo_before();
5616                 if( !shift_down() ) {
5617                         if( handle_result == 0 )
5618                                 edit_result->hard_left = !edit_result->hard_left;
5619                         else if( handle_result == 1 )
5620                                 edit_result->hard_right = !edit_result->hard_right;
5621                 }
5622                 else {
5623                         int status = handle_result == 0 ? edit_result->hard_left :
5624                                      handle_result == 1 ? edit_result->hard_right : 0;
5625                         int new_status = !status;
5626                         int64_t edit_edge = edit_result->startproject;
5627                         if( handle_result == 1 ) edit_edge += edit_result->length;
5628                         double edge_position = edit_result->track->from_units(edit_edge);
5629                         for( Track *track=mwindow->edl->tracks->first; track!=0; track=track->next ) {
5630                                 if( track->is_hidden() ) continue;
5631                                 int64_t track_position = track->to_units(edge_position, 1);
5632                                 Edit *left_edit = track->edits->editof(track_position, PLAY_FORWARD, 0);
5633                                 if( left_edit ) {
5634                                         int64_t left_edge = left_edit->startproject;
5635                                         double left_position = track->from_units(left_edge);
5636                                         if( EQUIV(edge_position, left_position) ) {
5637                                                 left_edit->hard_left = new_status;
5638                                                 if( left_edit->previous )
5639                                                         left_edit->previous->hard_right = new_status;
5640                                         }
5641                                 }
5642                                 Edit *right_edit = track->edits->editof(track_position, PLAY_REVERSE, 0);
5643                                 if( right_edit ) {
5644                                         int64_t right_edge = right_edit->startproject + right_edit->length;
5645                                         double right_position = track->from_units(right_edge);
5646                                         if( EQUIV(edge_position, right_position) ) {
5647                                                 right_edit->hard_right = new_status;
5648                                                 if( right_edit->next )
5649                                                         right_edit->next->hard_left = new_status;
5650                                         }
5651                                 }
5652                         }
5653                 }
5654                 rerender = update_overlay = 1;
5655                 mwindow->undo->update_undo_after(_("hard_edge"), LOAD_EDITS);
5656                 result = 1;
5657         }
5658
5659         return result;
5660 }
5661
5662 int TrackCanvas::do_plugin_handles(int cursor_x,
5663         int cursor_y,
5664         int button_press,
5665         int &rerender,
5666         int &update_overlay,
5667         int &new_cursor,
5668         int &update_cursor)
5669 {
5670         Plugin *plugin_result = 0;
5671         int handle_result = 0;
5672         int result = 0;
5673
5674         for(Track *track = mwindow->edl->tracks->first;
5675                 track && !result;
5676                 track = track->next) {
5677                 if( track->is_hidden() ) continue;
5678                 for(int i = 0; i < track->plugin_set.total && !result; i++) {
5679                         PluginSet *plugin_set = track->plugin_set.values[i];
5680                         for(Plugin *plugin = (Plugin*)plugin_set->first;
5681                                 plugin && !result;
5682                                 plugin = (Plugin*)plugin->next) {
5683                                 int64_t plugin_x, plugin_y, plugin_w, plugin_h;
5684                                 plugin_dimensions(plugin, plugin_x, plugin_y, plugin_w, plugin_h);
5685
5686                                 if(cursor_x >= plugin_x && cursor_x <= plugin_x + plugin_w &&
5687                                         cursor_y >= plugin_y && cursor_y < plugin_y + plugin_h) {
5688                                         if(cursor_x < plugin_x + HANDLE_W) {
5689                                                 plugin_result = plugin;
5690                                                 handle_result = 0;
5691                                                 result = 1;
5692                                         }
5693                                         else if(cursor_x >= plugin_x + plugin_w - HANDLE_W) {
5694                                                 plugin_result = plugin;
5695                                                 handle_result = 1;
5696                                                 result = 1;
5697                                         }
5698                                 }
5699                         }
5700
5701                         if(result && shift_down())
5702                                 mwindow->session->trim_edits = plugin_set;
5703                 }
5704         }
5705
5706         update_cursor = 1;
5707         if(result) {
5708                 double position = 0;
5709                 if(handle_result == 0) {
5710                         position = plugin_result->track->from_units(plugin_result->startproject);
5711                         new_cursor = LEFT_CURSOR;
5712                 }
5713                 else if(handle_result == 1) {
5714                         position = plugin_result->track->from_units(plugin_result->startproject + plugin_result->length);
5715                         new_cursor = RIGHT_CURSOR;
5716                 }
5717
5718                 if(button_press) {
5719                         mwindow->session->drag_plugin = plugin_result;
5720                         mwindow->session->drag_handle = handle_result;
5721                         mwindow->session->drag_button = get_buttonpress() - 1;
5722                         mwindow->session->drag_position = position;
5723                         mwindow->session->current_operation = DRAG_PLUGINHANDLE1;
5724                         mwindow->session->drag_origin_x = get_cursor_x();
5725                         mwindow->session->drag_origin_y = get_cursor_y();
5726                         mwindow->session->drag_start = position;
5727
5728                         rerender = start_selection(position);
5729                         update_overlay = 1;
5730                 }
5731         }
5732
5733         return result;
5734 }
5735
5736 int TrackCanvas::do_transition_handles(int cursor_x, int cursor_y, int button_press,
5737                 int &rerender, int &update_overlay, int &new_cursor, int &update_cursor)
5738 {
5739         if( !mwindow->edl->session->auto_conf->transitions )
5740                 return 0;
5741         Transition *trans_result = 0;
5742         int result = 0;
5743
5744         Track *track = mwindow->edl->tracks->first;
5745         for( ; track && !result; track=track->next) {
5746                 if( track->is_hidden() ) continue;
5747                 Edit *edit = track->edits->first;
5748                 for( ; edit && !result; edit=edit->next ) {
5749                         Transition *trans = edit->transition;
5750                         if( !trans ) continue;
5751                         int64_t x, y, w, h;
5752                         edit_dimensions(edit, x, y, w, h);
5753                         int strip_x = x, edit_y = y;
5754                         get_transition_coords(edit, x, y, w, h);
5755                         VFrame *strip = mwindow->theme->get_image("plugin_bg_data");
5756                         int strip_y = y - strip->get_h();
5757                         if( track->show_assets() && track->show_titles() )
5758                                 edit_y += mwindow->theme->get_image("title_bg_data")->get_h();
5759                         if( strip_y < edit_y ) strip_y = edit_y;
5760                         int strip_w = Units::round(edit->track->from_units(edit->transition->length) *
5761                                 mwindow->edl->session->sample_rate / mwindow->edl->local_session->zoom_sample);
5762                         int x1 = strip_x + strip_w - HANDLE_W/2, x2 = x1 + HANDLE_W;
5763                         int y1 = strip_y + strip->get_h()/2 - HANDLE_H/2, y2 = y1 + HANDLE_W;
5764                         if( cursor_x >= x1 && cursor_x < x2 &&
5765                             cursor_y >= y1 && cursor_y < y2 ) {
5766                                 trans_result = trans;
5767                                 result = 1;
5768                         }
5769                 }
5770         }
5771
5772         if( result ) {
5773                 if( button_press ) {
5774                         mwindow->session->drag_transition = trans_result;
5775                         mwindow->session->drag_handle = 1;
5776                         mwindow->session->drag_button = get_buttonpress() - 1;
5777                         int64_t trans_end = trans_result->edit->startproject + trans_result->length;
5778                         double position = trans_result->edit->track->from_units(trans_end);
5779                         mwindow->session->drag_position = position;
5780                         mwindow->session->drag_start = position;
5781                         mwindow->session->current_operation = DRAG_TRANSNHANDLE1;
5782                         mwindow->session->drag_origin_x = get_cursor_x();
5783                         mwindow->session->drag_origin_y = get_cursor_y();
5784                         update_cursor = 1;
5785                 }
5786                 new_cursor = RIGHT_CURSOR;
5787                 update_overlay = 1;
5788         }
5789
5790         return result;
5791 }
5792
5793 int TrackCanvas::drag_transition_handle(double position)
5794 {
5795         Transition *transition = mwindow->session->drag_transition;
5796         if( !transition ) return 1;
5797         mwindow->session->drag_position = position;
5798         mwindow->edl->local_session->set_selectionstart(position);
5799         mwindow->edl->local_session->set_selectionend(position);
5800         char string[BCSTRLEN];
5801         int64_t length = transition->length;
5802         Track *track = transition->edit->track;
5803         int64_t start_pos = track->to_units(mwindow->session->drag_start, 0);
5804         int64_t end_pos = track->to_units(mwindow->session->drag_position, 0);
5805         length += end_pos - start_pos;
5806         if( length < 0 ) length = 0;
5807         double time = track->from_units(length);
5808         Units::totext(string, time,
5809                 mwindow->edl->session->time_format,
5810                 mwindow->edl->session->sample_rate,
5811                 mwindow->edl->session->frame_rate,
5812                 mwindow->edl->session->frames_per_foot);
5813         mwindow->gui->show_message(string);
5814         if( mwindow->gui->transition_menu->length_thread->running() ) {
5815                 TransitionLengthDialog *dialog = (TransitionLengthDialog *)
5816                          mwindow->gui->transition_menu->length_thread->get_gui();
5817                 if( dialog ) {
5818                         dialog->lock_window("TrackCanvas::drag_transition_handle");
5819                         dialog->update_text(time);
5820                         dialog->thread->new_length = time;
5821                         dialog->unlock_window();
5822                 }
5823         }
5824         return 0;
5825 }
5826
5827
5828 int TrackCanvas::do_tracks(int cursor_x, int cursor_y, int button_press)
5829 {
5830         int result = 0;
5831
5832         Track *track=0;  Edit *edit=0;  PluginSet *pluginset=0;  Plugin *plugin=0;
5833         drag_cursor_motion(cursor_x, cursor_y,
5834                 &track, &edit, &pluginset, &plugin);
5835
5836         if( button_press && track ) {
5837                 switch( get_buttonpress() ) {
5838                 case RIGHT_BUTTON: {
5839                         double position =
5840                                 mwindow->edl->get_cursor_position(cursor_x, pane->number);
5841                         gui->track_menu->activate_menu(track, edit, pluginset, plugin, position);
5842                         mwindow->session->current_operation = NO_OPERATION;
5843                         result = 1;
5844                         break; }
5845                 case MIDDLE_BUTTON:
5846                         set_cursor(GRABBED_CURSOR, 0, 1);
5847                         mwindow->session->current_operation = DROP_TARGETING;
5848                         result = 1;
5849                         break;
5850                 }
5851         }
5852
5853         return result;
5854 }
5855
5856 int TrackCanvas::arrow_mode()
5857 {
5858         return mwindow->edl->session->editing_mode == EDITING_ARROW ? 1 : 0;
5859 }
5860 int TrackCanvas::ibeam_mode()
5861 {
5862         return mwindow->edl->session->editing_mode == EDITING_IBEAM ? 1 : 0;
5863 }
5864
5865 int TrackCanvas::do_edits(int cursor_x, int cursor_y, int button_press, int drag_start,
5866         int &redraw, int &rerender, int &new_cursor, int &update_cursor)
5867 {
5868         int result = 0;
5869
5870         for(Track *track = mwindow->edl->tracks->first; track && !result; track = track->next) {
5871                 if( track->is_hidden() ) continue;
5872                 for(Edit *edit = track->edits->first; edit && !result; edit = edit->next) {
5873                         int64_t edit_x, edit_y, edit_w, edit_h;
5874                         edit_dimensions(edit, edit_x, edit_y, edit_w, edit_h);
5875
5876 // Cursor inside a track
5877 // Cursor inside an edit
5878                         if( cursor_x >= edit_x && cursor_x < edit_x + edit_w &&
5879                             cursor_y >= edit_y && cursor_y < edit_y + edit_h ) {
5880                                 if( button_press && get_buttonpress() == LEFT_BUTTON ) {
5881                                         if( get_double_click() ) {
5882                                                 mwindow->edl->tracks->clear_selected_edits();
5883                                                 edit->select_affected_edits(1, -1);
5884                                                 double start = edit->track->from_units(edit->startproject);
5885                                                 start = mwindow->edl->align_to_frame(start, 0);
5886                                                 mwindow->edl->local_session->set_selectionstart(start);
5887                                                 double end = edit->track->from_units(edit->startproject+edit->length);
5888                                                 end = mwindow->edl->align_to_frame(end, 0);
5889                                                 mwindow->edl->local_session->set_selectionend(end);
5890                                                 result = 1; // Select edit duration or select_region
5891                                         }
5892                                         else if( arrow_mode() || (ibeam_mode() && ctrl_down()) ) {
5893                                                 mwindow->session->drag_edit = edit;
5894                                                 mwindow->session->current_operation = GROUP_TOGGLE;
5895                                                 result = 1;
5896                                         }
5897                                         if( result ) {
5898                                                 rerender = 1;
5899                                                 update_cursor = -1;
5900                                         }
5901                                 }
5902                                 else if( drag_start && track->is_armed() ) {
5903                                         mwindow->session->drag_edit = edit;
5904                                         mwindow->session->drag_origin_x = cursor_x;
5905                                         mwindow->session->drag_origin_y = cursor_y;
5906 // Where the drag started, so we know relative position inside the edit later
5907                                         double position = mwindow->edl->get_cursor_position(cursor_x, pane->number);
5908                                         mwindow->session->drag_position = mwindow->edl->align_to_frame(position, 1);
5909                                         if( get_buttonpress() == LEFT_BUTTON && alt_down() ) {
5910                                                 mwindow->session->drag_start = mwindow->session->drag_position;
5911                                                 drag_edit_select(edit, 0, 1);
5912                                                 mwindow->session->current_operation = DRAG_EDIT_SELECT;
5913                                                 result = 1;
5914                                                 continue;
5915                                         }
5916                                         drag_start = 0; // if unselected "fast" drag
5917                                         if( !edit->silence() && !edit->is_selected ) {
5918                                                 mwindow->edl->tracks->clear_selected_edits();
5919                                                 if( ibeam_mode() ) {
5920                                                         double start = edit->track->from_units(edit->startproject);
5921                                                         mwindow->edl->local_session->set_selectionstart(start);
5922                                                         mwindow->edl->local_session->set_selectionend(start);
5923                                                         edit->set_selected(1);
5924                                                 }
5925                                                 else
5926                                                         edit->select_affected_edits(1, -1);
5927                                                 drag_start = 1;
5928                                         }
5929 // Construct list of all affected edits
5930                                         if( drag_start || ibeam_mode() ) {
5931                                                 mwindow->edl->tracks->get_selected_edits(mwindow->session->drag_edits);
5932                                                 if( mwindow->session->drag_edits->size() > 0 ) {
5933                                                         mwindow->session->current_operation = DRAG_EDIT;
5934 // Need to create drag window
5935                                                         int cx, cy;  get_abs_cursor(cx, cy);
5936                                                         gui->drag_popup = new BC_DragWindow(gui,
5937                                                                 mwindow->theme->get_image("clip_icon"), cx, cy);
5938                                                         result = 1;
5939                                                 }
5940                                                 else {
5941                                                         rerender = start_selection(mwindow->session->drag_position);
5942                                                         mwindow->session->current_operation = SELECT_REGION;
5943                                                         update_cursor = 1;
5944                                                 }
5945                                         }
5946                                         else if( edit->is_selected && arrow_mode() ) {
5947                                                 if( mwindow->session->drag_group )
5948                                                         mwindow->session->drag_group->remove_user();
5949                                                 double start_position = 0;
5950                                                 mwindow->session->drag_group =
5951                                                         mwindow->edl->selected_edits_to_clip(0, &start_position,
5952                                                                 &mwindow->session->drag_group_first_track);
5953                                                 if( mwindow->session->drag_group ) {
5954                                                         mwindow->session->current_operation = DRAG_GROUP;
5955                                                         mwindow->session->drag_group_position = start_position;
5956                                                         mwindow->session->drag_group_edit = edit;
5957                                                         mwindow->session->drag_origin_y = edit_y;
5958                                                         result = 1;
5959                                                 }
5960                                         }
5961                                 }
5962                         }
5963                 }
5964         }
5965         return result;
5966 }
5967
5968 // returns -1=doesnt fit, 1=fits, 0=fits but overwrites
5969 int TrackCanvas::test_track_group(EDL *group, Track *first_track, double &pos)
5970 {
5971         int intersects = 0;
5972         Track *src = group->tracks->first;
5973         for( Track *track=first_track; track && src; track=track->next ) {
5974                 if( !track->is_armed() ) return -1;
5975                 if( src->data_type != track->data_type ) return -1;
5976                 for( Edit *src_edit=src->edits->first; src_edit; src_edit=src_edit->next ) {
5977                         if( src_edit->silence() ) continue;
5978                         if( !intersects && edit_intersects(track, src_edit, pos) )
5979                                 intersects = 1;
5980                 }
5981                 src = src->next;
5982         }
5983         return src ? -1 : !intersects ? 1 : 0;
5984 }
5985
5986 int TrackCanvas::edit_intersects(Track *track, Edit *src_edit, double &pos)
5987 {
5988         if( pos < 0 ) { pos = 0;  return 1; }
5989         int pane_no = pane->number;
5990         int cur_pix = track->edl->get_position_cursorx(pos, pane_no);
5991         int64_t src_start = src_edit->startproject;
5992         int64_t src_end = src_start + src_edit->length;
5993         double new_start = src_edit->track->from_units(src_start) + pos;
5994         double new_end = src_edit->track->from_units(src_end) + pos;
5995         int64_t trk_start = track->to_units(new_start, 1);
5996         int64_t trk_end = track->to_units(new_end, 1);
5997         for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
5998                 if( edit->is_selected || edit->silence() ) continue;
5999                 int64_t edit_start = edit->startproject;
6000                 if( edit_start >= trk_end ) continue;
6001                 int64_t edit_end = edit_start + edit->length;
6002                 if( trk_start >= edit_end ) continue;
6003
6004                 int64_t lt_dist = labs(trk_end - edit_start);
6005                 int64_t rt_dist = labs(edit_end - trk_start);
6006                 int64_t position;
6007                 if( lt_dist < rt_dist ) {
6008                         position = edit_start;
6009                         lt_dist = abs(trk_start - edit_start);
6010                         rt_dist = abs(trk_end - edit_start);
6011                         if( lt_dist > rt_dist )
6012                                 position -= src_end;
6013                 }
6014                 else {
6015                         position = edit_end;
6016                         lt_dist = abs(trk_start - edit_end);
6017                         rt_dist = abs(trk_end - edit_end);
6018                         if( lt_dist > rt_dist )
6019                                 position -= src_end;
6020                 }
6021                 double new_pos = edit->track->from_units(position);
6022                 int new_pix = track->edl->get_position_cursorx(new_pos, pane_no);
6023                 if( abs(new_pix-cur_pix) < HANDLE_W )
6024                         pos = new_pos;
6025                 return 1;
6026         }
6027         return 0;
6028 }
6029
6030 int TrackCanvas::test_resources(int cursor_x, int cursor_y)
6031 {
6032         return 0;
6033 }
6034
6035 int TrackCanvas::do_plugins(int cursor_x, int cursor_y, int drag_start,
6036         int button_press, int &redraw, int &rerender)
6037 {
6038         Plugin *plugin = 0;
6039         int result = 0;
6040         int done = 0;
6041         int64_t x, y, w, h;
6042         Track *track = 0;
6043
6044         for(track = mwindow->edl->tracks->first; track && !done; track = track->next) {
6045                 if(!track->expand_view) continue;
6046
6047                 for(int i = 0; i < track->plugin_set.total && !done; i++) {
6048                         // first check if plugins are visible at all
6049                         if (!track->expand_view)
6050                                 continue;
6051                         PluginSet *plugin_set = track->plugin_set.values[i];
6052                         for(plugin = (Plugin*)plugin_set->first;
6053                                 plugin && !done;
6054                                 plugin = (Plugin*)plugin->next) {
6055                                 plugin_dimensions(plugin, x, y, w, h);
6056                                 if(MWindowGUI::visible(x, x + w, 0, get_w()) &&
6057                                         MWindowGUI::visible(y, y + h, 0, get_h())) {
6058                                         if(cursor_x >= x && cursor_x < x + w &&
6059                                                 cursor_y >= y && cursor_y < y + h) {
6060                                                 done = 1;
6061                                                 break;
6062                                         }
6063                                 }
6064                         }
6065                 }
6066         }
6067
6068         if(plugin) {
6069 // Start plugin popup
6070                 if(button_press) {
6071                         if(get_buttonpress() == RIGHT_BUTTON ) {
6072                                 gui->plugin_menu->update(plugin);
6073                                 gui->plugin_menu->activate_menu();
6074                                 result = 1;
6075                         }
6076                         else if (get_double_click() && !drag_start) {
6077 // Select range of plugin on doubleclick over plugin
6078                                 mwindow->edl->local_session->set_selectionstart(plugin->track->from_units(plugin->startproject));
6079                                 mwindow->edl->local_session->set_selectionend(plugin->track->from_units(plugin->startproject) +
6080                                         plugin->track->from_units(plugin->length));
6081                                 if(mwindow->edl->session->cursor_on_frames) {
6082                                         mwindow->edl->local_session->set_selectionstart(
6083                                                 mwindow->edl->align_to_frame(mwindow->edl->local_session->get_selectionstart(1), 0));
6084                                         mwindow->edl->local_session->set_selectionend(
6085                                                 mwindow->edl->align_to_frame(mwindow->edl->local_session->get_selectionend(1), 1));
6086                                 }
6087                                 rerender = 1;
6088                                 redraw = 1;
6089                                 result = 1;
6090                         }
6091                 }
6092                 else
6093 // Move plugin
6094                 if( drag_start && plugin->track->is_armed() && !plugin->silence() ) {
6095                         if( mwindow->edl->session->editing_mode == EDITING_ARROW ) {
6096                                 if( plugin->track->data_type == TRACK_AUDIO )
6097                                         mwindow->session->current_operation = DRAG_AEFFECT_COPY;
6098                                 else if( plugin->track->data_type == TRACK_VIDEO )
6099                                         mwindow->session->current_operation = DRAG_VEFFECT_COPY;
6100
6101                                 mwindow->session->drag_plugin = plugin;
6102                                 mwindow->session->drag_origin_x = cursor_x;
6103                                 mwindow->session->drag_origin_y = cursor_y;
6104                                 // Where the drag started, so we know relative position inside the edit later
6105                                 mwindow->session->drag_position =
6106                                         (double)(cursor_x + mwindow->edl->local_session->view_start[pane->number]) *
6107                                         mwindow->edl->local_session->zoom_sample / mwindow->edl->session->sample_rate;
6108 // Create picon
6109                                 switch(plugin->plugin_type) {
6110                                 case PLUGIN_STANDALONE: {
6111                                         PluginServer *server =
6112                                                 mwindow->scan_plugindb(plugin->title, plugin->track->data_type);
6113                                         if( !server ) break;
6114                                         VFrame *frame = server->picon;
6115                                         if(!frame) {
6116                                                 if(plugin->track->data_type == TRACK_AUDIO) {
6117                                                         frame = mwindow->theme->get_image("aeffect_icon");
6118                                                 }
6119                                                 else {
6120                                                         frame = mwindow->theme->get_image("veffect_icon");
6121                                                 }
6122                                         }
6123                                         int cx, cy;
6124                                         get_abs_cursor(cx, cy);
6125                                         gui->drag_popup = new BC_DragWindow(gui, frame, cx, cy);
6126                                                 break; }
6127
6128                                 case PLUGIN_SHAREDPLUGIN:
6129                                 case PLUGIN_SHAREDMODULE: {
6130                                         VFrame *frame = mwindow->theme->get_image("clip_icon");
6131                                         int cx, cy;
6132                                         get_abs_cursor(cx, cy);
6133                                         gui->drag_popup = new BC_DragWindow(gui, frame, cx, cy);
6134                                         break; }
6135                                 }
6136
6137                                 result = 1;
6138                         }
6139                 }
6140         }
6141
6142         return result;
6143 }
6144
6145 int TrackCanvas::do_transitions(int cursor_x, int cursor_y,
6146         int button_press, int &new_cursor, int &update_cursor)
6147 {
6148         Transition *transition = 0;
6149         int result = 0;
6150         int64_t x, y, w, h;
6151
6152
6153         for( Track *track = mwindow->edl->tracks->first; track && !result; track = track->next ) {
6154                 if( track->is_hidden() ) continue;
6155                 if( !track->show_transitions() ) continue;
6156
6157                 for( Edit *edit = track->edits->first; edit; edit = edit->next ) {
6158                         if( edit->transition ) {
6159                                 edit_dimensions(edit, x, y, w, h);
6160                                 get_transition_coords(edit, x, y, w, h);
6161
6162                                 if( MWindowGUI::visible(x, x + w, 0, get_w()) &&
6163                                         MWindowGUI::visible(y, y + h, 0, get_h()) ) {
6164                                         if( cursor_x >= x && cursor_x < x + w &&
6165                                                 cursor_y >= y && cursor_y < y + h ) {
6166                                                 transition = edit->transition;
6167                                                 result = 1;
6168                                                 break;
6169                                         }
6170                                 }
6171                         }
6172                 }
6173         }
6174
6175         update_cursor = 1;
6176         if(transition) {
6177                 if(!button_press) {
6178                         new_cursor = UPRIGHT_ARROW_CURSOR;
6179                 }
6180                 else if(get_buttonpress() == RIGHT_BUTTON ) {
6181                         gui->transition_menu->update(transition);
6182                         gui->transition_menu->activate_menu();
6183                 }
6184         }
6185
6186         return result;
6187 }
6188
6189 int TrackCanvas::button_press_event()
6190 {
6191         int result = 0;
6192         int cursor_x, cursor_y;
6193         int new_cursor;
6194
6195         cursor_x = get_cursor_x();
6196         cursor_y = get_cursor_y();
6197         mwindow->session->trim_edits = 0;
6198
6199         if(is_event_win() && cursor_inside()) {
6200 //              double position = mwindow->edl->get_cursor_position(cursor_x, pane->number);
6201
6202                 result = 1;
6203                 if(!active) {
6204                         activate();
6205                 }
6206
6207                 if( get_buttonpress() == LEFT_BUTTON ) {
6208                         gui->stop_transport("TrackCanvas::button_press_event");
6209                 }
6210
6211                 int update_overlay = 0, update_cursor = 0, rerender = 0, update_message = 0;
6212
6213                 if(get_buttonpress() == WHEEL_UP) {
6214                         if(shift_down())
6215                                 mwindow->expand_sample();
6216                         else if(ctrl_down())
6217                                 mwindow->move_left(get_w()/ 10);
6218                         else
6219                                 mwindow->move_up(get_h() / 10);
6220                 }
6221                 else if(get_buttonpress() == WHEEL_DOWN) {
6222                         if(shift_down())
6223                                 mwindow->zoom_in_sample();
6224                         else if(ctrl_down())
6225                                 mwindow->move_right(get_w() / 10);
6226                         else
6227                                 mwindow->move_down(get_h() / 10);
6228                 }
6229                 else if(get_buttonpress() == 6) {
6230                         if(ctrl_down())
6231                                 mwindow->move_left(get_w());
6232                         else if(alt_down())
6233                                 mwindow->move_left(get_w() / 20);
6234                         else
6235                                 mwindow->move_left(get_w() / 5);
6236                 }
6237                 else if(get_buttonpress() == 7) {
6238                         if(ctrl_down())
6239                                 mwindow->move_right(get_w());
6240                         else if(alt_down())
6241                                 mwindow->move_right(get_w() / 20);
6242                         else
6243                                 mwindow->move_right(get_w() / 5);
6244                 }
6245                 else {
6246                         if( arrow_mode() ) do {
6247 // Test handles and resource boundaries and highlight a track
6248                                 if( do_transitions(cursor_x, cursor_y,
6249                                                 1, new_cursor, update_cursor) ) break;
6250
6251                                 if( do_keyframes(cursor_x, cursor_y,
6252                                         0, get_buttonpress(), new_cursor,
6253                                         update_cursor, rerender) ) break;
6254                                 update_message = 1;
6255
6256                                 if( do_transition_handles(cursor_x, cursor_y,
6257                                         1, rerender, update_overlay, new_cursor,
6258                                         update_cursor) ) break;
6259 // Test edit boundaries
6260                                 if( do_edit_handles(cursor_x, cursor_y,
6261                                         1, rerender, update_overlay, new_cursor,
6262                                         update_cursor) ) break;
6263 // Test plugin boundaries
6264                                 if( do_plugin_handles(cursor_x, cursor_y,
6265                                         1, rerender, update_overlay, new_cursor,
6266                                         update_cursor) ) break;
6267
6268                                 if( do_edits(cursor_x, cursor_y, 1, 0,
6269                                         update_overlay, rerender, new_cursor,
6270                                         update_cursor) ) break;
6271
6272                                 if( do_plugins(cursor_x, cursor_y, 0, 1,
6273                                         update_cursor, rerender) ) break;
6274
6275                                 if( test_resources(cursor_x, cursor_y) ) break;
6276
6277                                 if( do_tracks(cursor_x, cursor_y, 1) ) break;
6278
6279                                 result = 0;
6280                         } while(0);
6281                         else if( ibeam_mode() ) do {
6282 // Test handles only and select a region
6283                                 double position = mwindow->edl->get_cursor_position(cursor_x, pane->number);
6284 //printf("TrackCanvas::button_press_event %d\n", position);
6285
6286                                 if( do_transitions(cursor_x, cursor_y,
6287                                                 1, new_cursor, update_cursor)) break;
6288                                 if(do_keyframes(cursor_x, cursor_y,
6289                                         0, get_buttonpress(), new_cursor,
6290                                         update_cursor, rerender)) {
6291                                         update_overlay = 1;
6292                                         break;
6293                                 }
6294                                 update_message = 1;
6295                                 if( do_transition_handles(cursor_x, cursor_y,
6296                                         1, rerender, update_overlay, new_cursor, update_cursor) ) break;
6297 // Test edit boundaries
6298                                 if( do_edit_handles(cursor_x, cursor_y,
6299                                         1, rerender, update_overlay, new_cursor, update_cursor) ) break;
6300 // Test plugin boundaries
6301                                 if( do_plugin_handles(cursor_x, cursor_y,
6302                                         1, rerender, update_overlay, new_cursor, update_cursor) ) break;
6303
6304                                 if( do_edits(cursor_x, cursor_y,
6305                                         1, 0, update_cursor, rerender, new_cursor, update_cursor) ) break;
6306
6307                                 if( do_plugins(cursor_x, cursor_y, 0, 1, update_cursor, rerender) ) break;
6308
6309                                 if( do_tracks(cursor_x, cursor_y, 1) ) break;
6310 // Highlight selection
6311                                 if( get_buttonpress() != LEFT_BUTTON ) break;
6312                                 rerender = start_selection(position);
6313                                 mwindow->session->current_operation = SELECT_REGION;
6314                                 update_cursor = 1;
6315                         } while(0);
6316                 }
6317
6318                 if( rerender ) {
6319                         gui->unlock_window();
6320                         mwindow->cwindow->update(1, 0, 0, 0, 1);
6321                         gui->lock_window("TrackCanvas::button_press_event 2");
6322 // Update faders
6323                         mwindow->update_plugin_guis();
6324                         gui->update_patchbay();
6325                 }
6326
6327                 if( update_message )
6328                         gui->default_message();
6329
6330                 if( update_overlay )
6331                         gui->draw_overlays(1);
6332
6333                 if( update_cursor < 0 ) {
6334 // double_click edit
6335                         gui->swindow->update_selection();
6336                         gui->draw_canvas(0, 0);
6337                 }
6338                 if( update_cursor ) {
6339                         gui->update_timebar(0);
6340                         gui->hide_cursor(0);
6341                         gui->show_cursor(1);
6342                         gui->zoombar->update();
6343                         gui->flash_canvas(1);
6344                 }
6345         }
6346
6347         return result;
6348 }
6349
6350 int TrackCanvas::start_selection(double position)
6351 {
6352         int rerender = 0;
6353         position = mwindow->edl->align_to_frame(position, 1);
6354
6355
6356 // Extend a border
6357         if(shift_down())
6358         {
6359                 double midpoint = (mwindow->edl->local_session->get_selectionstart(1) +
6360                         mwindow->edl->local_session->get_selectionend(1)) / 2;
6361
6362                 if(position < midpoint)
6363                 {
6364                         mwindow->edl->local_session->set_selectionstart(position);
6365                         selection_midpoint = mwindow->edl->local_session->get_selectionend(1);
6366 // Que the CWindow
6367                         rerender = 1;
6368                 }
6369                 else
6370                 {
6371                         mwindow->edl->local_session->set_selectionend(position);
6372                         selection_midpoint = mwindow->edl->local_session->get_selectionstart(1);
6373 // Don't que the CWindow for the end
6374                 }
6375         }
6376         else
6377 // Start a new selection
6378         {
6379 //printf("TrackCanvas::start_selection %f\n", position);
6380                 mwindow->edl->local_session->set_selectionstart(position);
6381                 mwindow->edl->local_session->set_selectionend(position);
6382                 selection_midpoint = position;
6383 // Que the CWindow
6384                 rerender = 1;
6385         }
6386
6387         return rerender;
6388 }
6389
6390 void TrackCanvas::end_edithandle_selection()
6391 {
6392         mwindow->modify_edithandles();
6393         mwindow->session->drag_edit = 0;
6394 }
6395
6396 void TrackCanvas::end_pluginhandle_selection()
6397 {
6398         mwindow->modify_pluginhandles();
6399         mwindow->session->drag_plugin = 0;
6400 }
6401
6402 void TrackCanvas::end_transnhandle_selection()
6403 {
6404         mwindow->modify_transnhandles();
6405         mwindow->session->drag_transition = 0;
6406 }
6407
6408
6409 double TrackCanvas::time_visible()
6410 {
6411         return (double)get_w() *
6412                 mwindow->edl->local_session->zoom_sample /
6413                 mwindow->edl->session->sample_rate;
6414 }
6415
6416
6417 void TrackCanvas::show_message(Auto *current, int box_color, const char *fmt, ...)
6418 {
6419         char string[BCTEXTLEN];
6420         char *cp = string, *ep = cp + sizeof(string)-1;
6421         if( box_color >= 0 ) {
6422                 cp += snprintf(string, ep-cp, "%-8s ",
6423                         FloatAuto::curve_name(((FloatAuto*)current)->curve_mode));
6424         }
6425         char string2[BCTEXTLEN];
6426         Units::totext(string2,
6427                 current->autos->track->from_units(current->position),
6428                 mwindow->edl->session->time_format,
6429                 mwindow->edl->session->sample_rate,
6430                 mwindow->edl->session->frame_rate,
6431                 mwindow->edl->session->frames_per_foot);
6432         cp += snprintf(cp, ep-cp, "%s", string2);
6433         va_list ap;
6434         va_start(ap, fmt);
6435         vsnprintf(cp, ep-cp, fmt, ap);
6436         va_end(ap);
6437         gui->show_message(string, -1, box_color);
6438 }
6439
6440 // Patchbay* TrackCanvas::get_patchbay()
6441 // {
6442 //      if(pane->patchbay) return pane->patchbay;
6443 //      if(gui->total_panes() == 2 &&
6444 //              gui->pane[TOP_LEFT_PANE] &&
6445 //              gui->pane[TOP_RIGHT_PANE])
6446 //              return gui->pane[TOP_LEFT_PANE]->patchbay;
6447 //      if(gui->total_panes() == 4)
6448 //      {
6449 //              if(pane->number == TOP_RIGHT_PANE)
6450 //                      return gui->pane[TOP_LEFT_PANE]->patchbay;
6451 //              else
6452 //                      return gui->pane[BOTTOM_LEFT_PANE]->patchbay;
6453 //      }
6454 //
6455 //      return 0;
6456 // }
6457
6458