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