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