add Autosave continuous backups by Andras Reuss and Andrew-R
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / mwindowmove.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "automation.h"
23 #include "autos.h"
24 #include "clip.h"
25 #include "cplayback.h"
26 #include "cwindow.h"
27 #include "cwindowgui.h"
28 #include "edits.h"
29 #include "edl.h"
30 #include "edlsession.h"
31 #include "gwindowgui.h"
32 #include "keyframe.h"
33 #include "labels.h"
34 #include "localsession.h"
35 #include "maincursor.h"
36 #include "mainmenu.h"
37 #include "mainsession.h"
38 #include "mtimebar.h"
39 #include "mwindow.h"
40 #include "mwindowgui.h"
41 #include "patchbay.h"
42 #include "playbackengine.h"
43 #include "plugin.h"
44 #include "pluginset.h"
45 #include "resourcethread.h"
46 #include "samplescroll.h"
47 #include "theme.h"
48 #include "trackcanvas.h"
49 #include "tracks.h"
50 #include "track.h"
51 #include "transportque.h"
52 #include "zoombar.h"
53
54
55 void MWindow::update_plugins()
56 {
57 // Show plugins which are visible and hide plugins which aren't
58 // Update plugin pointers in plugin servers
59 }
60
61
62 int MWindow::expand_sample()
63 {
64         if(gui)
65         {
66                 if(edl->local_session->zoom_sample < 0x100000)
67                 {
68                         edl->local_session->zoom_sample *= 2;
69                         gui->zoombar->sample_zoom->update(edl->local_session->zoom_sample);
70                         zoom_sample(edl->local_session->zoom_sample);
71                 }
72         }
73         return 0;
74 }
75
76 int MWindow::zoom_in_sample()
77 {
78         if(gui)
79         {
80                 if(edl->local_session->zoom_sample > 1)
81                 {
82                         edl->local_session->zoom_sample /= 2;
83                         gui->zoombar->sample_zoom->update(edl->local_session->zoom_sample);
84                         zoom_sample(edl->local_session->zoom_sample);
85                 }
86         }
87         return 0;
88 }
89
90 int MWindow::zoom_sample(int64_t zoom_sample)
91 {
92         CLAMP(zoom_sample, 1, 0x100000);
93         edl->local_session->zoom_sample = zoom_sample;
94         find_cursor();
95
96         TimelinePane *pane = gui->get_focused_pane();
97         samplemovement(edl->local_session->view_start[pane->number], pane->number);
98         return 0;
99 }
100
101 void MWindow::find_cursor()
102 {
103         TimelinePane *pane = gui->get_focused_pane();
104         edl->local_session->view_start[pane->number] =
105                 Units::round((edl->local_session->get_selectionend(1) +
106                 edl->local_session->get_selectionstart(1)) /
107                 2 *
108                 edl->session->sample_rate /
109                 edl->local_session->zoom_sample -
110                 (double)pane->canvas->get_w() /
111                 2);
112
113         if(edl->local_session->view_start[pane->number] < 0)
114                 edl->local_session->view_start[pane->number] = 0;
115 }
116
117
118 void MWindow::fit_selection()
119 {
120         if(EQUIV(edl->local_session->get_selectionstart(1),
121                 edl->local_session->get_selectionend(1)))
122         {
123                 double total_samples = edl->tracks->total_length() *
124                         edl->session->sample_rate;
125                 TimelinePane *pane = gui->get_focused_pane();
126                 for(edl->local_session->zoom_sample = 1;
127                         pane->canvas->get_w() * edl->local_session->zoom_sample < total_samples;
128                         edl->local_session->zoom_sample *= 2)
129                         ;
130         }
131         else
132         {
133                 double total_samples = (edl->local_session->get_selectionend(1) -
134                         edl->local_session->get_selectionstart(1)) *
135                         edl->session->sample_rate;
136                 TimelinePane *pane = gui->get_focused_pane();
137                 for(edl->local_session->zoom_sample = 1;
138                         pane->canvas->get_w() * edl->local_session->zoom_sample < total_samples;
139                         edl->local_session->zoom_sample *= 2)
140                         ;
141         }
142
143         edl->local_session->zoom_sample = MIN(0x100000,
144                 edl->local_session->zoom_sample);
145         zoom_sample(edl->local_session->zoom_sample);
146 }
147
148
149 void MWindow::fit_autos(int all)
150 {
151         float min = 0, max = 0;
152         double start, end;
153
154 // Test all autos
155         if(EQUIV(edl->local_session->get_selectionstart(1),
156                 edl->local_session->get_selectionend(1)))
157         {
158                 start = 0;
159                 end = edl->tracks->total_length();
160         }
161         else
162 // Test autos in highlighting only
163         {
164                 start = edl->local_session->get_selectionstart(1);
165                 end = edl->local_session->get_selectionend(1);
166         }
167
168         int forstart = edl->local_session->zoombar_showautotype;
169         int forend   = edl->local_session->zoombar_showautotype + 1;
170
171         if( all ) {
172                 forstart = 0;
173                 forend   = AUTOGROUPTYPE_COUNT;
174         }
175
176         for (int i = forstart; i < forend; i++)
177         {
178 // Adjust min and max
179                 edl->tracks->get_automation_extents(&min, &max, start, end, i);
180 //printf("MWindow::fit_autos %d %f %f results in ", i, min, max);
181
182                 float range = max - min;
183                 switch (i)
184                 {
185                 case AUTOGROUPTYPE_AUDIO_FADE:
186                         if (range < 1) {
187                                 min = MIN(min, edl->local_session->automation_mins[i]);
188                                 max = MAX(max, edl->local_session->automation_maxs[i]);
189                                 if( min >= max-0.1 ) { min = -80.0; min = 6.0; }
190                         }
191                         break;
192                 case AUTOGROUPTYPE_VIDEO_FADE:
193                         if (range < 1) {
194                                 min = MIN(min, edl->local_session->automation_mins[i]);
195                                 max = MAX(max, edl->local_session->automation_maxs[i]);
196                                 if( min >= max-0.1 ) { min = 0.0; min = 100.0; }
197                         }
198                         break;
199                 case AUTOGROUPTYPE_ZOOM:
200                         if (range < 0.001) {
201                                 min = floor(min*50)/100;
202                                 max = floor(max*200)/100;
203                         }
204                         break;
205                 case AUTOGROUPTYPE_SPEED:
206                         if (range < 0.001) {
207                                 min = floor(min*5)/100;
208                                 max = floor(max*300)/100;
209                         }
210                         break;
211                 case AUTOGROUPTYPE_X:
212                 case AUTOGROUPTYPE_Y:
213                         if (range < 1) {
214                                 float scale = bmin(edl->session->output_w, edl->session->output_h);
215                                 min = floor((min+max)/2) - 0.5*scale;
216                                 max = floor((min+max)/2) + 0.5*scale;
217                         }
218                         break;
219                 }
220 //printf("%f %f\n", min, max);
221                 if (!Automation::autogrouptypes_fixedrange[i])
222                 {
223                         edl->local_session->automation_mins[i] = min;
224                         edl->local_session->automation_maxs[i] = max;
225                 }
226         }
227
228 // Show range in zoombar
229         gui->zoombar->update();
230
231 // Draw
232         gui->draw_overlays(1);
233 }
234
235
236 void MWindow::change_currentautorange(int autogrouptype, int increment, int changemax)
237 {
238         float val;
239         if (changemax) {
240                 val = edl->local_session->automation_maxs[autogrouptype];
241         } else {
242                 val = edl->local_session->automation_mins[autogrouptype];
243         }
244
245         if (increment)
246         {
247                 switch (autogrouptype) {
248                 case AUTOGROUPTYPE_AUDIO_FADE:
249                         val += 2;
250                         break;
251                 case AUTOGROUPTYPE_VIDEO_FADE:
252                         val += 1;
253                         break;
254                 case AUTOGROUPTYPE_ZOOM:
255                 case AUTOGROUPTYPE_SPEED:
256                         if (val == 0)
257                                 val = 0.001;
258                         else
259                                 val = val*2;
260                         break;
261                 case AUTOGROUPTYPE_X:
262                 case AUTOGROUPTYPE_Y:
263                         val = floor(val + 50);
264                         break;
265                 }
266         }
267         else
268         { // decrement
269                 switch (autogrouptype) {
270                 case AUTOGROUPTYPE_AUDIO_FADE:
271                         val -= 2;
272                         break;
273                 case AUTOGROUPTYPE_VIDEO_FADE:
274                         val -= 1;
275                         break;
276                 case AUTOGROUPTYPE_ZOOM:
277                 case AUTOGROUPTYPE_SPEED:
278                         if (val > 0) val = val/2;
279                         break;
280                 case AUTOGROUPTYPE_X:
281                 case AUTOGROUPTYPE_Y:
282                         val = floor(val-50);
283                         break;
284                 }
285         }
286
287         AUTOMATIONVIEWCLAMPS(val, autogrouptype);
288
289         if (changemax) {
290                 if (val > edl->local_session->automation_mins[autogrouptype])
291                         edl->local_session->automation_maxs[autogrouptype] = val;
292         }
293         else
294         {
295                 if (val < edl->local_session->automation_maxs[autogrouptype])
296                         edl->local_session->automation_mins[autogrouptype] = val;
297         }
298 }
299
300 void MWindow::update_autorange(int type, int increment, int use_max)
301 {
302         gui->lock_window("MWindow::update_autorange");
303         int group = Automation::autogrouptype(type, 0);
304         change_currentautorange(group, increment, use_max);
305         int color = GWindowGUI::auto_colors[type];
306         gui->zoombar->update_autozoom(group, color);
307         gui->draw_overlays(0);
308         gui->update_patchbay();
309         gui->flash_canvas(1);
310         gui->unlock_window();
311 }
312
313 void MWindow::expand_autos(int changeall, int domin, int domax)
314 {
315         if (changeall)
316                 for (int i = 0; i < AUTOGROUPTYPE_COUNT; i++) {
317                         if (domin) change_currentautorange(i, 1, 0);
318                         if (domax) change_currentautorange(i, 1, 1);
319                 }
320         else
321         {
322                 if (domin) change_currentautorange(edl->local_session->zoombar_showautotype, 1, 0);
323                 if (domax) change_currentautorange(edl->local_session->zoombar_showautotype, 1, 1);
324         }
325         gui->zoombar->update_autozoom();
326         gui->draw_overlays(0);
327         gui->update_patchbay();
328         gui->flash_canvas(1);
329 }
330
331 void MWindow::shrink_autos(int changeall, int domin, int domax)
332 {
333         if (changeall)
334                 for (int i = 0; i < AUTOGROUPTYPE_COUNT; i++) {
335                         if (domin) change_currentautorange(i, 0, 0);
336                         if (domax) change_currentautorange(i, 0, 1);
337                 }
338         else
339         {
340                 if (domin) change_currentautorange(edl->local_session->zoombar_showautotype, 0, 0);
341                 if (domax) change_currentautorange(edl->local_session->zoombar_showautotype, 0, 1);
342         }
343         gui->zoombar->update_autozoom();
344         gui->draw_overlays(0);
345         gui->update_patchbay();
346         gui->flash_canvas(1);
347 }
348
349
350 void MWindow::zoom_autos(float min, float max)
351 {
352         int i = edl->local_session->zoombar_showautotype;
353         edl->local_session->automation_mins[i] = min;
354         edl->local_session->automation_maxs[i] = max;
355         gui->zoombar->update_autozoom();
356         gui->draw_overlays(1);
357 }
358
359
360 void MWindow::zoom_amp(int64_t zoom_amp)
361 {
362         edl->local_session->zoom_y = zoom_amp;
363         gui->draw_canvas(0, 0);
364         gui->flash_canvas(0);
365         gui->update_patchbay();
366         gui->flush();
367 }
368
369 void MWindow::zoom_atrack(int64_t zoom)
370 {
371         int64_t old_zoom = edl->local_session->zoom_atrack;
372         CLAMP(zoom, MIN_TRACK_ZOOM, MAX_TRACK_ZOOM);
373         edl->local_session->zoom_atrack = zoom;
374
375         edl->local_session->zoom_atrack = zoom;
376         for( Track *track= edl->tracks->first; track; track=track->next ) {
377                 if( track->data_type != TRACK_AUDIO ) continue;
378                 track->data_h = zoom;
379         }
380 // shift row position
381         for( int i=0; i<TOTAL_PANES; ++i ) edl->local_session->track_start[i] =
382                 (edl->local_session->track_start[i] * zoom) / old_zoom;
383         edl->tracks->update_y_pixels(theme);
384         gui->draw_trackmovement();
385 }
386
387 void MWindow::zoom_vtrack(int64_t zoom)
388 {
389         int64_t old_zoom = edl->local_session->zoom_vtrack;
390         CLAMP(zoom, MIN_TRACK_ZOOM, MAX_TRACK_ZOOM);
391         edl->local_session->zoom_vtrack = zoom;
392
393         for( Track *track= edl->tracks->first; track; track=track->next ) {
394                 if( track->data_type != TRACK_VIDEO ) continue;
395                 track->data_h = zoom;
396         }
397 // shift row position
398         for( int i=0; i<TOTAL_PANES; ++i ) edl->local_session->track_start[i] =
399                 (edl->local_session->track_start[i] * zoom) / old_zoom;
400         edl->tracks->update_y_pixels(theme);
401         gui->draw_trackmovement();
402 }
403
404 void MWindow::trackmovement(int offset, int pane_number)
405 {
406         edl->local_session->track_start[pane_number] += offset;
407         if(edl->local_session->track_start[pane_number] < 0)
408                 edl->local_session->track_start[pane_number] = 0;
409
410         if(pane_number == TOP_RIGHT_PANE ||
411                 pane_number == TOP_LEFT_PANE)
412         {
413                 edl->local_session->track_start[TOP_LEFT_PANE] =
414                         edl->local_session->track_start[TOP_RIGHT_PANE] =
415                         edl->local_session->track_start[pane_number];
416         }
417         else
418         if(pane_number == BOTTOM_RIGHT_PANE ||
419                 pane_number == BOTTOM_LEFT_PANE)
420         {
421                 edl->local_session->track_start[BOTTOM_LEFT_PANE] =
422                         edl->local_session->track_start[BOTTOM_RIGHT_PANE] =
423                         edl->local_session->track_start[pane_number];
424         }
425
426
427         edl->tracks->update_y_pixels(theme);
428         gui->draw_trackmovement();
429 }
430
431 void MWindow::move_up(int64_t distance)
432 {
433         TimelinePane *pane = gui->get_focused_pane();
434         if(distance == 0) distance = pane->canvas->get_h() / 10;
435         trackmovement(-distance, pane->number);
436 }
437
438 void MWindow::move_down(int64_t distance)
439 {
440         TimelinePane *pane = gui->get_focused_pane();
441         if(distance == 0) distance = pane->canvas->get_h() / 10;
442         trackmovement(distance, pane->number);
443 }
444
445 int MWindow::goto_end()
446 {
447         TimelinePane *pane = gui->get_focused_pane();
448         int64_t old_view_start = edl->local_session->view_start[pane->number];
449
450         if( edl->tracks->total_length() > (double)pane->canvas->get_w() *
451                 edl->local_session->zoom_sample / edl->session->sample_rate ) {
452                 edl->local_session->view_start[pane->number] =
453                         Units::round(edl->tracks->total_length() *
454                                 edl->session->sample_rate /
455                                 edl->local_session->zoom_sample -
456                                 pane->canvas->get_w() /
457                                 2);
458         }
459         else
460         {
461                 edl->local_session->view_start[pane->number] = 0;
462         }
463
464         if(gui->shift_down())
465         {
466                 edl->local_session->set_selectionend(edl->tracks->total_length());
467         }
468         else
469         {
470                 edl->local_session->set_selectionstart(edl->tracks->total_length());
471                 edl->local_session->set_selectionend(edl->tracks->total_length());
472         }
473
474         if(edl->local_session->view_start[pane->number] != old_view_start)
475         {
476                 samplemovement(edl->local_session->view_start[pane->number], pane->number);
477                 gui->draw_samplemovement();
478         }
479
480         update_plugin_guis();
481
482         gui->update_patchbay();
483         gui->update_cursor();
484         gui->activate_timeline();
485         gui->zoombar->update();
486         gui->update_timebar(1);
487         cwindow->update(1, 0, 0, 0, 1);
488         return 0;
489 }
490
491 int MWindow::goto_start()
492 {
493         TimelinePane *pane = gui->get_focused_pane();
494         int64_t old_view_start = edl->local_session->view_start[pane->number];
495
496         edl->local_session->view_start[pane->number] = 0;
497         if(gui->shift_down())
498         {
499                 edl->local_session->set_selectionstart(0);
500         }
501         else
502         {
503                 edl->local_session->set_selectionstart(0);
504                 edl->local_session->set_selectionend(0);
505         }
506
507         if(edl->local_session->view_start[pane->number] != old_view_start)
508         {
509                 samplemovement(edl->local_session->view_start[pane->number], pane->number);
510                 gui->draw_samplemovement();
511         }
512
513         update_plugin_guis();
514         gui->update_patchbay();
515         gui->update_cursor();
516         gui->activate_timeline();
517         gui->zoombar->update();
518         gui->update_timebar(1);
519         cwindow->update(1, 0, 0, 0, 1);
520         return 0;
521 }
522
523 int MWindow::goto_position(double position)
524 {
525         position = edl->align_to_frame(position, 0);
526         if( position < 0 ) position = 0;
527         select_point(position);
528         gui->activate_timeline();
529         return 0;
530 }
531
532 int MWindow::samplemovement(int64_t view_start, int pane_number)
533 {
534         if( view_start < 0 ) view_start = 0;
535         edl->local_session->view_start[pane_number] = view_start;
536         if(edl->local_session->view_start[pane_number] < 0)
537                 edl->local_session->view_start[pane_number] = 0;
538
539         if(pane_number == TOP_LEFT_PANE ||
540                 pane_number == BOTTOM_LEFT_PANE)
541         {
542                 edl->local_session->view_start[TOP_LEFT_PANE] =
543                         edl->local_session->view_start[BOTTOM_LEFT_PANE] =
544                         edl->local_session->view_start[pane_number];
545         }
546         else
547         {
548                 edl->local_session->view_start[TOP_RIGHT_PANE] =
549                         edl->local_session->view_start[BOTTOM_RIGHT_PANE] =
550                         edl->local_session->view_start[pane_number];
551         }
552
553         gui->draw_samplemovement();
554
555         return 0;
556 }
557
558 int MWindow::move_left(int64_t distance)
559 {
560         TimelinePane *pane = gui->get_focused_pane();
561         if(!distance)
562                 distance = pane->canvas->get_w() / 10;
563         edl->local_session->view_start[pane->number] -= distance;
564         samplemovement(edl->local_session->view_start[pane->number],
565                 pane->number);
566         return 0;
567 }
568
569 int MWindow::move_right(int64_t distance)
570 {
571         TimelinePane *pane = gui->get_focused_pane();
572         if(!distance)
573                 distance = pane->canvas->get_w() / 10;
574         edl->local_session->view_start[pane->number] += distance;
575         samplemovement(edl->local_session->view_start[pane->number],
576                 pane->number);
577         return 0;
578 }
579
580 void MWindow::select_all()
581 {
582         if( edl->local_session->get_selectionstart(1) == 0 &&
583             edl->local_session->get_selectionend(1) == edl->tracks->total_length() )
584                 edl->local_session->set_selectionend(0);
585         else {
586                 edl->local_session->set_selectionstart(0);
587                 edl->local_session->set_selectionend(edl->tracks->total_length());
588         }
589         gui->update(0, NORMAL_DRAW, 1, 1, 0, 1, 0);
590         gui->activate_timeline();
591         cwindow->update(1, 0, 0, 0, 1);
592         update_plugin_guis();
593 }
594
595 int MWindow::next_label(int shift_down)
596 {
597         double position = edl->local_session->get_selectionend(1);
598         double total_length = edl->tracks->total_length();
599         Label *current = edl->labels->next_label(position);
600         double new_position = current ? current->position : total_length;
601 // last playback endpoints as fake label positions
602         double playback_start = edl->local_session->playback_start;
603         double playback_end = edl->local_session->playback_end;
604         if( playback_start > position && playback_start < new_position )
605                 new_position = playback_start;
606         else if( playback_end > position && playback_end < new_position )
607                 new_position = playback_end;
608         if( new_position > total_length )
609                 new_position = total_length;
610         edl->local_session->set_selectionend(new_position);
611 //printf("MWindow::next_edit_handle %d\n", shift_down);
612         if( !shift_down )
613                 edl->local_session->set_selectionstart(new_position);
614         return find_selection(new_position);
615 }
616
617 int MWindow::prev_label(int shift_down)
618 {
619         double position = edl->local_session->get_selectionstart(1);
620         Label *current = edl->labels->prev_label(position);
621         double new_position = current ? current->position : 0;
622 // last playback endpoints as fake label positions
623         double playback_start = edl->local_session->playback_start;
624         double playback_end = edl->local_session->playback_end;
625         if( playback_end < position && playback_end > new_position )
626                 new_position = playback_end;
627         else if( playback_start < position && playback_start > new_position )
628                 new_position = playback_start;
629         if( new_position < 0 )
630                 new_position = 0;
631         edl->local_session->set_selectionstart(new_position);
632 //printf("MWindow::next_edit_handle %d\n", shift_down);
633         if( !shift_down )
634                 edl->local_session->set_selectionend(new_position);
635         return find_selection(new_position);
636 }
637
638 int MWindow::next_edit_handle(int shift_down)
639 {
640         double position = edl->local_session->get_selectionend(1);
641         double new_position = edl->next_edit(position);
642         double total_length = edl->tracks->total_length();
643         if( new_position >= total_length )
644                 new_position = total_length;
645         edl->local_session->set_selectionend(new_position);
646 //printf("MWindow::next_edit_handle %d\n", shift_down);
647         if( !shift_down )
648                 edl->local_session->set_selectionstart(new_position);
649         return find_selection(new_position);
650 }
651
652 int MWindow::prev_edit_handle(int shift_down)
653 {
654         double position = edl->local_session->get_selectionstart(1);
655         double new_position = edl->prev_edit(position);
656         if( new_position < 0 )
657                 new_position = 0;
658         edl->local_session->set_selectionstart(new_position);
659         if( !shift_down )
660                 edl->local_session->set_selectionend(new_position);
661         return find_selection(new_position);
662 }
663
664 int MWindow::nearest_plugin_keyframe(int shift_down, int dir)
665 {
666         KeyFrame *keyframe = 0;
667         double start = edl->local_session->get_selectionstart(1);
668         double end = edl->local_session->get_selectionend(1);
669         double position = dir == PLAY_FORWARD ? end : start;
670         double new_position = dir == PLAY_FORWARD ? start : end;
671         for( Track *track=edl->tracks->first; track; track=track->next ) {
672                 if( !track->is_armed() ) continue;
673                 for( int i=0; i<track->plugin_set.size(); ++i ) {
674                         PluginSet *plugin_set = track->plugin_set[i];
675                         int64_t pos = track->to_units(position, 0);
676                         KeyFrame *key = plugin_set->nearest_keyframe(pos, dir);
677                         if( !key ) continue;
678                         double key_position = track->from_units(key->position);
679                         if( keyframe && (dir == PLAY_FORWARD ?
680                                 key_position >= new_position :
681                                 new_position >= key_position ) ) continue;
682                         keyframe = key;  new_position = key_position;
683                 }
684         }
685
686         new_position = keyframe ?
687                 keyframe->autos->track->from_units(keyframe->position) :
688                 dir == PLAY_FORWARD ? edl->tracks->total_length() : 0;
689
690         if( !shift_down )
691                 start = end = new_position;
692         else if( dir == PLAY_FORWARD )
693                 end = new_position;
694         else
695                 start = new_position;
696
697         edl->local_session->set_selectionstart(start);
698         edl->local_session->set_selectionend(end);
699         return find_selection(new_position);
700 }
701
702 int MWindow::nearest_auto_keyframe(int shift_down, int dir)
703 {
704         Auto *keyframe = 0;
705         double start = edl->local_session->get_selectionstart(1);
706         double end = edl->local_session->get_selectionend(1);
707         double position = dir == PLAY_FORWARD ? end : start;
708         double new_position = dir == PLAY_FORWARD ? start : end;
709         for( Track *track=edl->tracks->first; track; track=track->next ) {
710                 if( !track->is_armed() ) continue;
711                 int64_t pos = track->to_units(position, 0);
712                 for( int i=0; i<AUTOMATION_TOTAL; ++i ) {
713                         Autos *autos = track->automation->autos[i];
714                         if( !autos ) continue;
715                         Auto *key = dir == PLAY_FORWARD ?
716                                  autos->nearest_after(pos) :
717                                  autos->nearest_before(pos);
718                         if( !key ) continue;
719                         double key_position = track->from_units(key->position);
720                         if( keyframe && (dir == PLAY_FORWARD ?
721                                 key_position >= new_position :
722                                 new_position >= key_position ) ) continue;
723                         keyframe = key;  new_position = key_position;
724                 }
725         }
726
727         new_position = keyframe ?
728                 keyframe->autos->track->from_units(keyframe->position) :
729                 dir == PLAY_FORWARD ? edl->tracks->total_length() : 0;
730
731         if( !shift_down )
732                 start = end = new_position;
733         else if( dir == PLAY_FORWARD )
734                 end = new_position;
735         else
736                 start = new_position;
737
738         edl->local_session->set_selectionstart(start);
739         edl->local_session->set_selectionend(end);
740         return find_selection(new_position);
741 }
742
743 int MWindow::find_selection(double position, int scroll_display)
744 {
745         update_plugin_guis();
746         TimelinePane *pane = gui->get_focused_pane();
747         double pixel_zoom = (double)edl->session->sample_rate / edl->local_session->zoom_sample;
748         if( !scroll_display ) {
749                 double timeline_start = edl->local_session->view_start[pane->number] / pixel_zoom;
750                 double timeline_end = timeline_start + pane->canvas->time_visible();
751                 if( position >= timeline_end || position < timeline_start )
752                         scroll_display = 1;
753         }
754         if( scroll_display ) {
755                 samplemovement((int64_t)(position * pixel_zoom - pane->canvas->get_w() / 2), pane->number);
756                 cwindow->update(1, 0, 0, 0, 0);
757         }
758         else {
759                 gui->update_patchbay();
760                 gui->update_timebar(0);
761                 gui->hide_cursor(0);
762                 gui->draw_cursor(0);
763                 gui->zoombar->update();
764                 gui->flash_canvas(1);
765                 cwindow->update(1, 0, 0, 0, 1);
766         }
767         return 0;
768 }
769
770 double MWindow::get_position()
771 {
772         return edl->local_session->get_selectionstart(1);
773 }
774
775 void MWindow::set_position(double position)
776 {
777         if( position != get_position() ) {
778                 if( position < 0 ) position = 0;
779                 edl->local_session->set_selectionstart(position);
780                 edl->local_session->set_selectionend(position);
781                 gui->lock_window();
782                 find_cursor();
783                 gui->update(1, NORMAL_DRAW, 1, 1, 1, 1, 0);
784                 gui->unlock_window();
785                 cwindow->update(1, 0, 0, 0, 0);
786         }
787 }
788
789
790 double MWindow::get_timecode_offset()
791 {
792         return edl->session->timecode_offset;
793 }
794
795 void MWindow::set_timecode_offset(double offset)
796 {
797         edl->session->time_format = TIME_TIMECODE;
798         edl->session->timecode_offset = offset;
799         gui->lock_window();
800         gui->update(1, NORMAL_DRAW, 1, 1, 1, 1, 0);
801         gui->unlock_window();
802         cwindow->update(1, 0, 0, 0, 0);
803 }
804
805
806 int MWindow::expand_y()
807 {
808         int result = edl->local_session->zoom_y * 2;
809         result = MIN(result, MAX_AMP_ZOOM);
810         zoom_amp(result);
811         gui->zoombar->update();
812         return 0;
813 }
814
815 int MWindow::zoom_in_y()
816 {
817         int result = edl->local_session->zoom_y / 2;
818         result = MAX(result, MIN_AMP_ZOOM);
819         zoom_amp(result);
820         gui->zoombar->update();
821         return 0;
822 }
823
824 int MWindow::expand_t()
825 {
826         int zoom = edl->local_session->zoom_atrack * 2;
827         zoom = MIN(zoom, MAX_TRACK_ZOOM);
828         zoom_atrack(zoom);
829         zoom = edl->local_session->zoom_vtrack * 2;
830         zoom = MIN(zoom, MAX_TRACK_ZOOM);
831         zoom_vtrack(zoom);
832         gui->zoombar->update();
833         return 0;
834 }
835
836 int MWindow::zoom_in_t()
837 {
838         int zoom = edl->local_session->zoom_atrack / 2;
839         zoom = MAX(zoom, MIN_TRACK_ZOOM);
840         zoom_atrack(zoom);
841         zoom = edl->local_session->zoom_vtrack / 2;
842         zoom = MAX(zoom, MIN_TRACK_ZOOM);
843         zoom_vtrack(zoom);
844         gui->zoombar->update();
845         return 0;
846 }
847
848 void MWindow::split_x()
849 {
850         gui->resource_thread->stop_draw(1);
851
852         if(gui->pane[TOP_RIGHT_PANE])
853         {
854                 gui->delete_x_pane(theme->mcanvas_w);
855                 edl->local_session->x_pane = -1;
856         }
857         else
858         {
859                 gui->create_x_pane(theme->mcanvas_w / 2);
860                 edl->local_session->x_pane = theme->mcanvas_w / 2;
861         }
862
863         gui->mainmenu->update_toggles(0);
864         gui->update_pane_dividers();
865         gui->update_cursor();
866         gui->draw_samplemovement();
867 // required to get new widgets to appear
868         gui->show_window();
869
870         gui->resource_thread->start_draw();
871 }
872
873 void MWindow::split_y()
874 {
875         gui->resource_thread->stop_draw(1);
876         if(gui->pane[BOTTOM_LEFT_PANE])
877         {
878                 gui->delete_y_pane(theme->mcanvas_h);
879                 edl->local_session->y_pane = -1;
880         }
881         else
882         {
883                 gui->create_y_pane(theme->mcanvas_h / 2);
884                 edl->local_session->y_pane = theme->mcanvas_h / 2;
885         }
886
887         gui->mainmenu->update_toggles(0);
888         gui->update_pane_dividers();
889         gui->update_cursor();
890         gui->draw_trackmovement();
891 // required to get new widgets to appear
892         gui->show_window();
893         gui->resource_thread->start_draw();
894 }
895