repeat play, in/out <> shortcuts, append to proj wording, cleanup
[goodguy/history.git] / cinelerra-5.1 / cinelerra / cwindowgui.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 "bcsignals.h"
25 #include "canvas.h"
26 #include "clip.h"
27 #include "cpanel.h"
28 #include "cplayback.h"
29 #include "ctimebar.h"
30 #include "cursors.h"
31 #include "cwindowgui.h"
32 #include "cwindow.h"
33 #include "cwindowtool.h"
34 #include "editpanel.h"
35 #include "edl.h"
36 #include "edlsession.h"
37 #include "floatauto.h"
38 #include "floatautos.h"
39 #include "keys.h"
40 #include "language.h"
41 #include "localsession.h"
42 #include "mainclock.h"
43 #include "mainmenu.h"
44 #include "mainundo.h"
45 #include "mainsession.h"
46 #include "maskauto.h"
47 #include "maskautos.h"
48 #include "mbuttons.h"
49 #include "meterpanel.h"
50 #include "mwindowgui.h"
51 #include "mwindow.h"
52 #include "mwindow.h"
53 #include "playback3d.h"
54 #include "playtransport.h"
55 #include "theme.h"
56 #include "trackcanvas.h"
57 #include "tracks.h"
58 #include "transportque.h"
59 #include "vtrack.h"
60
61
62 static double my_zoom_table[] =
63 {
64         0.25,
65         0.33,
66         0.50,
67         0.75,
68         1.0,
69         1.5,
70         2.0,
71         3.0,
72         4.0
73 };
74
75 static int total_zooms = sizeof(my_zoom_table) / sizeof(double);
76
77
78 CWindowGUI::CWindowGUI(MWindow *mwindow, CWindow *cwindow)
79  : BC_Window(_(PROGRAM_NAME ": Compositor"),
80         mwindow->session->cwindow_x,
81     mwindow->session->cwindow_y,
82     mwindow->session->cwindow_w,
83     mwindow->session->cwindow_h,
84     100,
85     100,
86     1,
87     1,
88     1,
89         BC_WindowBase::get_resources()->bg_color,
90         mwindow->get_cwindow_display())
91 {
92         this->mwindow = mwindow;
93         this->cwindow = cwindow;
94         affected_track = 0;
95         affected_x = affected_y = affected_z = 0;
96         mask_keyframe = 0;
97         orig_mask_keyframe = new MaskAuto(0, 0);
98         affected_point = 0;
99         x_offset = y_offset = 0;
100         x_origin = y_origin = 0;
101         current_operation = CWINDOW_NONE;
102         tool_panel = 0;
103         active = 0;
104         inactive = 0;
105         crop_handle = -1; crop_translate = 0;
106         crop_origin_x = crop_origin_y = 0;
107         crop_origin_x1 = crop_origin_y1 = 0;
108         crop_origin_x2 = crop_origin_y2 = 0;
109         eyedrop_visible = 0;
110         eyedrop_x = eyedrop_y = 0;
111         ruler_origin_x = ruler_origin_y = 0;
112         ruler_handle = -1; ruler_translate = 0;
113         center_x = center_y = center_z = 0;
114         control_in_x = control_in_y = 0;
115         control_out_x = control_out_y = 0;
116         translating_zoom = 0;
117         highlighted = 0;
118 }
119
120 CWindowGUI::~CWindowGUI()
121 {
122         if(tool_panel) delete tool_panel;
123         delete meters;
124         delete composite_panel;
125         delete canvas;
126         delete transport;
127         delete edit_panel;
128         delete zoom_panel;
129         delete active;
130         delete inactive;
131         delete orig_mask_keyframe;
132 }
133
134 void CWindowGUI::create_objects()
135 {
136         lock_window("CWindowGUI::create_objects");
137         set_icon(mwindow->theme->get_image("cwindow_icon"));
138
139         active = new BC_Pixmap(this, mwindow->theme->get_image("cwindow_active"));
140         inactive = new BC_Pixmap(this, mwindow->theme->get_image("cwindow_inactive"));
141
142         mwindow->theme->get_cwindow_sizes(this, mwindow->session->cwindow_controls);
143         mwindow->theme->draw_cwindow_bg(this);
144         flash();
145
146 // Meters required by composite panel
147         meters = new CWindowMeters(mwindow,
148                 this,
149                 mwindow->theme->cmeter_x,
150                 mwindow->theme->cmeter_y,
151                 mwindow->theme->cmeter_h);
152         meters->create_objects();
153
154
155         composite_panel = new CPanel(mwindow,
156                 this,
157                 mwindow->theme->ccomposite_x,
158                 mwindow->theme->ccomposite_y,
159                 mwindow->theme->ccomposite_w,
160                 mwindow->theme->ccomposite_h);
161         composite_panel->create_objects();
162
163         canvas = new CWindowCanvas(mwindow, this);
164
165         canvas->create_objects(mwindow->edl);
166         canvas->use_cwindow();
167
168
169         add_subwindow(timebar = new CTimeBar(mwindow,
170                 this,
171                 mwindow->theme->ctimebar_x,
172                 mwindow->theme->ctimebar_y,
173                 mwindow->theme->ctimebar_w,
174                 mwindow->theme->ctimebar_h));
175         timebar->create_objects();
176
177 #ifdef USE_SLIDER
178         add_subwindow(slider = new CWindowSlider(mwindow,
179                 cwindow,
180                 mwindow->theme->cslider_x,
181                 mwindow->theme->cslider_y,
182                 mwindow->theme->cslider_w));
183 #endif
184
185         transport = new CWindowTransport(mwindow,
186                 this,
187                 mwindow->theme->ctransport_x,
188                 mwindow->theme->ctransport_y);
189         transport->create_objects();
190 #ifdef USE_SLIDER
191         transport->set_slider(slider);
192 #endif
193
194         edit_panel = new CWindowEditing(mwindow, cwindow);
195         edit_panel->set_meters(meters);
196         edit_panel->create_objects();
197
198 //      add_subwindow(clock = new MainClock(mwindow,
199 //              mwindow->theme->ctime_x,
200 //              mwindow->theme->ctime_y));
201
202         zoom_panel = new CWindowZoom(mwindow,
203                 this,
204                 mwindow->theme->czoom_x,
205                 mwindow->theme->czoom_y,
206                 mwindow->theme->czoom_w);
207         zoom_panel->create_objects();
208         zoom_panel->zoom_text->add_item(new BC_MenuItem(auto_zoom = _(AUTO_ZOOM)));
209         if( !mwindow->edl->session->cwindow_scrollbars )
210                 zoom_panel->set_text(auto_zoom);
211
212 //      destination = new CWindowDestination(mwindow,
213 //              this,
214 //              mwindow->theme->cdest_x,
215 //              mwindow->theme->cdest_y);
216 //      destination->create_objects();
217
218 // Must create after meter panel
219         tool_panel = new CWindowTool(mwindow, this);
220         tool_panel->Thread::start();
221
222
223         set_operation(mwindow->edl->session->cwindow_operation);
224
225
226
227         canvas->draw_refresh(0);
228
229
230         draw_status(0);
231         unlock_window();
232 }
233
234 int CWindowGUI::translation_event()
235 {
236         mwindow->session->cwindow_x = get_x();
237         mwindow->session->cwindow_y = get_y();
238         return 0;
239 }
240
241 int CWindowGUI::resize_event(int w, int h)
242 {
243         mwindow->session->cwindow_x = get_x();
244         mwindow->session->cwindow_y = get_y();
245         mwindow->session->cwindow_w = w;
246         mwindow->session->cwindow_h = h;
247
248         mwindow->theme->get_cwindow_sizes(this, mwindow->session->cwindow_controls);
249         mwindow->theme->draw_cwindow_bg(this);
250         flash(0);
251
252         composite_panel->reposition_buttons(mwindow->theme->ccomposite_x,
253                 mwindow->theme->ccomposite_y, mwindow->theme->ccomposite_h);
254
255         canvas->reposition_window(mwindow->edl,
256                 mwindow->theme->ccanvas_x,
257                 mwindow->theme->ccanvas_y,
258                 mwindow->theme->ccanvas_w,
259                 mwindow->theme->ccanvas_h);
260
261         timebar->resize_event();
262
263 #ifdef USE_SLIDER
264         slider->reposition_window(mwindow->theme->cslider_x,
265                 mwindow->theme->cslider_y,
266                 mwindow->theme->cslider_w);
267 // Recalibrate pointer motion range
268         slider->set_position();
269 #endif
270
271         transport->reposition_buttons(mwindow->theme->ctransport_x,
272                 mwindow->theme->ctransport_y);
273
274         edit_panel->reposition_buttons(mwindow->theme->cedit_x,
275                 mwindow->theme->cedit_y);
276
277 //      clock->reposition_window(mwindow->theme->ctime_x,
278 //              mwindow->theme->ctime_y);
279
280         zoom_panel->reposition_window(mwindow->theme->czoom_x,
281                 mwindow->theme->czoom_y);
282
283 //      destination->reposition_window(mwindow->theme->cdest_x,
284 //              mwindow->theme->cdest_y);
285
286         meters->reposition_window(mwindow->theme->cmeter_x,
287                 mwindow->theme->cmeter_y,
288                 -1,
289                 mwindow->theme->cmeter_h);
290
291         draw_status(0);
292
293         BC_WindowBase::resize_event(w, h);
294         return 1;
295 }
296
297 int CWindowGUI::button_press_event()
298 {
299         if(canvas->get_canvas())
300                 return canvas->button_press_event_base(canvas->get_canvas());
301         return 0;
302 }
303
304 int CWindowGUI::cursor_leave_event()
305 {
306         if(canvas->get_canvas())
307                 return canvas->cursor_leave_event_base(canvas->get_canvas());
308         return 0;
309 }
310
311 int CWindowGUI::cursor_enter_event()
312 {
313         if(canvas->get_canvas())
314                 return canvas->cursor_enter_event_base(canvas->get_canvas());
315         return 0;
316 }
317
318 int CWindowGUI::button_release_event()
319 {
320         if(canvas->get_canvas())
321                 return canvas->button_release_event();
322         return 0;
323 }
324
325 int CWindowGUI::cursor_motion_event()
326 {
327         if(canvas->get_canvas())
328         {
329                 canvas->get_canvas()->unhide_cursor();
330                 return canvas->cursor_motion_event();
331         }
332         return 0;
333 }
334
335
336
337
338
339
340
341 void CWindowGUI::draw_status(int flush)
342 {
343         if( (canvas->get_canvas() && canvas->get_canvas()->get_video_on()) ||
344                 canvas->is_processing )
345         {
346                 draw_pixmap(active,
347                         mwindow->theme->cstatus_x,
348                         mwindow->theme->cstatus_y);
349         }
350         else
351         {
352                 draw_pixmap(inactive,
353                         mwindow->theme->cstatus_x,
354                         mwindow->theme->cstatus_y);
355         }
356
357
358 // printf("CWindowGUI::draw_status %d %d %d\n", __LINE__, mwindow->theme->cstatus_x,
359 //              mwindow->theme->cstatus_y);
360         flash(mwindow->theme->cstatus_x,
361                 mwindow->theme->cstatus_y,
362                 active->get_w(),
363                 active->get_h(),
364                 flush);
365 }
366
367 float CWindowGUI::get_auto_zoom()
368 {
369         float conformed_w, conformed_h;
370         mwindow->edl->calculate_conformed_dimensions(0, conformed_w, conformed_h);
371         float zoom_x = canvas->w / conformed_w;
372         float zoom_y = canvas->h / conformed_h;
373         return zoom_x < zoom_y ? zoom_x : zoom_y;
374 }
375
376 void CWindowGUI::zoom_canvas(double value, int update_menu)
377 {
378         float x = 0, y = 0;
379         float zoom = !value ? get_auto_zoom() : value;
380         mwindow->edl->session->cwindow_scrollbars = !value ? 0 : 1;
381         if( value ) {
382                 float cx = canvas->get_xscroll() + 0.5f*canvas->w_visible;
383                 float cy = canvas->get_yscroll() + 0.5f*canvas->h_visible;
384                 float output_x = cx, output_y = cy;
385                 canvas->output_to_canvas(mwindow->edl, 0, cx, cy);
386                 x = output_x - cx / zoom;
387                 y = output_y - cy / zoom;
388         }
389         canvas->update_zoom((int)(x+0.5), (int)(y+0.5), zoom);
390
391         if( update_menu )
392                 zoom_panel->update(value);
393         if( mwindow->edl->session->cwindow_operation == CWINDOW_ZOOM )
394                 composite_panel->cpanel_zoom->update(zoom);
395
396         canvas->reposition_window(mwindow->edl,
397                 mwindow->theme->ccanvas_x, mwindow->theme->ccanvas_y,
398                 mwindow->theme->ccanvas_w, mwindow->theme->ccanvas_h);
399         canvas->draw_refresh();
400 }
401
402
403
404 void CWindowGUI::set_operation(int value)
405 {
406         mwindow->edl->session->cwindow_operation = value;
407
408         composite_panel->set_operation(value);
409         edit_panel->update();
410
411         tool_panel->start_tool(value);
412         canvas->draw_refresh();
413 }
414
415 void CWindowGUI::update_tool()
416 {
417         tool_panel->update_values();
418 }
419
420 int CWindowGUI::close_event()
421 {
422         cwindow->hide_window();
423         return 1;
424 }
425
426
427 int CWindowGUI::keypress_event()
428 {
429         int result = 0;
430
431         switch(get_keypress())
432         {
433                 case 'w':
434                 case 'W':
435                         close_event();
436                         result = 1;
437                         break;
438                 case '+':
439                 case '=':
440                         keyboard_zoomin();
441                         result = 1;
442                         break;
443                 case '-':
444                         keyboard_zoomout();
445                         result = 1;
446                         break;
447                 case 'f':
448                         unlock_window();
449                         if(mwindow->session->cwindow_fullscreen)
450                                 canvas->stop_fullscreen();
451                         else
452                                 canvas->start_fullscreen();
453                         lock_window("CWindowGUI::keypress_event 1");
454                         break;
455                 case 'x':
456                         unlock_window();
457                         mwindow->gui->lock_window("CWindowGUI::keypress_event 2");
458                         mwindow->cut();
459                         mwindow->gui->unlock_window();
460                         lock_window("CWindowGUI::keypress_event 2");
461                         break;
462                 case DELETE:
463                         unlock_window();
464                         mwindow->gui->lock_window("CWindowGUI::keypress_event 2");
465                         mwindow->clear_entry();
466                         mwindow->gui->unlock_window();
467                         lock_window("CWindowGUI::keypress_event 3");
468                         break;
469                 case ESC:
470                         unlock_window();
471                         if(mwindow->session->cwindow_fullscreen)
472                                 canvas->stop_fullscreen();
473                         lock_window("CWindowGUI::keypress_event 4");
474                         break;
475                 case LEFT:
476                         if( !ctrl_down() ) {
477                                 int alt_down = this->alt_down();
478                                 int shift_down = this->shift_down();
479                                 unlock_window();
480                                 stop_transport(0);
481                                 mwindow->gui->lock_window("CWindowGUI::keypress_event 2");
482                                 if( alt_down )
483                                         mwindow->prev_edit_handle(shift_down);
484                                 else
485                                         mwindow->move_left();
486                                 mwindow->gui->unlock_window();
487                                 lock_window("CWindowGUI::keypress_event 2");
488                                 result = 1;
489                         }
490                         break;
491                 case RIGHT:
492                         if( !ctrl_down() ) {
493                                 int alt_down = this->alt_down();
494                                 int shift_down = this->shift_down();
495                                 unlock_window();
496                                 stop_transport(0);
497                                 mwindow->gui->lock_window("CWindowGUI::keypress_event 2");
498                                 if( alt_down )
499                                         mwindow->next_edit_handle(shift_down);
500                                 else
501                                         mwindow->move_right();
502                                 mwindow->gui->unlock_window();
503                                 lock_window("CWindowGUI::keypress_event 2");
504                                 result = 1;
505                         }
506                         break;
507         }
508
509         if(!result) result = transport->keypress_event();
510
511         return result;
512 }
513
514
515 void CWindowGUI::reset_affected()
516 {
517         affected_x = 0;
518         affected_y = 0;
519         affected_z = 0;
520 }
521
522 void CWindowGUI::keyboard_zoomin()
523 {
524 //      if(mwindow->edl->session->cwindow_scrollbars)
525 //      {
526                 zoom_panel->zoom_tumbler->handle_up_event();
527 //      }
528 //      else
529 //      {
530 //      }
531 }
532
533 void CWindowGUI::keyboard_zoomout()
534 {
535 //      if(mwindow->edl->session->cwindow_scrollbars)
536 //      {
537                 zoom_panel->zoom_tumbler->handle_down_event();
538 //      }
539 //      else
540 //      {
541 //      }
542 }
543
544
545 void CWindowGUI::drag_motion()
546 {
547         if(get_hidden()) return;
548
549         if(mwindow->session->current_operation != DRAG_ASSET &&
550                 mwindow->session->current_operation != DRAG_VTRANSITION &&
551                 mwindow->session->current_operation != DRAG_VEFFECT) return;
552         int need_highlight = cursor_above() && get_cursor_over_window();
553         if( highlighted == need_highlight ) return;
554         highlighted = need_highlight;
555         canvas->draw_refresh();
556 }
557
558 int CWindowGUI::drag_stop()
559 {
560         int result = 0;
561         if(get_hidden()) return 0;
562         if( !highlighted ) return 0;
563         if( mwindow->session->current_operation != DRAG_ASSET &&
564             mwindow->session->current_operation != DRAG_VTRANSITION &&
565             mwindow->session->current_operation != DRAG_VEFFECT) return 0;
566         highlighted = 0;
567         canvas->draw_refresh();
568         result = 1;
569
570         if(mwindow->session->current_operation == DRAG_ASSET)
571         {
572                 if(mwindow->session->drag_assets->total ||
573                         mwindow->session->drag_clips->total)
574                 {
575                         mwindow->gui->lock_window("CWindowGUI::drag_stop 5");
576                         mwindow->undo->update_undo_before(_("insert assets"), 0);
577                         mwindow->gui->unlock_window();
578                 }
579
580                 if(mwindow->session->drag_assets->total)
581                 {
582                         mwindow->gui->lock_window("CWindowGUI::drag_stop 1");
583                         mwindow->clear(0);
584                         mwindow->load_assets(mwindow->session->drag_assets,
585                                 mwindow->edl->local_session->get_selectionstart(),
586                                 LOADMODE_PASTE,
587                                 mwindow->session->track_highlighted,
588                                 0,
589                                 mwindow->edl->session->labels_follow_edits,
590                                 mwindow->edl->session->plugins_follow_edits,
591                                 mwindow->edl->session->autos_follow_edits,
592                                 0); // overwrite
593                 }
594
595                 if(mwindow->session->drag_clips->total)
596                 {
597                         mwindow->gui->lock_window("CWindowGUI::drag_stop 2");
598                         mwindow->clear(0);
599                         mwindow->paste_edls(mwindow->session->drag_clips,
600                                 LOADMODE_PASTE,
601                                 mwindow->session->track_highlighted,
602                                 mwindow->edl->local_session->get_selectionstart(),
603                                 mwindow->edl->session->labels_follow_edits,
604                                 mwindow->edl->session->plugins_follow_edits,
605                                 mwindow->edl->session->autos_follow_edits,
606                                 0); // overwrite
607                 }
608
609                 if(mwindow->session->drag_assets->total ||
610                         mwindow->session->drag_clips->total)
611                 {
612                         mwindow->save_backup();
613                         mwindow->restart_brender();
614                         mwindow->gui->update(1, 1, 1, 1, 0, 1, 0);
615                         mwindow->undo->update_undo_after(_("insert assets"), LOAD_ALL);
616                         mwindow->gui->unlock_window();
617                         mwindow->sync_parameters(LOAD_ALL);
618                 }
619         }
620
621         if(mwindow->session->current_operation == DRAG_VEFFECT)
622         {
623 //printf("CWindowGUI::drag_stop 1\n");
624                 Track *affected_track = cwindow->calculate_affected_track();
625 //printf("CWindowGUI::drag_stop 2\n");
626
627                 mwindow->gui->lock_window("CWindowGUI::drag_stop 3");
628                 mwindow->insert_effects_cwindow(affected_track);
629                 mwindow->session->current_operation = NO_OPERATION;
630                 mwindow->gui->unlock_window();
631         }
632
633         if(mwindow->session->current_operation == DRAG_VTRANSITION)
634         {
635                 Track *affected_track = cwindow->calculate_affected_track();
636                 mwindow->gui->lock_window("CWindowGUI::drag_stop 4");
637                 mwindow->paste_transition_cwindow(affected_track);
638                 mwindow->session->current_operation = NO_OPERATION;
639                 mwindow->gui->unlock_window();
640         }
641
642         return result;
643 }
644
645 void CWindowGUI::update_meters()
646 {
647         if(mwindow->edl->session->cwindow_meter != meters->visible)
648         {
649                 meters->set_meters(meters->meter_count, mwindow->edl->session->cwindow_meter);
650                 mwindow->theme->get_cwindow_sizes(this, mwindow->session->cwindow_controls);
651                 resize_event(get_w(), get_h());
652         }
653 }
654
655 void CWindowGUI::stop_transport(const char *lock_msg)
656 {
657         if( lock_msg ) unlock_window();
658         mwindow->stop_transport();
659         if( lock_msg ) lock_window(lock_msg);
660 }
661
662
663 CWindowEditing::CWindowEditing(MWindow *mwindow, CWindow *cwindow)
664  : EditPanel(mwindow,
665                 cwindow->gui,
666                 mwindow->theme->cedit_x,
667                 mwindow->theme->cedit_y,
668                 mwindow->edl->session->editing_mode,
669                 0,
670                 1,
671                 0,
672                 0,
673                 1,
674                 1,
675                 1,
676                 1,
677                 1,
678                 0,
679                 0, // locklabels
680                 1,
681                 1,
682                 1,
683                 0,
684                 1,
685                 0)
686 {
687         this->mwindow = mwindow;
688         this->cwindow = cwindow;
689 }
690
691 #define CWrapper(fn) void CWindowEditing::fn() { \
692         mwindow->gui->lock_window("CWrapper::" #fn); \
693         EditPanel::fn(); \
694         mwindow->gui->unlock_window(); \
695 }
696
697 CWrapper(copy_selection)
698 CWrapper(splice_selection)
699 CWrapper(overwrite_selection)
700 CWrapper(set_inpoint)
701 CWrapper(set_outpoint)
702 CWrapper(unset_inoutpoint)
703 CWrapper(toggle_label)
704 CWrapper(prev_label)
705 CWrapper(next_label)
706 CWrapper(prev_edit)
707 CWrapper(next_edit)
708
709 void CWindowEditing::to_clip()
710 {
711         mwindow->to_clip(mwindow->edl, _("composer window: "));
712 }
713
714
715 CWindowMeters::CWindowMeters(MWindow *mwindow,
716         CWindowGUI *gui,
717         int x,
718         int y,
719         int h)
720  : MeterPanel(mwindow,
721                 gui,
722                 x,
723                 y,
724                 -1,
725                 h,
726                 mwindow->edl->session->audio_channels,
727                 mwindow->edl->session->cwindow_meter,
728                 0,
729                 0)
730 {
731         this->mwindow = mwindow;
732         this->gui = gui;
733 }
734
735 CWindowMeters::~CWindowMeters()
736 {
737 }
738
739 int CWindowMeters::change_status_event(int new_status)
740 {
741         mwindow->edl->session->cwindow_meter = new_status;
742         gui->update_meters();
743         return 1;
744 }
745
746
747
748
749 CWindowZoom::CWindowZoom(MWindow *mwindow, CWindowGUI *gui, int x, int y, int w)
750  : ZoomPanel(mwindow, gui, (double)mwindow->edl->session->cwindow_zoom,
751         x, y, w, my_zoom_table, total_zooms, ZOOM_PERCENTAGE)
752 {
753         this->mwindow = mwindow;
754         this->gui = gui;
755 }
756
757 CWindowZoom::~CWindowZoom()
758 {
759 }
760
761 void CWindowZoom::update(double value)
762 {
763         char string[BCSTRLEN];
764         const char *cp = string;
765         if( value ) {
766                 this->value = value;
767                 int frac = value >= 1.0f ? 1 :
768                            value >= 0.1f ? 2 :
769                            value >= .01f ? 3 : 4;
770                 sprintf(string, "x %.*f", frac, value);
771         }
772         else
773                 cp = gui->auto_zoom;
774         ZoomPanel::update(cp);
775 }
776
777 int CWindowZoom::handle_event()
778 {
779         double value = !strcasecmp(gui->auto_zoom, get_text()) ? 0 : get_value();
780         gui->zoom_canvas(value, 0);
781         return 1;
782 }
783
784
785
786 #ifdef USE_SLIDER
787 CWindowSlider::CWindowSlider(MWindow *mwindow, CWindow *cwindow, int x, int y, int pixels)
788  : BC_PercentageSlider(x,
789                         y,
790                         0,
791                         pixels,
792                         pixels,
793                         0,
794                         1,
795                         0)
796 {
797         this->mwindow = mwindow;
798         this->cwindow = cwindow;
799         set_precision(0.00001);
800 }
801
802 CWindowSlider::~CWindowSlider()
803 {
804 }
805
806 int CWindowSlider::handle_event()
807 {
808         cwindow->update_position((double)get_value());
809         return 1;
810 }
811
812 void CWindowSlider::set_position()
813 {
814         double new_length = mwindow->edl->tracks->total_length();
815 //      if(mwindow->edl->local_session->preview_end <= 0 ||
816 //              mwindow->edl->local_session->preview_end > new_length)
817 //              mwindow->edl->local_session->preview_end = new_length;
818 //      if(mwindow->edl->local_session->preview_start >
819 //              mwindow->edl->local_session->preview_end)
820 //              mwindow->edl->local_session->preview_start = 0;
821
822
823         update(mwindow->theme->cslider_w,
824                 mwindow->edl->local_session->get_selectionstart(1),
825                 0,
826                 new_length);
827 //              mwindow->edl->local_session->preview_start,
828 //              mwindow->edl->local_session->preview_end);
829 }
830
831
832 int CWindowSlider::increase_value()
833 {
834         unlock_window();
835         cwindow->gui->transport->handle_transport(SINGLE_FRAME_FWD);
836         lock_window("CWindowSlider::increase_value");
837         return 1;
838 }
839
840 int CWindowSlider::decrease_value()
841 {
842         unlock_window();
843         cwindow->gui->transport->handle_transport(SINGLE_FRAME_REWIND);
844         lock_window("CWindowSlider::decrease_value");
845         return 1;
846 }
847
848
849 // CWindowDestination::CWindowDestination(MWindow *mwindow, CWindowGUI *cwindow, int x, int y)
850 //  : BC_PopupTextBox(cwindow,
851 //      &cwindow->destinations,
852 //      cwindow->destinations.values[cwindow->cwindow->destination]->get_text(),
853 //      x,
854 //      y,
855 //      70,
856 //      200)
857 // {
858 //      this->mwindow = mwindow;
859 //      this->cwindow = cwindow;
860 // }
861 //
862 // CWindowDestination::~CWindowDestination()
863 // {
864 // }
865 //
866 // int CWindowDestination::handle_event()
867 // {
868 //      return 1;
869 // }
870 #endif // USE_SLIDER
871
872
873
874
875
876
877 CWindowTransport::CWindowTransport(MWindow *mwindow,
878         CWindowGUI *gui,
879         int x,
880         int y)
881  : PlayTransport(mwindow,
882         gui,
883         x,
884         y)
885 {
886         this->gui = gui;
887 }
888
889 EDL* CWindowTransport::get_edl()
890 {
891         return mwindow->edl;
892 }
893
894 void CWindowTransport::goto_start()
895 {
896         gui->unlock_window();
897         handle_transport(REWIND, 1);
898
899         mwindow->gui->lock_window("CWindowTransport::goto_start 1");
900         mwindow->goto_start();
901         mwindow->gui->unlock_window();
902
903         gui->lock_window("CWindowTransport::goto_start 2");
904 }
905
906 void CWindowTransport::goto_end()
907 {
908         gui->unlock_window();
909         handle_transport(GOTO_END, 1);
910
911         mwindow->gui->lock_window("CWindowTransport::goto_end 1");
912         mwindow->goto_end();
913         mwindow->gui->unlock_window();
914
915         gui->lock_window("CWindowTransport::goto_end 2");
916 }
917
918
919
920 CWindowCanvas::CWindowCanvas(MWindow *mwindow, CWindowGUI *gui)
921  : Canvas(mwindow,
922         gui,
923         mwindow->theme->ccanvas_x,
924         mwindow->theme->ccanvas_y,
925         mwindow->theme->ccanvas_w,
926         mwindow->theme->ccanvas_h,
927         0,
928         0,
929         mwindow->edl->session->cwindow_scrollbars)
930 {
931         this->mwindow = mwindow;
932         this->gui = gui;
933 }
934
935 void CWindowCanvas::status_event()
936 {
937         gui->draw_status(1);
938 }
939
940 int CWindowCanvas::get_fullscreen()
941 {
942         return mwindow->session->cwindow_fullscreen;
943 }
944
945 void CWindowCanvas::set_fullscreen(int value)
946 {
947         mwindow->session->cwindow_fullscreen = value;
948 }
949
950
951 void CWindowCanvas::update_zoom(int x, int y, float zoom)
952 {
953         use_scrollbars = mwindow->edl->session->cwindow_scrollbars;
954
955         mwindow->edl->session->cwindow_xscroll = x;
956         mwindow->edl->session->cwindow_yscroll = y;
957         mwindow->edl->session->cwindow_zoom = zoom;
958 }
959
960 void CWindowCanvas::zoom_auto()
961 {
962         gui->zoom_canvas(0, 1);
963 }
964
965 int CWindowCanvas::get_xscroll()
966 {
967         return mwindow->edl->session->cwindow_xscroll;
968 }
969
970 int CWindowCanvas::get_yscroll()
971 {
972         return mwindow->edl->session->cwindow_yscroll;
973 }
974
975
976 float CWindowCanvas::get_zoom()
977 {
978         return mwindow->edl->session->cwindow_zoom;
979 }
980
981 void CWindowCanvas::draw_refresh(int flush)
982 {
983         if(get_canvas() && !get_canvas()->get_video_on())
984         {
985
986                 if(refresh_frame && refresh_frame->get_w()>0 && refresh_frame->get_h()>0)
987                 {
988                         float in_x1, in_y1, in_x2, in_y2;
989                         float out_x1, out_y1, out_x2, out_y2;
990                         get_transfers(mwindow->edl,
991                                 in_x1,
992                                 in_y1,
993                                 in_x2,
994                                 in_y2,
995                                 out_x1,
996                                 out_y1,
997                                 out_x2,
998                                 out_y2);
999
1000                         if(!EQUIV(out_x1, 0) ||
1001                                 !EQUIV(out_y1, 0) ||
1002                                 !EQUIV(out_x2, get_canvas()->get_w()) ||
1003                                 !EQUIV(out_y2, get_canvas()->get_h()))
1004                         {
1005                                 get_canvas()->clear_box(0,
1006                                         0,
1007                                         get_canvas()->get_w(),
1008                                         get_canvas()->get_h());
1009                         }
1010
1011 //printf("CWindowCanvas::draw_refresh %.2f %.2f %.2f %.2f -> %.2f %.2f %.2f %.2f\n",
1012 //in_x1, in_y1, in_x2, in_y2, out_x1, out_y1, out_x2, out_y2);
1013
1014
1015                         if(out_x2 > out_x1 &&
1016                                 out_y2 > out_y1 &&
1017                                 in_x2 > in_x1 &&
1018                                 in_y2 > in_y1)
1019                         {
1020 // Can't use OpenGL here because it is called asynchronously of the
1021 // playback operation.
1022                                 get_canvas()->draw_vframe(refresh_frame,
1023                                                 (int)out_x1,
1024                                                 (int)out_y1,
1025                                                 (int)(out_x2 - out_x1),
1026                                                 (int)(out_y2 - out_y1),
1027                                                 (int)in_x1,
1028                                                 (int)in_y1,
1029                                                 (int)(in_x2 - in_x1),
1030                                                 (int)(in_y2 - in_y1),
1031                                                 0);
1032                         }
1033                 }
1034                 else
1035                 {
1036                         get_canvas()->clear_box(0,
1037                                 0,
1038                                 get_canvas()->get_w(),
1039                                 get_canvas()->get_h());
1040                 }
1041
1042                 draw_overlays();
1043                 get_canvas()->flash(flush);
1044         }
1045 //printf("CWindowCanvas::draw_refresh 10\n");
1046 }
1047
1048 #define CROPHANDLE_W 10
1049 #define CROPHANDLE_H 10
1050
1051 void CWindowCanvas::draw_crophandle(int x, int y)
1052 {
1053         get_canvas()->draw_box(x, y, CROPHANDLE_W, CROPHANDLE_H);
1054 }
1055
1056
1057
1058
1059
1060
1061 #define CONTROL_W 10
1062 #define CONTROL_H 10
1063 #define FIRST_CONTROL_W 20
1064 #define FIRST_CONTROL_H 20
1065 #undef BC_INFINITY
1066 #define BC_INFINITY 65536
1067
1068 #define RULERHANDLE_W 16
1069 #define RULERHANDLE_H 16
1070
1071
1072
1073 int CWindowCanvas::do_ruler(int draw,
1074         int motion,
1075         int button_press,
1076         int button_release)
1077 {
1078         int result = 0;
1079         float x1 = mwindow->edl->session->ruler_x1;
1080         float y1 = mwindow->edl->session->ruler_y1;
1081         float x2 = mwindow->edl->session->ruler_x2;
1082         float y2 = mwindow->edl->session->ruler_y2;
1083         float canvas_x1 = x1;
1084         float canvas_y1 = y1;
1085         float canvas_x2 = x2;
1086         float canvas_y2 = y2;
1087         float output_x = get_cursor_x();
1088         float output_y = get_cursor_y();
1089         float canvas_cursor_x = output_x;
1090         float canvas_cursor_y = output_y;
1091
1092         canvas_to_output(mwindow->edl, 0, output_x, output_y);
1093         output_to_canvas(mwindow->edl, 0, canvas_x1, canvas_y1);
1094         output_to_canvas(mwindow->edl, 0, canvas_x2, canvas_y2);
1095         mwindow->session->cwindow_output_x = roundf(output_x);
1096         mwindow->session->cwindow_output_y = roundf(output_y);
1097
1098         if(button_press && get_buttonpress() == 1)
1099         {
1100                 gui->ruler_handle = -1;
1101                 gui->ruler_translate = 0;
1102                 if(gui->alt_down())
1103                 {
1104                         gui->ruler_translate = 1;
1105                         gui->ruler_origin_x = x1;
1106                         gui->ruler_origin_y = y1;
1107                 }
1108                 else
1109                 if(canvas_cursor_x >= canvas_x1 - RULERHANDLE_W / 2 &&
1110                         canvas_cursor_x < canvas_x1 + RULERHANDLE_W / 2 &&
1111                         canvas_cursor_y >= canvas_y1 - RULERHANDLE_W &&
1112                         canvas_cursor_y < canvas_y1 + RULERHANDLE_H / 2)
1113                 {
1114                         gui->ruler_handle = 0;
1115                         gui->ruler_origin_x = x1;
1116                         gui->ruler_origin_y = y1;
1117                 }
1118                 else
1119                 if(canvas_cursor_x >= canvas_x2 - RULERHANDLE_W / 2 &&
1120                         canvas_cursor_x < canvas_x2 + RULERHANDLE_W / 2 &&
1121                         canvas_cursor_y >= canvas_y2 - RULERHANDLE_W &&
1122                         canvas_cursor_y < canvas_y2 + RULERHANDLE_H / 2)
1123                 {
1124                         gui->ruler_handle = 1;
1125                         gui->ruler_origin_x = x2;
1126                         gui->ruler_origin_y = y2;
1127                 }
1128
1129
1130 // Start new selection
1131                 if(!gui->ruler_translate &&
1132                         (gui->ruler_handle < 0 ||
1133                         (EQUIV(x2, x1) &&
1134                         EQUIV(y2, y1))))
1135                 {
1136 // Hide previous
1137                         do_ruler(1, 0, 0, 0);
1138                         get_canvas()->flash();
1139                         gui->ruler_handle = 1;
1140                         mwindow->edl->session->ruler_x1 = output_x;
1141                         mwindow->edl->session->ruler_y1 = output_y;
1142                         mwindow->edl->session->ruler_x2 = output_x;
1143                         mwindow->edl->session->ruler_y2 = output_y;
1144                         gui->ruler_origin_x = mwindow->edl->session->ruler_x2;
1145                         gui->ruler_origin_y = mwindow->edl->session->ruler_y2;
1146                 }
1147
1148                 gui->x_origin = output_x;
1149                 gui->y_origin = output_y;
1150                 gui->current_operation = CWINDOW_RULER;
1151                 gui->tool_panel->raise_window();
1152                 result = 1;
1153         }
1154
1155         if(motion)
1156         {
1157                 if(gui->current_operation == CWINDOW_RULER)
1158                 {
1159                         if(gui->ruler_translate)
1160                         {
1161 // Hide ruler
1162                                 do_ruler(1, 0, 0, 0);
1163                                 float x_difference = mwindow->edl->session->ruler_x1;
1164                                 float y_difference = mwindow->edl->session->ruler_y1;
1165                                 mwindow->edl->session->ruler_x1 = output_x - gui->x_origin + gui->ruler_origin_x;
1166                                 mwindow->edl->session->ruler_y1 = output_y - gui->y_origin + gui->ruler_origin_y;
1167                                 x_difference -= mwindow->edl->session->ruler_x1;
1168                                 y_difference -= mwindow->edl->session->ruler_y1;
1169                                 mwindow->edl->session->ruler_x2 -= x_difference;
1170                                 mwindow->edl->session->ruler_y2 -= y_difference;
1171 // Show ruler
1172                                 do_ruler(1, 0, 0, 0);
1173                                 get_canvas()->flash();
1174                                 gui->update_tool();
1175                         }
1176                         else
1177                         switch(gui->ruler_handle)
1178                         {
1179                                 case 0:
1180                                         do_ruler(1, 0, 0, 0);
1181                                         mwindow->edl->session->ruler_x1 = output_x - gui->x_origin + gui->ruler_origin_x;
1182                                         mwindow->edl->session->ruler_y1 = output_y - gui->y_origin + gui->ruler_origin_y;
1183                                         if(gui->alt_down() || gui->ctrl_down())
1184                                         {
1185                                                 double angle_value = fabs(atan((mwindow->edl->session->ruler_y2 - mwindow->edl->session->ruler_y1) /
1186                                                         (mwindow->edl->session->ruler_x2 - mwindow->edl->session->ruler_x1)) *
1187                                                         360 /
1188                                                         2 /
1189                                                         M_PI);
1190                                                 double distance_value =
1191                                                         sqrt(SQR(mwindow->edl->session->ruler_x2 - mwindow->edl->session->ruler_x1) +
1192                                                         SQR(mwindow->edl->session->ruler_y2 - mwindow->edl->session->ruler_y1));
1193                                                 if(angle_value < 22)
1194                                                         mwindow->edl->session->ruler_y1 = mwindow->edl->session->ruler_y2;
1195                                                 else
1196                                                 if(angle_value > 67)
1197                                                         mwindow->edl->session->ruler_x1 = mwindow->edl->session->ruler_x2;
1198                                                 else
1199                                                 if(mwindow->edl->session->ruler_x1 < mwindow->edl->session->ruler_x2 &&
1200                                                         mwindow->edl->session->ruler_y1 < mwindow->edl->session->ruler_y2)
1201                                                 {
1202                                                         mwindow->edl->session->ruler_x1 = mwindow->edl->session->ruler_x2 - distance_value / 1.414214;
1203                                                         mwindow->edl->session->ruler_y1 = mwindow->edl->session->ruler_y2 - distance_value / 1.414214;
1204                                                 }
1205                                                 else
1206                                                 if(mwindow->edl->session->ruler_x1 < mwindow->edl->session->ruler_x2 && mwindow->edl->session->ruler_y1 > mwindow->edl->session->ruler_y2)
1207                                                 {
1208                                                         mwindow->edl->session->ruler_x1 = mwindow->edl->session->ruler_x2 - distance_value / 1.414214;
1209                                                         mwindow->edl->session->ruler_y1 = mwindow->edl->session->ruler_y2 + distance_value / 1.414214;
1210                                                 }
1211                                                 else
1212                                                 if(mwindow->edl->session->ruler_x1 > mwindow->edl->session->ruler_x2 &&
1213                                                         mwindow->edl->session->ruler_y1 < mwindow->edl->session->ruler_y2)
1214                                                 {
1215                                                         mwindow->edl->session->ruler_x1 = mwindow->edl->session->ruler_x2 + distance_value / 1.414214;
1216                                                         mwindow->edl->session->ruler_y1 = mwindow->edl->session->ruler_y2 - distance_value / 1.414214;
1217                                                 }
1218                                                 else
1219                                                 {
1220                                                         mwindow->edl->session->ruler_x1 = mwindow->edl->session->ruler_x2 + distance_value / 1.414214;
1221                                                         mwindow->edl->session->ruler_y1 = mwindow->edl->session->ruler_y2 + distance_value / 1.414214;
1222                                                 }
1223                                         }
1224                                         do_ruler(1, 0, 0, 0);
1225                                         get_canvas()->flash();
1226                                         gui->update_tool();
1227                                         break;
1228
1229                                 case 1:
1230                                         do_ruler(1, 0, 0, 0);
1231                                         mwindow->edl->session->ruler_x2 = output_x - gui->x_origin + gui->ruler_origin_x;
1232                                         mwindow->edl->session->ruler_y2 = output_y - gui->y_origin + gui->ruler_origin_y;
1233                                         if(gui->alt_down() || gui->ctrl_down())
1234                                         {
1235                                                 double angle_value = fabs(atan((mwindow->edl->session->ruler_y2 - mwindow->edl->session->ruler_y1) /
1236                                                         (mwindow->edl->session->ruler_x2 - mwindow->edl->session->ruler_x1)) *
1237                                                         360 /
1238                                                         2 /
1239                                                         M_PI);
1240                                                 double distance_value =
1241                                                         sqrt(SQR(mwindow->edl->session->ruler_x2 - mwindow->edl->session->ruler_x1) +
1242                                                         SQR(mwindow->edl->session->ruler_y2 - mwindow->edl->session->ruler_y1));
1243                                                 if(angle_value < 22)
1244                                                         mwindow->edl->session->ruler_y2 = mwindow->edl->session->ruler_y1;
1245                                                 else
1246                                                 if(angle_value > 67)
1247                                                         mwindow->edl->session->ruler_x2 = mwindow->edl->session->ruler_x1;
1248                                                 else
1249                                                 if(mwindow->edl->session->ruler_x2 < mwindow->edl->session->ruler_x1 &&
1250                                                         mwindow->edl->session->ruler_y2 < mwindow->edl->session->ruler_y1)
1251                                                 {
1252                                                         mwindow->edl->session->ruler_x2 = mwindow->edl->session->ruler_x1 - distance_value / 1.414214;
1253                                                         mwindow->edl->session->ruler_y2 = mwindow->edl->session->ruler_y1 - distance_value / 1.414214;
1254                                                 }
1255                                                 else
1256                                                 if(mwindow->edl->session->ruler_x2 < mwindow->edl->session->ruler_x1 &&
1257                                                         mwindow->edl->session->ruler_y2 > mwindow->edl->session->ruler_y1)
1258                                                 {
1259                                                         mwindow->edl->session->ruler_x2 = mwindow->edl->session->ruler_x1 - distance_value / 1.414214;
1260                                                         mwindow->edl->session->ruler_y2 = mwindow->edl->session->ruler_y1 + distance_value / 1.414214;
1261                                                 }
1262                                                 else
1263                                                 if(mwindow->edl->session->ruler_x2 > mwindow->edl->session->ruler_x1 && mwindow->edl->session->ruler_y2 < mwindow->edl->session->ruler_y1)
1264                                                 {
1265                                                         mwindow->edl->session->ruler_x2 = mwindow->edl->session->ruler_x1 + distance_value / 1.414214;
1266                                                         mwindow->edl->session->ruler_y2 = mwindow->edl->session->ruler_y1 - distance_value / 1.414214;
1267                                                 }
1268                                                 else
1269                                                 {
1270                                                         mwindow->edl->session->ruler_x2 = mwindow->edl->session->ruler_x1 + distance_value / 1.414214;
1271                                                         mwindow->edl->session->ruler_y2 = mwindow->edl->session->ruler_y1 + distance_value / 1.414214;
1272                                                 }
1273                                         }
1274                                         do_ruler(1, 0, 0, 0);
1275                                         get_canvas()->flash();
1276                                         gui->update_tool();
1277                                         break;
1278                         }
1279 //printf("CWindowCanvas::do_ruler 2 %f %f %f %f\n", gui->ruler_x1, gui->ruler_y1, gui->ruler_x2, gui->ruler_y2);
1280                 }
1281                 else
1282                 {
1283 // printf("CWindowCanvas::do_ruler 2 %f %f %f %f\n",
1284 // canvas_cursor_x,
1285 // canvas_cursor_y,
1286 // canvas_x1,
1287 // canvas_y1);
1288                         if(canvas_cursor_x >= canvas_x1 - RULERHANDLE_W / 2 &&
1289                                 canvas_cursor_x < canvas_x1 + RULERHANDLE_W / 2 &&
1290                                 canvas_cursor_y >= canvas_y1 - RULERHANDLE_W &&
1291                                 canvas_cursor_y < canvas_y1 + RULERHANDLE_H / 2)
1292                         {
1293                                 set_cursor(UPRIGHT_ARROW_CURSOR);
1294                         }
1295                         else
1296                         if(canvas_cursor_x >= canvas_x2 - RULERHANDLE_W / 2 &&
1297                                 canvas_cursor_x < canvas_x2 + RULERHANDLE_W / 2 &&
1298                                 canvas_cursor_y >= canvas_y2 - RULERHANDLE_W &&
1299                                 canvas_cursor_y < canvas_y2 + RULERHANDLE_H / 2)
1300                         {
1301                                 set_cursor(UPRIGHT_ARROW_CURSOR);
1302                         }
1303                         else
1304                                 set_cursor(CROSS_CURSOR);
1305
1306 // Update current position
1307                         gui->update_tool();
1308                 }
1309         }
1310
1311 // Assume no ruler measurement if 0 length
1312         if(draw && (!EQUIV(x2, x1) || !EQUIV(y2, y1)))
1313         {
1314                 get_canvas()->set_inverse();
1315                 get_canvas()->set_color(WHITE);
1316                 get_canvas()->draw_line((int)canvas_x1,
1317                         (int)canvas_y1,
1318                         (int)canvas_x2,
1319                         (int)canvas_y2);
1320                 get_canvas()->draw_line(roundf(canvas_x1) - RULERHANDLE_W / 2,
1321                         roundf(canvas_y1),
1322                         roundf(canvas_x1) + RULERHANDLE_W / 2,
1323                         roundf(canvas_y1));
1324                 get_canvas()->draw_line(roundf(canvas_x1),
1325                         roundf(canvas_y1) - RULERHANDLE_H / 2,
1326                         roundf(canvas_x1),
1327                         roundf(canvas_y1) + RULERHANDLE_H / 2);
1328                 get_canvas()->draw_line(roundf(canvas_x2) - RULERHANDLE_W / 2,
1329                         roundf(canvas_y2),
1330                         roundf(canvas_x2) + RULERHANDLE_W / 2,
1331                         roundf(canvas_y2));
1332                 get_canvas()->draw_line(roundf(canvas_x2),
1333                         roundf(canvas_y2) - RULERHANDLE_H / 2,
1334                         roundf(canvas_x2),
1335                         roundf(canvas_y2) + RULERHANDLE_H / 2);
1336                 get_canvas()->set_opaque();
1337         }
1338
1339         return result;
1340 }
1341
1342
1343 static inline double line_dist(float cx,float cy, float tx,float ty)
1344 {
1345         double dx = tx-cx, dy = ty-cy;
1346         return sqrt(dx*dx + dy*dy);
1347 }
1348
1349 static inline bool test_bbox(int cx, int cy, int tx, int ty)
1350 {
1351 //      printf("test_bbox %d,%d - %d,%d = %f\n",cx,cy,tx,ty,line_dist(cx,cy,tx,ty));
1352         return (llabs(cx-tx) < CONTROL_W/2 && llabs(cy-ty) < CONTROL_H/2);
1353 }
1354
1355
1356 int CWindowCanvas::do_mask(int &redraw, int &rerender,
1357                 int button_press, int cursor_motion, int draw)
1358 {
1359 // Retrieve points from top recordable track
1360 //printf("CWindowCanvas::do_mask 1\n");
1361         Track *track = gui->cwindow->calculate_affected_track();
1362 //printf("CWindowCanvas::do_mask 2\n");
1363
1364         if(!track) return 0;
1365 //printf("CWindowCanvas::do_mask 3\n");
1366
1367         MaskAutos *mask_autos = (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
1368         int64_t position = track->to_units(
1369                 mwindow->edl->local_session->get_selectionstart(1),
1370                 0);
1371         ArrayList<MaskPoint*> points;
1372
1373 // Determine the points based on whether
1374 // new keyframes will be generated or drawing is performed.
1375 // If keyframe generation occurs, use the interpolated mask.
1376 // If no keyframe generation occurs, use the previous mask.
1377         int use_interpolated = 0;
1378         if(button_press || cursor_motion) {
1379 #ifdef USE_KEYFRAME_SPANNING
1380                 double selection_start = mwindow->edl->local_session->get_selectionstart(0);
1381                 double selection_end = mwindow->edl->local_session->get_selectionend(0);
1382
1383                 Auto *first = 0;
1384                 mask_autos->get_prev_auto(track->to_units(selection_start, 0),
1385                         PLAY_FORWARD, first, 1);
1386                 Auto *last = 0;
1387                 mask_autos->get_prev_auto(track->to_units(selection_end, 0),
1388                         PLAY_FORWARD, last, 1);
1389
1390                 if(last == first && (!mwindow->edl->session->auto_keyframes))
1391                         use_interpolated = 0;
1392                 else
1393 // If keyframe spanning occurs, use the interpolated points.
1394 // If new keyframe is generated, use the interpolated points.
1395                         use_interpolated = 1;
1396
1397 #else
1398                 if(mwindow->edl->session->auto_keyframes)
1399                         use_interpolated = 1;
1400 #endif
1401         }
1402         else
1403                 use_interpolated = 1;
1404
1405         if(use_interpolated) {
1406 // Interpolate the points to get exactly what is being rendered at this position.
1407                 mask_autos->get_points(&points,
1408                         mwindow->edl->session->cwindow_mask,
1409                         position,
1410                         PLAY_FORWARD);
1411         }
1412         else {
1413 // Use the prev mask
1414                 Auto *prev = 0;
1415                 mask_autos->get_prev_auto(position,
1416                         PLAY_FORWARD,
1417                         prev,
1418                         1);
1419                 ((MaskAuto*)prev)->get_points(&points,
1420                         mwindow->edl->session->cwindow_mask);
1421         }
1422
1423 // Projector zooms relative to the center of the track output.
1424         float half_track_w = (float)track->track_w / 2;
1425         float half_track_h = (float)track->track_h / 2;
1426 // Translate mask to projection
1427         float projector_x, projector_y, projector_z;
1428         track->automation->get_projector(
1429                 &projector_x, &projector_y, &projector_z,
1430                 position, PLAY_FORWARD);
1431
1432
1433 // Get position of cursor relative to mask
1434         float cursor_x = get_cursor_x(), cursor_y = get_cursor_y();
1435         float mask_cursor_x = cursor_x, mask_cursor_y = cursor_y;
1436         canvas_to_output(mwindow->edl, 0, mask_cursor_x, mask_cursor_y);
1437
1438         projector_x += mwindow->edl->session->output_w / 2;
1439         projector_y += mwindow->edl->session->output_h / 2;
1440         mask_cursor_x = (mask_cursor_x - projector_x) / projector_z + half_track_w;
1441         mask_cursor_y = (mask_cursor_y - projector_y) / projector_z + half_track_h;
1442
1443 // Fix cursor origin
1444         if(button_press) {
1445                 gui->x_origin = mask_cursor_x;
1446                 gui->y_origin = mask_cursor_y;
1447         }
1448
1449         int result = 0;
1450 // Points of closest line
1451         int shortest_point1 = -1;
1452         int shortest_point2 = -1;
1453 // Closest point
1454         int shortest_point = -1;
1455 // Distance to closest line
1456         float shortest_line_distance = BC_INFINITY;
1457 // Distance to closest point
1458         float shortest_point_distance = BC_INFINITY;
1459         int selected_point = -1;
1460         int selected_control_point = -1;
1461         float selected_control_point_distance = BC_INFINITY;
1462         ArrayList<int> x_points;
1463         ArrayList<int> y_points;
1464
1465         if(!cursor_motion) {
1466                 if(draw) {
1467                         get_canvas()->set_color(WHITE);
1468                         get_canvas()->set_inverse();
1469                 }
1470 //printf("CWindowCanvas::do_mask 1 %d\n", points.size());
1471
1472 // Never draw closed polygon and a closed
1473 // polygon is harder to add points to.
1474                 for(int i = 0; i < points.size() && !result; i++) {
1475                         MaskPoint *point1 = points.get(i);
1476                         MaskPoint *point2 = (i >= points.size() - 1) ?
1477                                 points.get(0) : points.get(i + 1);
1478                         if(button_press) {
1479                                 float point_distance1 = line_dist(point1->x,point1->y, mask_cursor_x,mask_cursor_y);
1480                                 if(point_distance1 < shortest_point_distance || shortest_point < 0) {
1481                                         shortest_point_distance = point_distance1;
1482                                         shortest_point = i;
1483                                 }
1484
1485                                 float point_distance2 = line_dist(point2->x,point2->y, mask_cursor_x,mask_cursor_y);
1486                                 if(point_distance2 < shortest_point_distance || shortest_point < 0) {
1487                                         shortest_point_distance = point_distance2;
1488                                         shortest_point = (i >= points.size() - 1) ? 0 : (i + 1);
1489                                 }
1490                         }
1491
1492                         int segments = 1 + line_dist(point1->x,point1->y, point2->x,point2->y);
1493
1494 //printf("CWindowCanvas::do_mask 1 %f, %f -> %f, %f projectorz=%f\n",
1495 //point1->x, point1->y, point2->x, point2->y, projector_z);
1496                         for(int j = 0; j <= segments && !result; j++) {
1497 //printf("CWindowCanvas::do_mask 1 %f, %f -> %f, %f\n", x0, y0, x3, y3);
1498                                 float x0 = point1->x, y0 = point1->y;
1499                                 float x1 = point1->x + point1->control_x2;
1500                                 float y1 = point1->y + point1->control_y2;
1501                                 float x2 = point2->x + point2->control_x1;
1502                                 float y2 = point2->y + point2->control_y1;
1503                                 float x3 = point2->x, y3 = point2->y;
1504                                 float canvas_x0 = (x0 - half_track_w) * projector_z + projector_x;
1505                                 float canvas_y0 = (y0 - half_track_h) * projector_z + projector_y;
1506                                 float canvas_x1 = (x1 - half_track_w) * projector_z + projector_x;
1507                                 float canvas_y1 = (y1 - half_track_h) * projector_z + projector_y;
1508                                 float canvas_x2 = (x2 - half_track_w) * projector_z + projector_x;
1509                                 float canvas_y2 = (y2 - half_track_h) * projector_z + projector_y;
1510                                 float canvas_x3 = (x3 - half_track_w) * projector_z + projector_x;
1511                                 float canvas_y3 = (y3 - half_track_h) * projector_z + projector_y;
1512
1513                                 float t = (float)j / segments;
1514                                 float tpow2 = t * t;
1515                                 float tpow3 = t * t * t;
1516                                 float invt = 1 - t;
1517                                 float invtpow2 = invt * invt;
1518                                 float invtpow3 = invt * invt * invt;
1519
1520                                 float x = (           invtpow3 * x0
1521                                         + 3 * t     * invtpow2 * x1
1522                                         + 3 * tpow2 * invt     * x2
1523                                         +     tpow3            * x3);
1524                                 float y = (           invtpow3 * y0
1525                                         + 3 * t     * invtpow2 * y1
1526                                         + 3 * tpow2 * invt     * y2
1527                                         +     tpow3            * y3);
1528                                 float canvas_x = (x - half_track_w) * projector_z + projector_x;
1529                                 float canvas_y = (y - half_track_h) * projector_z + projector_y;
1530 // Test new point addition
1531                                 if(button_press) {
1532                                         float line_distance = line_dist(x,y, mask_cursor_x,mask_cursor_y);
1533
1534 //printf("CWindowCanvas::do_mask 1 x=%f cursor_x=%f y=%f cursor_y=%f %f %f %d, %d\n",
1535 //  x, cursor_x, y, cursor_y, line_distance, shortest_line_distance, shortest_point1, shortest_point2);
1536                                         if(line_distance < shortest_line_distance ||
1537                                                 shortest_point1 < 0) {
1538                                                 shortest_line_distance = line_distance;
1539                                                 shortest_point1 = i;
1540                                                 shortest_point2 = (i >= points.size() - 1) ? 0 : (i + 1);
1541 //printf("CWindowCanvas::do_mask 2 %f %f %d, %d\n",
1542 //  line_distance, shortest_line_distance, shortest_point1, shortest_point2);
1543                                         }
1544
1545 // Test existing point selection
1546 // Test first point
1547                                         if(gui->ctrl_down()) {
1548                                                 float distance = line_dist(x1,y1, mask_cursor_x,mask_cursor_y);
1549
1550                                                 if(distance < selected_control_point_distance) {
1551                                                         selected_point = i;
1552                                                         selected_control_point = 1;
1553                                                         selected_control_point_distance = distance;
1554                                                 }
1555                                         }
1556                                         else {
1557                                                 if(!gui->shift_down()) {
1558                                                         output_to_canvas(mwindow->edl, 0, canvas_x0, canvas_y0);
1559                                                         if(test_bbox(cursor_x, cursor_y, canvas_x0, canvas_y0)) {
1560                                                                 selected_point = i;
1561                                                         }
1562                                                 }
1563                                                 else {
1564                                                         selected_point = shortest_point;
1565                                                 }
1566                                         }
1567 // Test second point
1568                                         if(gui->ctrl_down()) {
1569                                                 float distance = line_dist(x2,y2, mask_cursor_x,mask_cursor_y);
1570
1571 //printf("CWindowCanvas::do_mask %d %f %f\n", i, distance, selected_control_point_distance);
1572                                                 if(distance < selected_control_point_distance) {
1573                                                         selected_point = (i < points.size() - 1 ? i + 1 : 0);
1574                                                         selected_control_point = 0;
1575                                                         selected_control_point_distance = distance;
1576                                                 }
1577                                         }
1578                                         else if(i < points.size() - 1) {
1579                                                 if(!gui->shift_down()) {
1580                                                         output_to_canvas(mwindow->edl, 0, canvas_x3, canvas_y3);
1581                                                         if(test_bbox(cursor_x, cursor_y, canvas_x3, canvas_y3)) {
1582                                                                 selected_point = (i < points.size() - 1 ? i + 1 : 0);
1583                                                         }
1584                                                 }
1585                                                 else {
1586                                                         selected_point = shortest_point;
1587                                                 }
1588                                         }
1589                                 }
1590
1591                                 output_to_canvas(mwindow->edl, 0, canvas_x, canvas_y);
1592
1593                                 if(j > 0) {
1594
1595                                         if(draw) { // Draw joining line
1596                                                 x_points.append((int)canvas_x);
1597                                                 y_points.append((int)canvas_y);
1598                                         }
1599
1600                                         if(j == segments) {
1601                                                 if(draw) { // Draw second anchor
1602                                                         if(i < points.size() - 1) {
1603                                                                 if(i == gui->affected_point - 1)
1604                                                                         get_canvas()->draw_disc(
1605                                                                                 (int)canvas_x - CONTROL_W / 2,
1606                                                                                 (int)canvas_y - CONTROL_W / 2,
1607                                                                                 CONTROL_W, CONTROL_H);
1608                                                                 else
1609                                                                         get_canvas()->draw_circle(
1610                                                                                 (int)canvas_x - CONTROL_W / 2,
1611                                                                                 (int)canvas_y - CONTROL_W / 2,
1612                                                                                 CONTROL_W, CONTROL_H);
1613 // char string[BCTEXTLEN];
1614 // sprintf(string, "%d", (i < points.size() - 1 ? i + 1 : 0));
1615 // canvas->draw_text((int)canvas_x + CONTROL_W, (int)canvas_y + CONTROL_W, string);
1616                                                         }
1617 // Draw second control point.
1618                                                         output_to_canvas(mwindow->edl, 0, canvas_x2, canvas_y2);
1619                                                         get_canvas()->draw_line(
1620                                                                 (int)canvas_x, (int)canvas_y,
1621                                                                 (int)canvas_x2, (int)canvas_y2);
1622                                                         get_canvas()->draw_rectangle(
1623                                                                 (int)canvas_x2 - CONTROL_W / 2,
1624                                                                 (int)canvas_y2 - CONTROL_H / 2,
1625                                                                 CONTROL_W, CONTROL_H);
1626                                                 }
1627                                         }
1628                                 }
1629                                 else {
1630 // Draw first anchor
1631                                         if(i == 0 && draw) {
1632                                                 char mask_label[BCSTRLEN];
1633                                                 sprintf(mask_label, "%d",
1634                                                         mwindow->edl->session->cwindow_mask);
1635                                                 get_canvas()->draw_text(
1636                                                         (int)canvas_x - FIRST_CONTROL_W,
1637                                                         (int)canvas_y - FIRST_CONTROL_H,
1638                                                         mask_label);
1639
1640                                                 get_canvas()->draw_disc(
1641                                                         (int)canvas_x - FIRST_CONTROL_W / 2,
1642                                                         (int)canvas_y - FIRST_CONTROL_H / 2,
1643                                                         FIRST_CONTROL_W, FIRST_CONTROL_H);
1644                                         }
1645
1646 // Draw first control point.
1647                                         if(draw) {
1648                                                 output_to_canvas(mwindow->edl, 0, canvas_x1, canvas_y1);
1649                                                 get_canvas()->draw_line(
1650                                                         (int)canvas_x, (int)canvas_y,
1651                                                         (int)canvas_x1, (int)canvas_y1);
1652                                                 get_canvas()->draw_rectangle(
1653                                                         (int)canvas_x1 - CONTROL_W / 2,
1654                                                         (int)canvas_y1 - CONTROL_H / 2,
1655                                                         CONTROL_W, CONTROL_H);
1656
1657                                                 x_points.append((int)canvas_x);
1658                                                 y_points.append((int)canvas_y);
1659                                         }
1660                                 }
1661 //printf("CWindowCanvas::do_mask 1\n");
1662
1663                         }
1664                 }
1665 //printf("CWindowCanvas::do_mask 1\n");
1666
1667                 if(draw) {
1668                         get_canvas()->draw_polygon(&x_points, &y_points);
1669                         get_canvas()->set_opaque();
1670                 }
1671 //printf("CWindowCanvas::do_mask 1\n");
1672         }
1673
1674         if(button_press && !result) {
1675                 gui->affected_track = gui->cwindow->calculate_affected_track();
1676
1677 // Get keyframe outside the EDL to edit.  This must be rendered
1678 // instead of the EDL keyframes when it exists.  Then it must be
1679 // applied to the EDL keyframes on buttonrelease.
1680                 if(gui->affected_track) {
1681 #ifdef USE_KEYFRAME_SPANNING
1682 // Make copy of current parameters in local keyframe
1683                         gui->mask_keyframe =
1684                                 (MaskAuto*)gui->cwindow->calculate_affected_auto(
1685                                         mask_autos,
1686                                         0);
1687                         gui->orig_mask_keyframe->copy_data(gui->mask_keyframe);
1688 #else
1689
1690                         gui->mask_keyframe =
1691                                 (MaskAuto*)gui->cwindow->calculate_affected_auto(
1692                                         mask_autos,
1693                                         1);
1694 #endif
1695                 }
1696                 SubMask *mask = gui->mask_keyframe->get_submask(mwindow->edl->session->cwindow_mask);
1697
1698
1699 // Translate entire keyframe
1700                 if(gui->alt_down() && mask->points.size()) {
1701                         mwindow->undo->update_undo_before(_("mask translate"), 0);
1702                         gui->current_operation = CWINDOW_MASK_TRANSLATE;
1703                         gui->affected_point = 0;
1704                 }
1705                 else
1706 // Existing point or control point was selected
1707                 if(selected_point >= 0) {
1708                         mwindow->undo->update_undo_before(_("mask adjust"), 0);
1709                         gui->affected_point = selected_point;
1710
1711                         if(selected_control_point == 0)
1712                                 gui->current_operation = CWINDOW_MASK_CONTROL_IN;
1713                         else
1714                         if(selected_control_point == 1)
1715                                 gui->current_operation = CWINDOW_MASK_CONTROL_OUT;
1716                         else
1717                                 gui->current_operation = mwindow->edl->session->cwindow_operation;
1718                 }
1719                 else // No existing point or control point was selected so create a new one
1720                 if(!gui->ctrl_down() && !gui->alt_down()) {
1721                         mwindow->undo->update_undo_before(_("mask point"), 0);
1722 // Create the template
1723                         MaskPoint *point = new MaskPoint;
1724                         point->x = mask_cursor_x;
1725                         point->y = mask_cursor_y;
1726                         point->control_x1 = 0;
1727                         point->control_y1 = 0;
1728                         point->control_x2 = 0;
1729                         point->control_y2 = 0;
1730
1731
1732                         if(shortest_point2 < shortest_point1) {
1733                                 shortest_point2 ^= shortest_point1;
1734                                 shortest_point1 ^= shortest_point2;
1735                                 shortest_point2 ^= shortest_point1;
1736                         }
1737
1738
1739
1740 // printf("CWindowGUI::do_mask 40\n");
1741 // mwindow->edl->dump();
1742 // printf("CWindowGUI::do_mask 50\n");
1743
1744
1745
1746 //printf("CWindowCanvas::do_mask 1 %f %f %d %d\n",
1747 //      shortest_line_distance, shortest_point_distance, shortest_point1, shortest_point2);
1748 //printf("CWindowCanvas::do_mask %d %d\n", shortest_point1, shortest_point2);
1749
1750 // Append to end of list
1751                         if( shortest_point1 == shortest_point2 ||
1752                             labs(shortest_point1 - shortest_point2) > 1) {
1753 #ifdef USE_KEYFRAME_SPANNING
1754
1755                                 MaskPoint *new_point = new MaskPoint;
1756                                 points.append(new_point);
1757                                 *new_point = *point;
1758                                 gui->affected_point = points.size() - 1;
1759
1760 #else
1761
1762 // Need to apply the new point to every keyframe
1763                                 for(MaskAuto *current = (MaskAuto*)mask_autos->default_auto; current; ) {
1764                                         SubMask *submask = current->get_submask(mwindow->edl->session->cwindow_mask);
1765                                         MaskPoint *new_point = new MaskPoint;
1766                                         submask->points.append(new_point);
1767                                         *new_point = *point;
1768                                         if(current == (MaskAuto*)mask_autos->default_auto)
1769                                                 current = (MaskAuto*)mask_autos->first;
1770                                         else
1771                                                 current = (MaskAuto*)NEXT;
1772                                 }
1773                                 gui->affected_point = mask->points.size() - 1;
1774 #endif
1775
1776                                 result = 1;
1777                         }
1778                         else
1779 // Insert between 2 points, shifting back point 2
1780                         if(shortest_point1 >= 0 && shortest_point2 >= 0) {
1781
1782 #ifdef USE_KEYFRAME_SPANNING
1783 // In case the keyframe point count isn't synchronized with the rest of the keyframes,
1784 // avoid a crash.
1785                                 if(points.size() >= shortest_point2) {
1786                                         MaskPoint *new_point = new MaskPoint;
1787                                         points.append(0);
1788                                         for(int i = points.size() - 1;
1789                                                 i > shortest_point2;
1790                                                 i--)
1791                                                 points.values[i] = points.values[i - 1];
1792                                         points.values[shortest_point2] = new_point;
1793
1794                                         *new_point = *point;
1795                                 }
1796
1797 #else
1798
1799                                 for(MaskAuto *current = (MaskAuto*)mask_autos->default_auto; current; ) {
1800                                         SubMask *submask = current->get_submask(mwindow->edl->session->cwindow_mask);
1801 // In case the keyframe point count isn't synchronized with the rest of the keyframes,
1802 // avoid a crash.
1803                                         if(submask->points.size() >= shortest_point2) {
1804                                                 MaskPoint *new_point = new MaskPoint;
1805                                                 submask->points.append(0);
1806                                                 for(int i = submask->points.size() - 1;
1807                                                         i > shortest_point2;
1808                                                         i--)
1809                                                         submask->points.values[i] = submask->points.values[i - 1];
1810                                                 submask->points.values[shortest_point2] = new_point;
1811
1812                                                 *new_point = *point;
1813                                         }
1814
1815                                         if(current == (MaskAuto*)mask_autos->default_auto)
1816                                                 current = (MaskAuto*)mask_autos->first;
1817                                         else
1818                                                 current = (MaskAuto*)NEXT;
1819                                 }
1820
1821 #endif
1822                                 gui->affected_point = shortest_point2;
1823                                 result = 1;
1824                         }
1825
1826
1827 // printf("CWindowGUI::do_mask 20\n");
1828 // mwindow->edl->dump();
1829 // printf("CWindowGUI::do_mask 30\n");
1830
1831                         if(!result) {
1832 //printf("CWindowCanvas::do_mask 1\n");
1833 // Create the first point.
1834 #ifdef USE_KEYFRAME_SPANNING
1835                                 MaskPoint *new_point = new MaskPoint;
1836                                 points.append(new_point);
1837                                 *new_point = *point;
1838                                 gui->affected_point = points.size() - 1;
1839 #else
1840                                 for(MaskAuto *current = (MaskAuto*)mask_autos->default_auto; current; ) {
1841                                         SubMask *submask = current->get_submask(mwindow->edl->session->cwindow_mask);
1842                                         MaskPoint *new_point = new MaskPoint;
1843                                         submask->points.append(new_point);
1844                                         *new_point = *point;
1845                                         if(current == (MaskAuto*)mask_autos->default_auto)
1846                                                 current = (MaskAuto*)mask_autos->first;
1847                                         else
1848                                                 current = (MaskAuto*)NEXT;
1849                                 }
1850                                 gui->affected_point = points.size() - 1;
1851 #endif
1852
1853 //printf("CWindowCanvas::do_mask 3 %d\n", mask->points.size());
1854                         }
1855
1856                         gui->current_operation = mwindow->edl->session->cwindow_operation;
1857 // Delete the template
1858                         delete point;
1859                 }
1860
1861                 result = 1;
1862                 rerender = 1;
1863                 redraw = 1;
1864         }
1865
1866         if(button_press && result) {
1867 #ifdef USE_KEYFRAME_SPANNING
1868                 MaskPoint *point = points.values[gui->affected_point];
1869                 gui->center_x = point->x;
1870                 gui->center_y = point->y;
1871                 gui->control_in_x = point->control_x1;
1872                 gui->control_in_y = point->control_y1;
1873                 gui->control_out_x = point->control_x2;
1874                 gui->control_out_y = point->control_y2;
1875                 gui->tool_panel->raise_window();
1876 #else
1877                 SubMask *mask = gui->mask_keyframe->get_submask(mwindow->edl->session->cwindow_mask);
1878                 MaskPoint *point = mask->points.values[gui->affected_point];
1879                 gui->center_x = point->x;
1880                 gui->center_y = point->y;
1881                 gui->control_in_x = point->control_x1;
1882                 gui->control_in_y = point->control_y1;
1883                 gui->control_out_x = point->control_x2;
1884                 gui->control_out_y = point->control_y2;
1885                 gui->tool_panel->raise_window();
1886 #endif
1887         }
1888
1889 //printf("CWindowCanvas::do_mask 8\n");
1890         if(cursor_motion) {
1891
1892 #ifdef USE_KEYFRAME_SPANNING
1893 // Must update the reference keyframes for every cursor motion
1894                 gui->mask_keyframe =
1895                         (MaskAuto*)gui->cwindow->calculate_affected_auto(
1896                                 mask_autos,
1897                                 0);
1898                 gui->orig_mask_keyframe->copy_data(gui->mask_keyframe);
1899 #endif
1900
1901 //printf("CWindowCanvas::do_mask %d %d\n", __LINE__, gui->affected_point);
1902
1903                 SubMask *mask = gui->mask_keyframe->get_submask(mwindow->edl->session->cwindow_mask);
1904                 if( gui->affected_point >= 0 && gui->affected_point < mask->points.size() &&
1905                         gui->current_operation != CWINDOW_NONE) {
1906 //                      mwindow->undo->update_undo_before(_("mask point"), this);
1907 #ifdef USE_KEYFRAME_SPANNING
1908                         MaskPoint *point = points.get(gui->affected_point);
1909 #else
1910                         MaskPoint *point = mask->points.get(gui->affected_point);
1911 #endif
1912 //                      canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
1913 //printf("CWindowCanvas::do_mask 9 %d %d\n", mask->points.size(), gui->affected_point);
1914
1915                         float last_x = point->x;
1916                         float last_y = point->y;
1917                         float last_control_x1 = point->control_x1;
1918                         float last_control_y1 = point->control_y1;
1919                         float last_control_x2 = point->control_x2;
1920                         float last_control_y2 = point->control_y2;
1921
1922                         switch(gui->current_operation) {
1923                                 case CWINDOW_MASK:
1924 //printf("CWindowCanvas::do_mask %d %d\n", __LINE__, gui->affected_point);
1925                                         point->x = mask_cursor_x - gui->x_origin + gui->center_x;
1926                                         point->y = mask_cursor_y - gui->y_origin + gui->center_y;
1927                                         break;
1928
1929                                 case CWINDOW_MASK_CONTROL_IN:
1930                                         point->control_x1 = mask_cursor_x - gui->x_origin + gui->control_in_x;
1931                                         point->control_y1 = mask_cursor_y - gui->y_origin + gui->control_in_y;
1932                                         break;
1933
1934                                 case CWINDOW_MASK_CONTROL_OUT:
1935                                         point->control_x2 = mask_cursor_x - gui->x_origin + gui->control_out_x;
1936                                         point->control_y2 = mask_cursor_y - gui->y_origin + gui->control_out_y;
1937                                         break;
1938
1939                                 case CWINDOW_MASK_TRANSLATE:
1940 #ifdef USE_KEYFRAME_SPANNING
1941                                         for(int i = 0; i < points.size(); i++) {
1942                                                 points.values[i]->x += mask_cursor_x - gui->x_origin;
1943                                                 points.values[i]->y += mask_cursor_y - gui->y_origin;
1944                                         }
1945 #else
1946                                         for(int i = 0; i < mask->points.size(); i++) {
1947                                                 mask->points.values[i]->x += mask_cursor_x - gui->x_origin;
1948                                                 mask->points.values[i]->y += mask_cursor_y - gui->y_origin;
1949                                         }
1950 #endif
1951                                         gui->x_origin = mask_cursor_x;
1952                                         gui->y_origin = mask_cursor_y;
1953                                         break;
1954                         }
1955
1956                         if( !EQUIV(last_x, point->x) ||
1957                                 !EQUIV(last_y, point->y) ||
1958                                 !EQUIV(last_control_x1, point->control_x1) ||
1959                                 !EQUIV(last_control_y1, point->control_y1) ||
1960                                 !EQUIV(last_control_x2, point->control_x2) ||
1961                                 !EQUIV(last_control_y2, point->control_y2)) {
1962                                 rerender = 1;
1963                                 redraw = 1;
1964                         }
1965                 }
1966                 else
1967                 if(gui->current_operation == CWINDOW_NONE) {
1968 //                      printf("CWindowCanvas::do_mask %d\n", __LINE__);
1969                         int over_point = 0;
1970                         for(int i = 0; i < points.size() && !over_point; i++) {
1971                                 MaskPoint *point = points.get(i);
1972                                 float x0 = point->x;
1973                                 float y0 = point->y;
1974                                 float x1 = point->x + point->control_x1;
1975                                 float y1 = point->y + point->control_y1;
1976                                 float x2 = point->x + point->control_x2;
1977                                 float y2 = point->y + point->control_y2;
1978                                 float canvas_x0 = (x0 - half_track_w) * projector_z + projector_x;
1979                                 float canvas_y0 = (y0 - half_track_h) * projector_z + projector_y;
1980
1981                                 output_to_canvas(mwindow->edl, 0, canvas_x0, canvas_y0);
1982                                 if(test_bbox(cursor_x, cursor_y, canvas_x0, canvas_y0)) {
1983                                         over_point = 1;
1984                                 }
1985
1986                                 if(!over_point && gui->ctrl_down()) {
1987                                         float canvas_x1 = (x1 - half_track_w) * projector_z + projector_x;
1988                                         float canvas_y1 = (y1 - half_track_h) * projector_z + projector_y;
1989                                         output_to_canvas(mwindow->edl, 0, canvas_x1, canvas_y1);
1990                                         if(test_bbox(cursor_x, cursor_y, canvas_x1, canvas_y1)) {
1991                                                 over_point = 1;
1992                                         }
1993                                         else {
1994                                                 float canvas_x2 = (x2 - half_track_w) * projector_z + projector_x;
1995                                                 float canvas_y2 = (y2 - half_track_h) * projector_z + projector_y;
1996                                                 output_to_canvas(mwindow->edl, 0, canvas_x2, canvas_y2);
1997                                                 if(test_bbox(cursor_x, cursor_y, canvas_x2, canvas_y2)) {
1998                                                         over_point = 1;
1999                                                 }
2000                                         }
2001                                 }
2002                         }
2003
2004                         set_cursor( over_point ? ARROW_CURSOR : CROSS_CURSOR );
2005                 }
2006
2007                 result = 1;
2008         }
2009 //printf("CWindowCanvas::do_mask 2 %d %d %d\n", result, rerender, redraw);
2010
2011
2012 #ifdef USE_KEYFRAME_SPANNING
2013 // Must commit change after operation.
2014         if(rerender && track) {
2015 // Swap EDL keyframe with original.
2016 // Apply new values to keyframe span
2017                 MaskAuto temp_keyframe(mwindow->edl, mask_autos);
2018                 temp_keyframe.copy_data(gui->mask_keyframe);
2019 // Apply interpolated points back to keyframe
2020                 temp_keyframe.set_points(&points, mwindow->edl->session->cwindow_mask);
2021                 gui->mask_keyframe->copy_data(gui->orig_mask_keyframe);
2022                 mask_autos->update_parameter(&temp_keyframe);
2023         }
2024 #endif
2025
2026         points.remove_all_objects();
2027 //printf("CWindowCanvas::do_mask 20\n");
2028         return result;
2029 }
2030
2031 int CWindowCanvas::do_eyedrop(int &rerender, int button_press, int draw)
2032 {
2033         int result = 0;
2034         int radius = mwindow->edl->session->eyedrop_radius;
2035         int row1 = 0;
2036         int row2 = 0;
2037         int column1 = 0;
2038         int column2 = 0;
2039
2040
2041
2042         if(refresh_frame && refresh_frame->get_w()>0 && refresh_frame->get_h()>0)
2043         {
2044
2045                 if(draw)
2046                 {
2047                         row1 = gui->eyedrop_y - radius;
2048                         row2 = gui->eyedrop_y + radius;
2049                         column1 = gui->eyedrop_x - radius;
2050                         column2 = gui->eyedrop_x + radius;
2051
2052                         CLAMP(row1, 0, refresh_frame->get_h() - 1);
2053                         CLAMP(row2, 0, refresh_frame->get_h() - 1);
2054                         CLAMP(column1, 0, refresh_frame->get_w() - 1);
2055                         CLAMP(column2, 0, refresh_frame->get_w() - 1);
2056
2057                         if(row2 <= row1) row2 = row1 + 1;
2058                         if(column2 <= column1) column2 = column1 + 1;
2059
2060                         float x1 = column1;
2061                         float y1 = row1;
2062                         float x2 = column2;
2063                         float y2 = row2;
2064
2065                         output_to_canvas(mwindow->edl, 0, x1, y1);
2066                         output_to_canvas(mwindow->edl, 0, x2, y2);
2067 //printf("CWindowCanvas::do_eyedrop %d %f %f %f %f\n", __LINE__, x1, x2, y1, y2);
2068
2069                         if(x2 - x1 >= 1 && y2 - y1 >= 1)
2070                         {
2071                                 get_canvas()->set_inverse();
2072                                 get_canvas()->set_color(WHITE);
2073
2074                                 get_canvas()->draw_rectangle((int)x1,
2075                                         (int)y1,
2076                                         (int)(x2 - x1),
2077                                         (int)(y2 - y1));
2078
2079                                 get_canvas()->set_opaque();
2080                                 get_canvas()->flash();
2081                         }
2082                         return 0;
2083                 }
2084         }
2085
2086         if(button_press)
2087         {
2088                 gui->current_operation = CWINDOW_EYEDROP;
2089                 gui->tool_panel->raise_window();
2090         }
2091
2092         if(gui->current_operation == CWINDOW_EYEDROP)
2093         {
2094                 mwindow->undo->update_undo_before(_("Eyedrop"), this);
2095
2096 // Get color out of frame.
2097 // Doesn't work during playback because that bypasses the refresh frame.
2098                 if(refresh_frame && refresh_frame->get_w()>0 && refresh_frame->get_h()>0)
2099                 {
2100                         float cursor_x = get_cursor_x();
2101                         float cursor_y = get_cursor_y();
2102                         canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
2103                         CLAMP(cursor_x, 0, refresh_frame->get_w() - 1);
2104                         CLAMP(cursor_y, 0, refresh_frame->get_h() - 1);
2105
2106                         row1 = cursor_y - radius;
2107                         row2 = cursor_y + radius;
2108                         column1 = cursor_x - radius;
2109                         column2 = cursor_x + radius;
2110                         CLAMP(row1, 0, refresh_frame->get_h() - 1);
2111                         CLAMP(row2, 0, refresh_frame->get_h() - 1);
2112                         CLAMP(column1, 0, refresh_frame->get_w() - 1);
2113                         CLAMP(column2, 0, refresh_frame->get_w() - 1);
2114                         if(row2 <= row1) row2 = row1 + 1;
2115                         if(column2 <= column1) column2 = column1 + 1;
2116
2117
2118 // hide it
2119                         if(gui->eyedrop_visible)
2120                         {
2121                                 int temp;
2122                                 do_eyedrop(temp, 0, 1);
2123                                 gui->eyedrop_visible = 0;
2124                         }
2125
2126                         gui->eyedrop_x = cursor_x;
2127                         gui->eyedrop_y = cursor_y;
2128
2129 // show it
2130                         {
2131                                 int temp;
2132                                 do_eyedrop(temp, 0, 1);
2133                                 gui->eyedrop_visible = 1;
2134                         }
2135
2136 // Decompression coefficients straight out of jpeglib
2137 #define V_TO_R    1.40200
2138 #define V_TO_G    -0.71414
2139
2140 #define U_TO_G    -0.34414
2141 #define U_TO_B    1.77200
2142
2143 #define GET_COLOR(type, components, max, do_yuv) \
2144 { \
2145         type *row = (type*)(refresh_frame->get_rows()[i]) + \
2146                 j * components; \
2147         float red = (float)*row++ / max; \
2148         float green = (float)*row++ / max; \
2149         float blue = (float)*row++ / max; \
2150         if(do_yuv) \
2151         { \
2152                 float r = red + V_TO_R * (blue - 0.5); \
2153                 float g = red + U_TO_G * (green - 0.5) + V_TO_G * (blue - 0.5); \
2154                 float b = red + U_TO_B * (green - 0.5); \
2155                 mwindow->edl->local_session->red += r; \
2156                 mwindow->edl->local_session->green += g; \
2157                 mwindow->edl->local_session->blue += b; \
2158                 if(r > mwindow->edl->local_session->red_max) mwindow->edl->local_session->red_max = r; \
2159                 if(g > mwindow->edl->local_session->green_max) mwindow->edl->local_session->green_max = g; \
2160                 if(b > mwindow->edl->local_session->blue_max) mwindow->edl->local_session->blue_max = b; \
2161         } \
2162         else \
2163         { \
2164                 mwindow->edl->local_session->red += red; \
2165                 mwindow->edl->local_session->green += green; \
2166                 mwindow->edl->local_session->blue += blue; \
2167                 if(red > mwindow->edl->local_session->red_max) mwindow->edl->local_session->red_max = red; \
2168                 if(green > mwindow->edl->local_session->green_max) mwindow->edl->local_session->green_max = green; \
2169                 if(blue > mwindow->edl->local_session->blue_max) mwindow->edl->local_session->blue_max = blue; \
2170         } \
2171 }
2172
2173
2174
2175                         mwindow->edl->local_session->red = 0;
2176                         mwindow->edl->local_session->green = 0;
2177                         mwindow->edl->local_session->blue = 0;
2178                         mwindow->edl->local_session->red_max = 0;
2179                         mwindow->edl->local_session->green_max = 0;
2180                         mwindow->edl->local_session->blue_max = 0;
2181                         for(int i = row1; i < row2; i++)
2182                         {
2183                                 for(int j = column1; j < column2; j++)
2184                                 {
2185                                         switch(refresh_frame->get_color_model())
2186                                         {
2187                                                 case BC_YUV888:
2188                                                         GET_COLOR(unsigned char, 3, 0xff, 1);
2189                                                         break;
2190                                                 case BC_YUVA8888:
2191                                                         GET_COLOR(unsigned char, 4, 0xff, 1);
2192                                                         break;
2193                                                 case BC_YUV161616:
2194                                                         GET_COLOR(uint16_t, 3, 0xffff, 1);
2195                                                         break;
2196                                                 case BC_YUVA16161616:
2197                                                         GET_COLOR(uint16_t, 4, 0xffff, 1);
2198                                                         break;
2199                                                 case BC_RGB888:
2200                                                         GET_COLOR(unsigned char, 3, 0xff, 0);
2201                                                         break;
2202                                                 case BC_RGBA8888:
2203                                                         GET_COLOR(unsigned char, 4, 0xff, 0);
2204                                                         break;
2205                                                 case BC_RGB_FLOAT:
2206                                                         GET_COLOR(float, 3, 1.0, 0);
2207                                                         break;
2208                                                 case BC_RGBA_FLOAT:
2209                                                         GET_COLOR(float, 4, 1.0, 0);
2210                                                         break;
2211                                         }
2212                                 }
2213                         }
2214
2215                         mwindow->edl->local_session->red /= (row2 - row1) * (column2 - column1);
2216                         mwindow->edl->local_session->green /= (row2 - row1) * (column2 - column1);
2217                         mwindow->edl->local_session->blue /= (row2 - row1) * (column2 - column1);
2218
2219                 }
2220                 else
2221                 {
2222                         mwindow->edl->local_session->red = 0;
2223                         mwindow->edl->local_session->green = 0;
2224                         mwindow->edl->local_session->blue = 0;
2225                         gui->eyedrop_visible = 0;
2226                 }
2227
2228
2229                 gui->update_tool();
2230
2231
2232
2233                 result = 1;
2234 // Can't rerender since the color value is from the output of any effect it
2235 // goes into.
2236 //              rerender = 1;
2237                 mwindow->undo->update_undo_after(_("Eyedrop"), LOAD_SESSION);
2238         }
2239
2240         return result;
2241 }
2242
2243 void CWindowCanvas::draw_overlays()
2244 {
2245         if(mwindow->edl->session->safe_regions)
2246         {
2247                 draw_safe_regions();
2248         }
2249
2250         if(mwindow->edl->session->cwindow_scrollbars)
2251         {
2252 // Always draw output rectangle
2253                 float x1, y1, x2, y2;
2254                 x1 = 0;
2255                 x2 = mwindow->edl->session->output_w;
2256                 y1 = 0;
2257                 y2 = mwindow->edl->session->output_h;
2258                 output_to_canvas(mwindow->edl, 0, x1, y1);
2259                 output_to_canvas(mwindow->edl, 0, x2, y2);
2260
2261                 get_canvas()->set_inverse();
2262                 get_canvas()->set_color(WHITE);
2263
2264                 get_canvas()->draw_rectangle((int)x1,
2265                                 (int)y1,
2266                                 (int)(x2 - x1),
2267                                 (int)(y2 - y1));
2268
2269                 get_canvas()->set_opaque();
2270         }
2271
2272         if(gui->highlighted)
2273         {
2274                 get_canvas()->set_color(WHITE);
2275                 get_canvas()->set_inverse();
2276                 get_canvas()->draw_rectangle(0, 0, get_canvas()->get_w(), get_canvas()->get_h());
2277                 get_canvas()->draw_rectangle(1, 1, get_canvas()->get_w() - 2, get_canvas()->get_h() - 2);
2278                 get_canvas()->set_opaque();
2279         }
2280
2281         int temp1 = 0, temp2 = 0;
2282 //printf("CWindowCanvas::draw_overlays 1 %d\n", mwindow->edl->session->cwindow_operation);
2283         switch(mwindow->edl->session->cwindow_operation)
2284         {
2285                 case CWINDOW_CAMERA:
2286                         draw_bezier(1);
2287                         break;
2288
2289                 case CWINDOW_PROJECTOR:
2290                         draw_bezier(0);
2291                         break;
2292
2293                 case CWINDOW_CROP:
2294                         draw_crop();
2295                         break;
2296
2297                 case CWINDOW_MASK:
2298                         do_mask(temp1, temp2, 0, 0, 1);
2299                         break;
2300
2301                 case CWINDOW_RULER:
2302                         do_ruler(1, 0, 0, 0);
2303                         break;
2304
2305                 case CWINDOW_EYEDROP:
2306                 if(gui->eyedrop_visible)
2307                 {
2308                         int rerender;
2309                         do_eyedrop(rerender, 0, 1);
2310                         gui->eyedrop_visible = 1;
2311                         break;
2312                 }
2313         }
2314 }
2315
2316 void CWindowCanvas::draw_safe_regions()
2317 {
2318         float action_x1, action_x2, action_y1, action_y2;
2319         float title_x1, title_x2, title_y1, title_y2;
2320
2321         action_x1 = mwindow->edl->session->output_w / 2 - mwindow->edl->session->output_w / 2 * 0.9;
2322         action_x2 = mwindow->edl->session->output_w / 2 + mwindow->edl->session->output_w / 2 * 0.9;
2323         action_y1 = mwindow->edl->session->output_h / 2 - mwindow->edl->session->output_h / 2 * 0.9;
2324         action_y2 = mwindow->edl->session->output_h / 2 + mwindow->edl->session->output_h / 2 * 0.9;
2325         title_x1 = mwindow->edl->session->output_w / 2 - mwindow->edl->session->output_w / 2 * 0.8;
2326         title_x2 = mwindow->edl->session->output_w / 2 + mwindow->edl->session->output_w / 2 * 0.8;
2327         title_y1 = mwindow->edl->session->output_h / 2 - mwindow->edl->session->output_h / 2 * 0.8;
2328         title_y2 = mwindow->edl->session->output_h / 2 + mwindow->edl->session->output_h / 2 * 0.8;
2329
2330         output_to_canvas(mwindow->edl, 0, action_x1, action_y1);
2331         output_to_canvas(mwindow->edl, 0, action_x2, action_y2);
2332         output_to_canvas(mwindow->edl, 0, title_x1, title_y1);
2333         output_to_canvas(mwindow->edl, 0, title_x2, title_y2);
2334
2335         get_canvas()->set_inverse();
2336         get_canvas()->set_color(WHITE);
2337
2338         get_canvas()->draw_rectangle((int)action_x1,
2339                         (int)action_y1,
2340                         (int)(action_x2 - action_x1),
2341                         (int)(action_y2 - action_y1));
2342         get_canvas()->draw_rectangle((int)title_x1,
2343                         (int)title_y1,
2344                         (int)(title_x2 - title_x1),
2345                         (int)(title_y2 - title_y1));
2346
2347         get_canvas()->set_opaque();
2348 }
2349
2350 void CWindowCanvas::reset_keyframe(int do_camera)
2351 {
2352         FloatAuto *x_keyframe = 0;
2353         FloatAuto *y_keyframe = 0;
2354         FloatAuto *z_keyframe = 0;
2355         Track *affected_track = 0;
2356
2357         affected_track = gui->cwindow->calculate_affected_track();
2358
2359         if(affected_track)
2360         {
2361                 gui->cwindow->calculate_affected_autos(&x_keyframe,
2362                         &y_keyframe,
2363                         &z_keyframe,
2364                         affected_track,
2365                         do_camera,
2366                         1,
2367                         1,
2368                         1);
2369
2370                 x_keyframe->set_value(0);
2371                 y_keyframe->set_value(0);
2372                 z_keyframe->set_value(1);
2373
2374                 mwindow->sync_parameters(CHANGE_PARAMS);
2375                 gui->update_tool();
2376         }
2377 }
2378
2379 void CWindowCanvas::reset_camera()
2380 {
2381         reset_keyframe(1);
2382 }
2383
2384 void CWindowCanvas::reset_projector()
2385 {
2386         reset_keyframe(0);
2387 }
2388
2389 int CWindowCanvas::test_crop(int button_press, int &redraw)
2390 {
2391         int result = 0;
2392         int handle_selected = -1;
2393         float x1 = mwindow->edl->session->crop_x1;
2394         float y1 = mwindow->edl->session->crop_y1;
2395         float x2 = mwindow->edl->session->crop_x2;
2396         float y2 = mwindow->edl->session->crop_y2;
2397         float cursor_x = get_cursor_x();
2398         float cursor_y = get_cursor_y();
2399         float canvas_x1 = x1;
2400         float canvas_y1 = y1;
2401         float canvas_x2 = x2;
2402         float canvas_y2 = y2;
2403         float canvas_cursor_x = cursor_x;
2404         float canvas_cursor_y = cursor_y;
2405
2406         canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
2407 // Use screen normalized coordinates for hot spot tests.
2408         output_to_canvas(mwindow->edl, 0, canvas_x1, canvas_y1);
2409         output_to_canvas(mwindow->edl, 0, canvas_x2, canvas_y2);
2410
2411
2412         if(gui->current_operation == CWINDOW_CROP)
2413         {
2414                 handle_selected = gui->crop_handle;
2415         }
2416         else
2417         if(canvas_cursor_x >= canvas_x1 && canvas_cursor_x < canvas_x1 + CROPHANDLE_W &&
2418                 canvas_cursor_y >= canvas_y1 && canvas_cursor_y < canvas_y1 + CROPHANDLE_H)
2419         {
2420                 handle_selected = 0;
2421                 gui->crop_origin_x = x1;
2422                 gui->crop_origin_y = y1;
2423         }
2424         else
2425         if(canvas_cursor_x >= canvas_x2 - CROPHANDLE_W && canvas_cursor_x < canvas_x2 &&
2426                 canvas_cursor_y >= canvas_y1 && canvas_cursor_y < canvas_y1 + CROPHANDLE_H)
2427         {
2428                 handle_selected = 1;
2429                 gui->crop_origin_x = x2;
2430                 gui->crop_origin_y = y1;
2431         }
2432         else
2433         if(canvas_cursor_x >= canvas_x1 && canvas_cursor_x < canvas_x1 + CROPHANDLE_W &&
2434                 canvas_cursor_y >= canvas_y2 - CROPHANDLE_H && canvas_cursor_y < canvas_y2)
2435         {
2436                 handle_selected = 2;
2437                 gui->crop_origin_x = x1;
2438                 gui->crop_origin_y = y2;
2439         }
2440         else
2441         if(canvas_cursor_x >= canvas_x2 - CROPHANDLE_W && canvas_cursor_x < canvas_x2 &&
2442                 canvas_cursor_y >= canvas_y2 - CROPHANDLE_H && canvas_cursor_y < canvas_y2)
2443         {
2444                 handle_selected = 3;
2445                 gui->crop_origin_x = x2;
2446                 gui->crop_origin_y = y2;
2447         }
2448         else
2449 // Start new box
2450         {
2451                 gui->crop_origin_x = cursor_x;
2452                 gui->crop_origin_y = cursor_y;
2453         }
2454
2455 // printf("test crop %d %d\n",
2456 //      gui->current_operation,
2457 //      handle_selected);
2458
2459 // Start dragging.
2460         if(button_press)
2461         {
2462                 if(gui->alt_down())
2463                 {
2464                         gui->crop_translate = 1;
2465                         gui->crop_origin_x1 = x1;
2466                         gui->crop_origin_y1 = y1;
2467                         gui->crop_origin_x2 = x2;
2468                         gui->crop_origin_y2 = y2;
2469                 }
2470                 else
2471                         gui->crop_translate = 0;
2472
2473                 gui->current_operation = CWINDOW_CROP;
2474                 gui->crop_handle = handle_selected;
2475                 gui->x_origin = cursor_x;
2476                 gui->y_origin = cursor_y;
2477                 gui->tool_panel->raise_window();
2478                 result = 1;
2479
2480                 if(handle_selected < 0 && !gui->crop_translate)
2481                 {
2482                         x2 = x1 = cursor_x;
2483                         y2 = y1 = cursor_y;
2484                         mwindow->edl->session->crop_x1 = (int)x1;
2485                         mwindow->edl->session->crop_y1 = (int)y1;
2486                         mwindow->edl->session->crop_x2 = (int)x2;
2487                         mwindow->edl->session->crop_y2 = (int)y2;
2488                         redraw = 1;
2489                 }
2490         }
2491     else
2492 // Translate all 4 points
2493         if(gui->current_operation == CWINDOW_CROP && gui->crop_translate)
2494         {
2495                 x1 = cursor_x - gui->x_origin + gui->crop_origin_x1;
2496                 y1 = cursor_y - gui->y_origin + gui->crop_origin_y1;
2497                 x2 = cursor_x - gui->x_origin + gui->crop_origin_x2;
2498                 y2 = cursor_y - gui->y_origin + gui->crop_origin_y2;
2499
2500                 mwindow->edl->session->crop_x1 = (int)x1;
2501                 mwindow->edl->session->crop_y1 = (int)y1;
2502                 mwindow->edl->session->crop_x2 = (int)x2;
2503                 mwindow->edl->session->crop_y2 = (int)y2;
2504                 result = 1;
2505                 redraw = 1;
2506         }
2507         else
2508 // Update dragging
2509         if(gui->current_operation == CWINDOW_CROP)
2510         {
2511                 switch(gui->crop_handle)
2512                 {
2513                         case -1:
2514                                 x1 = gui->crop_origin_x;
2515                                 y1 = gui->crop_origin_y;
2516                                 x2 = gui->crop_origin_x;
2517                                 y2 = gui->crop_origin_y;
2518                                 if(cursor_x < gui->x_origin)
2519                                 {
2520                                         if(cursor_y < gui->y_origin)
2521                                         {
2522                                                 x1 = cursor_x;
2523                                                 y1 = cursor_y;
2524                                         }
2525                                         else
2526                                         if(cursor_y >= gui->y_origin)
2527                                         {
2528                                                 x1 = cursor_x;
2529                                                 y2 = cursor_y;
2530                                         }
2531                                 }
2532                                 else
2533                                 if(cursor_x  >= gui->x_origin)
2534                                 {
2535                                         if(cursor_y < gui->y_origin)
2536                                         {
2537                                                 y1 = cursor_y;
2538                                                 x2 = cursor_x;
2539                                         }
2540                                         else
2541                                         if(cursor_y >= gui->y_origin)
2542                                         {
2543                                                 x2 = cursor_x;
2544                                                 y2 = cursor_y;
2545                                         }
2546                                 }
2547
2548 // printf("test crop %d %d %d %d\n",
2549 //      mwindow->edl->session->crop_x1,
2550 //      mwindow->edl->session->crop_y1,
2551 //      mwindow->edl->session->crop_x2,
2552 //      mwindow->edl->session->crop_y2);
2553                                 break;
2554                         case 0:
2555                                 x1 = cursor_x - gui->x_origin + gui->crop_origin_x;
2556                                 y1 = cursor_y - gui->y_origin + gui->crop_origin_y;
2557                                 break;
2558                         case 1:
2559                                 x2 = cursor_x - gui->x_origin + gui->crop_origin_x;
2560                                 y1 = cursor_y - gui->y_origin + gui->crop_origin_y;
2561                                 break;
2562                         case 2:
2563                                 x1 = cursor_x - gui->x_origin + gui->crop_origin_x;
2564                                 y2 = cursor_y - gui->y_origin + gui->crop_origin_y;
2565                                 break;
2566                         case 3:
2567                                 x2 = cursor_x - gui->x_origin + gui->crop_origin_x;
2568                                 y2 = cursor_y - gui->y_origin + gui->crop_origin_y;
2569                                 break;
2570                 }
2571
2572                 if(!EQUIV(mwindow->edl->session->crop_x1, x1) ||
2573                         !EQUIV(mwindow->edl->session->crop_x2, x2) ||
2574                         !EQUIV(mwindow->edl->session->crop_y1, y1) ||
2575                         !EQUIV(mwindow->edl->session->crop_y2, y2))
2576                 {
2577                         if (x1 > x2)
2578                         {
2579                                 float tmp = x1;
2580                                 x1 = x2;
2581                                 x2 = tmp;
2582                                 switch (gui->crop_handle)
2583                                 {
2584                                         case 0: gui->crop_handle = 1; break;
2585                                         case 1: gui->crop_handle = 0; break;
2586                                         case 2: gui->crop_handle = 3; break;
2587                                         case 3: gui->crop_handle = 2; break;
2588                                         default: break;
2589                                 }
2590                         }
2591
2592                         if (y1 > y2)
2593                         {
2594                                 float tmp = y1;
2595                                 y1 = y2;
2596                                 y2 = tmp;
2597                                 switch (gui->crop_handle)
2598                                 {
2599                                         case 0: gui->crop_handle = 2; break;
2600                                         case 1: gui->crop_handle = 3; break;
2601                                         case 2: gui->crop_handle = 0; break;
2602                                         case 3: gui->crop_handle = 1; break;
2603                                         default: break;
2604                                 }
2605                         }
2606
2607                         mwindow->edl->session->crop_x1 = (int)x1;
2608                         mwindow->edl->session->crop_y1 = (int)y1;
2609                         mwindow->edl->session->crop_x2 = (int)x2;
2610                         mwindow->edl->session->crop_y2 = (int)y2;
2611                         result = 1;
2612                         redraw = 1;
2613                 }
2614         }
2615         else
2616 // Update cursor font
2617         if(handle_selected >= 0)
2618         {
2619                 switch(handle_selected)
2620                 {
2621                         case 0:
2622                                 set_cursor(UPLEFT_RESIZE);
2623                                 break;
2624                         case 1:
2625                                 set_cursor(UPRIGHT_RESIZE);
2626                                 break;
2627                         case 2:
2628                                 set_cursor(DOWNLEFT_RESIZE);
2629                                 break;
2630                         case 3:
2631                                 set_cursor(DOWNRIGHT_RESIZE);
2632                                 break;
2633                 }
2634                 result = 1;
2635         }
2636         else
2637         {
2638                 set_cursor(ARROW_CURSOR);
2639         }
2640 #define CLAMP(x, y, z) ((x) = ((x) < (y) ? (y) : ((x) > (z) ? (z) : (x))))
2641
2642         if(redraw)
2643         {
2644                 CLAMP(mwindow->edl->session->crop_x1, 0, mwindow->edl->session->output_w);
2645                 CLAMP(mwindow->edl->session->crop_x2, 0, mwindow->edl->session->output_w);
2646                 CLAMP(mwindow->edl->session->crop_y1, 0, mwindow->edl->session->output_h);
2647                 CLAMP(mwindow->edl->session->crop_y2, 0, mwindow->edl->session->output_h);
2648 // printf("CWindowCanvas::test_crop %d %d %d %d\n",
2649 //      mwindow->edl->session->crop_x2,
2650 //      mwindow->edl->session->crop_y2,
2651 //      mwindow->edl->calculate_output_w(0),
2652 //      mwindow->edl->calculate_output_h(0));
2653         }
2654         return result;
2655 }
2656
2657
2658 void CWindowCanvas::draw_crop()
2659 {
2660         get_canvas()->set_inverse();
2661         get_canvas()->set_color(WHITE);
2662
2663         float x1 = mwindow->edl->session->crop_x1;
2664         float y1 = mwindow->edl->session->crop_y1;
2665         float x2 = mwindow->edl->session->crop_x2;
2666         float y2 = mwindow->edl->session->crop_y2;
2667
2668         output_to_canvas(mwindow->edl, 0, x1, y1);
2669         output_to_canvas(mwindow->edl, 0, x2, y2);
2670
2671         if(x2 - x1 && y2 - y1)
2672                 get_canvas()->draw_rectangle((int)x1,
2673                         (int)y1,
2674                         (int)(x2 - x1),
2675                         (int)(y2 - y1));
2676
2677         draw_crophandle((int)x1, (int)y1);
2678         draw_crophandle((int)x2 - CROPHANDLE_W, (int)y1);
2679         draw_crophandle((int)x1, (int)y2 - CROPHANDLE_H);
2680         draw_crophandle((int)x2 - CROPHANDLE_W, (int)y2 - CROPHANDLE_H);
2681         get_canvas()->set_opaque();
2682 }
2683
2684
2685
2686
2687
2688
2689
2690
2691 void CWindowCanvas::draw_bezier(int do_camera)
2692 {
2693         Track *track = gui->cwindow->calculate_affected_track();
2694
2695         if(!track) return;
2696
2697         float center_x;
2698         float center_y;
2699         float center_z;
2700         int64_t position = track->to_units(
2701                 mwindow->edl->local_session->get_selectionstart(1),
2702                 0);
2703
2704         track->automation->get_projector(&center_x,
2705                 &center_y,
2706                 &center_z,
2707                 position,
2708                 PLAY_FORWARD);
2709
2710 //      center_x += track->track_w / 2;
2711 //      center_y += track->track_h / 2;
2712         center_x += mwindow->edl->session->output_w / 2;
2713         center_y += mwindow->edl->session->output_h / 2;
2714         float track_x1 = center_x - track->track_w / 2 * center_z;
2715         float track_y1 = center_y - track->track_h / 2 * center_z;
2716         float track_x2 = track_x1 + track->track_w * center_z;
2717         float track_y2 = track_y1 + track->track_h * center_z;
2718
2719         output_to_canvas(mwindow->edl, 0, track_x1, track_y1);
2720         output_to_canvas(mwindow->edl, 0, track_x2, track_y2);
2721
2722 #define DRAW_PROJECTION(offset) \
2723         get_canvas()->draw_rectangle((int)track_x1 + offset, \
2724                 (int)track_y1 + offset, \
2725                 (int)(track_x2 - track_x1), \
2726                 (int)(track_y2 - track_y1)); \
2727         get_canvas()->draw_line((int)track_x1 + offset,  \
2728                 (int)track_y1 + offset, \
2729                 (int)track_x2 + offset, \
2730                 (int)track_y2 + offset); \
2731         get_canvas()->draw_line((int)track_x2 + offset,  \
2732                 (int)track_y1 + offset, \
2733                 (int)track_x1 + offset, \
2734                 (int)track_y2 + offset); \
2735
2736
2737 // Drop shadow
2738         get_canvas()->set_color(BLACK);
2739         DRAW_PROJECTION(1);
2740
2741 //      canvas->set_inverse();
2742         if(do_camera)
2743                 get_canvas()->set_color(GREEN);
2744         else
2745                 get_canvas()->set_color(RED);
2746
2747         DRAW_PROJECTION(0);
2748 //      canvas->set_opaque();
2749
2750 }
2751
2752 int CWindowCanvas::test_bezier(int button_press,
2753         int &redraw,
2754         int &redraw_canvas,
2755         int &rerender,
2756         int do_camera)
2757 {
2758         int result = 0;
2759
2760 // Processing drag operation.
2761 // Create keyframe during first cursor motion.
2762         if(!button_press)
2763         {
2764
2765                 float cursor_x = get_cursor_x();
2766                 float cursor_y = get_cursor_y();
2767                 canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
2768
2769                 if(gui->current_operation == CWINDOW_CAMERA ||
2770                         gui->current_operation == CWINDOW_PROJECTOR)
2771                 {
2772                         if(!gui->ctrl_down() && gui->shift_down() && !gui->translating_zoom)
2773                         {
2774                                 gui->translating_zoom = 1;
2775                                 gui->reset_affected();
2776                         }
2777                         else
2778                         if(!gui->ctrl_down() && !gui->shift_down() && gui->translating_zoom)
2779                         {
2780                                 gui->translating_zoom = 0;
2781                                 gui->reset_affected();
2782                         }
2783
2784 // Get target keyframe
2785                         float last_center_x;
2786                         float last_center_y;
2787                         float last_center_z;
2788                         int created;
2789
2790                         if(!gui->affected_x && !gui->affected_y && !gui->affected_z)
2791                         {
2792                                 FloatAutos *affected_x_autos;
2793                                 FloatAutos *affected_y_autos;
2794                                 FloatAutos *affected_z_autos;
2795                                 if(!gui->affected_track) return 0;
2796                                 double position = mwindow->edl->local_session->get_selectionstart(1);
2797                                 int64_t track_position = gui->affected_track->to_units(position, 0);
2798
2799                                 if(mwindow->edl->session->cwindow_operation == CWINDOW_CAMERA)
2800                                 {
2801                                         affected_x_autos = (FloatAutos*)gui->affected_track->automation->autos[AUTOMATION_CAMERA_X];
2802                                         affected_y_autos = (FloatAutos*)gui->affected_track->automation->autos[AUTOMATION_CAMERA_Y];
2803                                         affected_z_autos = (FloatAutos*)gui->affected_track->automation->autos[AUTOMATION_CAMERA_Z];
2804                                 }
2805                                 else
2806                                 {
2807                                         affected_x_autos = (FloatAutos*)gui->affected_track->automation->autos[AUTOMATION_PROJECTOR_X];
2808                                         affected_y_autos = (FloatAutos*)gui->affected_track->automation->autos[AUTOMATION_PROJECTOR_Y];
2809                                         affected_z_autos = (FloatAutos*)gui->affected_track->automation->autos[AUTOMATION_PROJECTOR_Z];
2810                                 }
2811
2812
2813                                 if(gui->translating_zoom)
2814                                 {
2815                                         FloatAuto *previous = 0;
2816                                         FloatAuto *next = 0;
2817                                         float new_z = affected_z_autos->get_value(
2818                                                 track_position,
2819                                                 PLAY_FORWARD,
2820                                                 previous,
2821                                                 next);
2822                                         gui->affected_z =
2823                                                 (FloatAuto*)gui->cwindow->calculate_affected_auto(
2824                                                         affected_z_autos, 1, &created, 0);
2825                                         if(created) {
2826                                                 gui->affected_z->set_value(new_z);
2827                                                 redraw_canvas = 1;
2828                                         }
2829                                 }
2830                                 else
2831                                 {
2832                                         FloatAuto *previous = 0;
2833                                         FloatAuto *next = 0;
2834                                         float new_x = affected_x_autos->get_value(
2835                                                 track_position,
2836                                                 PLAY_FORWARD,
2837                                                 previous,
2838                                                 next);