f3fd68e330660b86d1d207f78ed20d2244c38a2c
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / cwindowtool.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008-2017 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include <stdio.h>
22 #include <stdint.h>
23
24 #include "automation.h"
25 #include "bccolors.h"
26 #include "bctimer.h"
27 #include "clip.h"
28 #include "condition.h"
29 #include "cpanel.h"
30 #include "cplayback.h"
31 #include "cwindow.h"
32 #include "cwindowgui.h"
33 #include "cwindowtool.h"
34 #include "edl.h"
35 #include "edlsession.h"
36 #include "file.h"
37 #include "filexml.h"
38 #include "floatauto.h"
39 #include "floatautos.h"
40 #include "gwindowgui.h"
41 #include "keys.h"
42 #include "language.h"
43 #include "localsession.h"
44 #include "mainsession.h"
45 #include "mainundo.h"
46 #include "maskauto.h"
47 #include "maskautos.h"
48 #include "mutex.h"
49 #include "mwindow.h"
50 #include "mwindowgui.h"
51 #include "theme.h"
52 #include "track.h"
53 #include "tracks.h"
54 #include "trackcanvas.h"
55 #include "transportque.h"
56 #include "zoombar.h"
57
58
59 CWindowTool::CWindowTool(MWindow *mwindow, CWindowGUI *gui)
60  : Thread(1, 0, 0)
61 {
62         this->mwindow = mwindow;
63         this->gui = gui;
64         tool_gui = 0;
65         done = 0;
66         current_tool = CWINDOW_NONE;
67         set_synchronous(1);
68         input_lock = new Condition(0, "CWindowTool::input_lock");
69         output_lock = new Condition(1, "CWindowTool::output_lock");
70         tool_gui_lock = new Mutex("CWindowTool::tool_gui_lock");
71 }
72
73 CWindowTool::~CWindowTool()
74 {
75         done = 1;
76         stop_tool();
77         input_lock->unlock();
78         Thread::join();
79         delete input_lock;
80         delete output_lock;
81         delete tool_gui_lock;
82 }
83
84 void CWindowTool::start_tool(int operation)
85 {
86         CWindowToolGUI *new_gui = 0;
87         int result = 0;
88
89 //printf("CWindowTool::start_tool 1\n");
90         if(current_tool != operation)
91         {
92                 int previous_tool = current_tool;
93                 current_tool = operation;
94                 switch(operation)
95                 {
96                         case CWINDOW_EYEDROP:
97                                 new_gui = new CWindowEyedropGUI(mwindow, this);
98                                 break;
99                         case CWINDOW_CROP:
100                                 new_gui = new CWindowCropGUI(mwindow, this);
101                                 break;
102                         case CWINDOW_CAMERA:
103                                 new_gui = new CWindowCameraGUI(mwindow, this);
104                                 break;
105                         case CWINDOW_PROJECTOR:
106                                 new_gui = new CWindowProjectorGUI(mwindow, this);
107                                 break;
108                         case CWINDOW_MASK:
109                                 new_gui = new CWindowMaskGUI(mwindow, this);
110                                 break;
111                         case CWINDOW_RULER:
112                                 new_gui = new CWindowRulerGUI(mwindow, this);
113                                 break;
114                         case CWINDOW_PROTECT:
115                                 mwindow->edl->session->tool_window = 0;
116                                 gui->composite_panel->operation[CWINDOW_TOOL_WINDOW]->update(0);
117                                 // fall thru
118                         default:
119                                 result = 1;
120                                 stop_tool();
121                                 break;
122                 }
123
124 //printf("CWindowTool::start_tool 1\n");
125
126
127                 if(!result)
128                 {
129                         stop_tool();
130 // Wait for previous tool GUI to finish
131                         output_lock->lock("CWindowTool::start_tool");
132                         this->tool_gui = new_gui;
133                         tool_gui->create_objects();
134                         if( previous_tool == CWINDOW_PROTECT || previous_tool == CWINDOW_NONE ) {
135                                 mwindow->edl->session->tool_window = 1;
136                                 gui->composite_panel->operation[CWINDOW_TOOL_WINDOW]->update(1);
137                         }
138                         mwindow->edl->session->tool_window = new_gui ? 1 : 0;
139                         update_show_window();
140
141 // Signal thread to run next tool GUI
142                         input_lock->unlock();
143                 }
144 //printf("CWindowTool::start_tool 1\n");
145         }
146         else
147         if(tool_gui)
148         {
149                 tool_gui->lock_window("CWindowTool::start_tool");
150                 tool_gui->update();
151                 tool_gui->unlock_window();
152         }
153
154 //printf("CWindowTool::start_tool 2\n");
155
156 }
157
158
159 void CWindowTool::stop_tool()
160 {
161         if(tool_gui)
162         {
163                 tool_gui->lock_window("CWindowTool::stop_tool");
164                 tool_gui->set_done(0);
165                 tool_gui->unlock_window();
166         }
167 }
168
169 void CWindowTool::show_tool()
170 {
171         if(tool_gui && mwindow->edl->session->tool_window)
172         {
173                 tool_gui->lock_window("CWindowTool::show_tool");
174                 tool_gui->show_window();
175                 tool_gui->unlock_window();
176         }
177 }
178
179 void CWindowTool::hide_tool()
180 {
181         if(tool_gui && mwindow->edl->session->tool_window)
182         {
183                 tool_gui->lock_window("CWindowTool::show_tool");
184                 tool_gui->hide_window();
185                 tool_gui->unlock_window();
186         }
187 }
188
189 void CWindowTool::raise_tool()
190 {
191         if(tool_gui && mwindow->edl->session->tool_window)
192         {
193                 tool_gui->lock_window("CWindowTool::show_tool");
194                 tool_gui->raise_window();
195                 tool_gui->unlock_window();
196         }
197 }
198
199
200 void CWindowTool::run()
201 {
202         while(!done)
203         {
204                 input_lock->lock("CWindowTool::run");
205                 if(!done)
206                 {
207                         tool_gui->run_window();
208                         tool_gui_lock->lock("CWindowTool::run");
209                         delete tool_gui;
210                         tool_gui = 0;
211                         tool_gui_lock->unlock();
212                 }
213                 output_lock->unlock();
214         }
215 }
216
217 void CWindowTool::update_show_window()
218 {
219         if(tool_gui)
220         {
221                 tool_gui->lock_window("CWindowTool::update_show_window");
222
223                 if(mwindow->edl->session->tool_window)
224                 {
225                         tool_gui->update();
226                         tool_gui->show_window();
227                 }
228                 else
229                         tool_gui->hide_window();
230                 tool_gui->flush();
231
232                 tool_gui->unlock_window();
233         }
234 }
235
236 void CWindowTool::raise_window()
237 {
238         if(tool_gui)
239         {
240                 gui->unlock_window();
241                 tool_gui->lock_window("CWindowTool::raise_window");
242                 tool_gui->raise_window();
243                 tool_gui->unlock_window();
244                 gui->lock_window("CWindowTool::raise_window");
245         }
246 }
247
248 void CWindowTool::update_values()
249 {
250         tool_gui_lock->lock("CWindowTool::update_values");
251         if(tool_gui)
252         {
253                 tool_gui->lock_window("CWindowTool::update_values");
254                 tool_gui->update();
255                 tool_gui->flush();
256                 tool_gui->unlock_window();
257         }
258         tool_gui_lock->unlock();
259 }
260
261
262
263
264
265
266
267 CWindowToolGUI::CWindowToolGUI(MWindow *mwindow,
268         CWindowTool *thread, const char *title, int w, int h)
269  : BC_Window(title,
270                 mwindow->session->ctool_x, mwindow->session->ctool_y,
271                 w, h, w, h, 0, 0, 1)
272 {
273         this->mwindow = mwindow;
274         this->thread = thread;
275         current_operation = 0;
276 }
277
278 CWindowToolGUI::~CWindowToolGUI()
279 {
280 }
281
282 int CWindowToolGUI::close_event()
283 {
284         hide_window();
285         flush();
286         mwindow->edl->session->tool_window = 0;
287         unlock_window();
288         thread->gui->lock_window("CWindowToolGUI::close_event");
289         thread->gui->composite_panel->set_operation(mwindow->edl->session->cwindow_operation);
290         thread->gui->flush();
291         thread->gui->unlock_window();
292         lock_window("CWindowToolGUI::close_event");
293         return 1;
294 }
295
296 int CWindowToolGUI::keypress_event()
297 {
298         int result = 0;
299
300         switch( get_keypress() ) {
301         case 'w':
302         case 'W':
303                 return close_event();
304         case KEY_F1:
305         case KEY_F2:
306         case KEY_F3:
307         case KEY_F4:
308         case KEY_F5:
309         case KEY_F6:
310         case KEY_F7:
311         case KEY_F8:
312         case KEY_F9:
313         case KEY_F10:
314         case KEY_F11:
315         case KEY_F12:
316                 resend_event(thread->gui);
317                 result = 1;
318         }
319
320         return result;
321 }
322
323 int CWindowToolGUI::translation_event()
324 {
325         mwindow->session->ctool_x = get_x();
326         mwindow->session->ctool_y = get_y();
327         return 0;
328 }
329
330
331 void CWindowToolGUI::update_preview(int changed_edl)
332 {
333         unlock_window();
334         draw_preview(changed_edl);
335         lock_window("CWindowToolGUI::update_preview");
336 }
337
338 void CWindowToolGUI::draw_preview(int changed_edl)
339 {
340         CWindowGUI *cgui = mwindow->cwindow->gui;
341         cgui->lock_window("CWindowToolGUI::draw_preview");
342         int change_type = !changed_edl ? CHANGE_PARAMS : CHANGE_EDL;
343         cgui->sync_parameters(change_type, 0, 1);
344         cgui->unlock_window();
345 }
346
347
348 CWindowCoord::CWindowCoord(CWindowToolGUI *gui, int x, int y, float value, int type)
349  : BC_TumbleTextBox(gui, (float)value, (float)-65536, (float)65536, x, y, xS(70), 3)
350 {
351         this->gui = gui;
352         this->type = type;
353         slider = 0;
354         range = 0;
355 }
356
357 CWindowCoord::CWindowCoord(CWindowToolGUI *gui, int x, int y, int value, int type)
358  : BC_TumbleTextBox(gui, (int64_t)value, (int64_t)-65536, (int64_t)65536, x, y, xS(70))
359 {
360         this->gui = gui;
361         this->type = type;
362         slider = 0;
363         range = 0;
364 }
365
366 void CWindowCoord::create_objects()
367 {
368         BC_TumbleTextBox::create_objects();
369         if( type >= 0 ) {
370                 int x1 = get_x() + BC_TumbleTextBox::get_w() + xS(10), y1 = get_y();
371                 int group = Automation::autogrouptype(type, 0);
372                 float min = gui->mwindow->edl->local_session->automation_mins[group];
373                 float max = gui->mwindow->edl->local_session->automation_maxs[group];
374                 float v = atof(get_text());
375                 gui->add_subwindow(slider = new CWindowCoordSlider(this,
376                                 x1, y1, xS(150), min, max, v));
377                 x1 += slider->get_w() + xS(5);
378                 gui->add_subwindow(range = new CWindowCoordRange(this, x1, y1));
379         }
380 }
381
382 void CWindowCoord::update_gui(float value)
383 {
384         BC_TumbleTextBox::update(value);
385         if( slider ) {
386                 int group = Automation::autogrouptype(type, 0);
387                 LocalSession *local_session = gui->mwindow->edl->local_session;
388                 slider->update(slider->get_pointer_motion_range(), value,
389                         local_session->automation_mins[group],
390                         local_session->automation_maxs[group]);
391                 int x1 = range->get_x() + range->get_w() + xS(5);
392                 int y1 = range->get_y() - yS(2), d = xS(16);
393                 gui->set_color(GWindowGUI::auto_colors[type]);
394                 gui->draw_disc(x1, y1, d, d);
395         }
396 }
397
398 int CWindowCoord::handle_event()
399 {
400         if( slider )
401                 slider->update(atof(get_text()));
402         gui->event_caller = this;
403         gui->handle_event();
404         return 1;
405 }
406
407 CWindowCoordSlider::CWindowCoordSlider(CWindowCoord *coord,
408                 int x, int y, int w, float mn, float mx, float value)
409  : BC_FSlider(x, y, 0, w, w, mn, mx, value)
410 {
411         this->coord = coord;
412         set_precision(0.01);
413 }
414
415 CWindowCoordSlider::~CWindowCoordSlider()
416 {
417 }
418
419 int CWindowCoordSlider::handle_event()
420 {
421         float value = get_value();
422         coord->update(value);
423         coord->gui->event_caller = coord;
424         coord->gui->handle_event();
425         return 1;
426 }
427
428 CWindowCoordRange::CWindowCoordRange(CWindowCoord *coord, int x, int y)
429  : BC_Tumbler(x, y)
430 {
431         this->coord = coord;
432 }
433 CWindowCoordRange::~CWindowCoordRange()
434 {
435 }
436
437 int CWindowCoordRange::update(float scale)
438 {
439         CWindowCoordSlider *slider = coord->slider;
440         MWindow *mwindow = coord->gui->mwindow;
441         LocalSession *local_session = mwindow->edl->local_session;
442         int group = Automation::autogrouptype(coord->type, 0);
443         float min = local_session->automation_mins[group];
444         float max = local_session->automation_maxs[group];
445         if( min >= max ) {
446                 switch( group ) {
447                 case AUTOGROUPTYPE_ZOOM: min = 0.005;  max = 5.0;  break;
448                 case AUTOGROUPTYPE_X:    min = -100;   max = 100;  break;
449                 case AUTOGROUPTYPE_Y:    min = -100;   max = 100;  break;
450                 }
451         }
452         switch( group ) {
453         case AUTOGROUPTYPE_ZOOM: { // exp
454                 float lv = log(slider->get_value());
455                 float lmin = log(min), lmax = log(max);
456                 float lr = (lmax - lmin) * scale;
457                 min = exp(lv - 0.5*lr);
458                 max = exp(lv + 0.5*lr);
459                 if( min < 0.001 ) min = 0.001;
460                 if( max > 1000. ) max = 1000.;
461                 break; }
462         case AUTOGROUPTYPE_X:
463         case AUTOGROUPTYPE_Y: { // linear
464                 float dr = (max - min) * (scale-1);
465                 if( (min -= dr) < -32767 ) min = -32767;
466                 if( (max += dr) >  32767 ) max =  32767;
467                 break; }
468         }
469         slider->update(slider->get_pointer_motion_range(),
470                         slider->get_value(), min, max);
471         unlock_window();
472         MWindowGUI *mgui = mwindow->gui;
473         mgui->lock_window("CWindowCoordRange::update");
474         local_session->zoombar_showautotype = group;
475         local_session->automation_mins[group] = min;
476         local_session->automation_maxs[group] = max;
477         mgui->zoombar->update_autozoom();
478         mgui->draw_overlays(0);
479         mgui->update_patchbay();
480         mgui->flash_canvas(1);
481         mgui->unlock_window();
482         lock_window("CWindowCoordRange::update");
483         return 1;
484 }
485
486 int CWindowCoordRange::handle_up_event()
487 {
488         return update(1.25);
489 }
490 int CWindowCoordRange::handle_down_event()
491 {
492         return update(0.8);
493 }
494
495 CWindowCropApply::CWindowCropApply(MWindow *mwindow, CWindowCropGUI *crop_gui, int x, int y)
496  : BC_GenericButton(x, y, _("Apply"))
497 {
498         this->mwindow = mwindow;
499         this->crop_gui = crop_gui;
500 }
501 int CWindowCropApply::handle_event()
502 {
503         mwindow->crop_video(crop_gui->crop_mode->mode);
504         return 1;
505 }
506
507
508 int CWindowCropApply::keypress_event()
509 {
510         if(get_keypress() == 0xd)
511         {
512                 handle_event();
513                 return 1;
514         }
515         return 0;
516 }
517
518 const char *CWindowCropOpMode::crop_ops[] = {
519         N_("Reformat"),
520         N_("Resize"),
521         N_("Shrink"),
522 };
523
524 CWindowCropOpMode::CWindowCropOpMode(MWindow *mwindow, CWindowCropGUI *crop_gui,
525                         int mode, int x, int y)
526  : BC_PopupMenu(x, y, xS(140), _(crop_ops[mode]), 1)
527 {
528         this->mwindow = mwindow;
529         this->crop_gui = crop_gui;
530         this->mode = mode;
531 }
532 CWindowCropOpMode::~CWindowCropOpMode()
533 {
534 }
535
536 void CWindowCropOpMode::create_objects()
537 {
538         for( int id=0,nid=sizeof(crop_ops)/sizeof(crop_ops[0]); id<nid; ++id )
539                 add_item(new CWindowCropOpItem(this, _(crop_ops[id]), id));
540         handle_event();
541 }
542
543 int CWindowCropOpMode::handle_event()
544 {
545         set_text(_(crop_ops[mode]));
546         return 1;
547 }
548
549 CWindowCropOpItem::CWindowCropOpItem(CWindowCropOpMode *popup, const char *text, int id)
550  : BC_MenuItem(text)
551 {
552         this->popup = popup;
553         this->id = id;
554 }
555
556 int CWindowCropOpItem::handle_event()
557 {
558         popup->set_text(get_text());
559         popup->mode = id;
560         return popup->handle_event();
561 }
562
563
564
565
566
567 CWindowCropGUI::CWindowCropGUI(MWindow *mwindow, CWindowTool *thread)
568  : CWindowToolGUI(mwindow, thread, _(PROGRAM_NAME ": Crop"), xS(330), yS(100))
569 {
570 }
571
572
573 CWindowCropGUI::~CWindowCropGUI()
574 {
575 }
576
577 void CWindowCropGUI::create_objects()
578 {
579         int xs5 = xS(5), xs10 = xS(10);
580         int ys5 = yS(5), ys10 = yS(10);
581         int x = xs10, y = ys10;
582         BC_Title *title;
583
584         lock_window("CWindowCropGUI::create_objects");
585         int column1 = 0;
586         int pad = MAX(BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1),
587                 BC_Title::calculate_h(this, "X")) + ys5;
588         add_subwindow(title = new BC_Title(x, y, "X1:"));
589         column1 = MAX(column1, title->get_w());
590         y += pad;
591         add_subwindow(title = new BC_Title(x, y, _("W:")));
592         column1 = MAX(column1, title->get_w());
593         y += pad;
594         add_subwindow(new CWindowCropApply(mwindow, this, x, y));
595
596         x += column1 + xs5;
597         y = ys10;
598         x1 = new CWindowCoord(thread->tool_gui, x, y,
599                 mwindow->edl->session->crop_x1);
600         x1->create_objects();
601         x1->set_boundaries((int64_t)0, (int64_t)65536);
602         y += pad;
603         width = new CWindowCoord(thread->tool_gui, x, y,
604                 mwindow->edl->session->crop_x2 - mwindow->edl->session->crop_x1);
605         width->create_objects();
606         width->set_boundaries((int64_t)1, (int64_t)65536);
607
608
609         x += x1->get_w() + xs10;
610         y = ys10;
611         int column2 = 0;
612         add_subwindow(title = new BC_Title(x, y, "Y1:"));
613         column2 = MAX(column2, title->get_w());
614         y += pad;
615         add_subwindow(title = new BC_Title(x, y, _("H:")));
616         column2 = MAX(column2, title->get_w());
617         y += pad;
618
619         y = ys10;
620         x += column2 + xs5;
621         y1 = new CWindowCoord(thread->tool_gui, x, y,
622                 mwindow->edl->session->crop_y1);
623         y1->create_objects();
624         y1->set_boundaries((int64_t)0, (int64_t)65536);
625         y += pad;
626
627         height = new CWindowCoord(thread->tool_gui, x, y,
628                 mwindow->edl->session->crop_y2 - mwindow->edl->session->crop_y1);
629         height->create_objects();
630         height->set_boundaries((int64_t)1, (int64_t)65536);
631         y += pad;
632
633         add_subwindow(crop_mode = new CWindowCropOpMode(mwindow, this,
634                                 CROP_REFORMAT, x, y));
635         crop_mode->create_objects();
636
637         unlock_window();
638 }
639
640 void CWindowCropGUI::handle_event()
641 {
642         int new_x1, new_y1;
643         new_x1 = atol(x1->get_text());
644         new_y1 = atol(y1->get_text());
645         if(new_x1 != mwindow->edl->session->crop_x1)
646         {
647                 mwindow->edl->session->crop_x2 = new_x1 +
648                         mwindow->edl->session->crop_x2 -
649                         mwindow->edl->session->crop_x1;
650                 mwindow->edl->session->crop_x1 = new_x1;
651         }
652         if(new_y1 != mwindow->edl->session->crop_y1)
653         {
654                 mwindow->edl->session->crop_y2 = new_y1 +
655                         mwindow->edl->session->crop_y2 -
656                         mwindow->edl->session->crop_y1;
657                 mwindow->edl->session->crop_y1 = atol(y1->get_text());
658         }
659         mwindow->edl->session->crop_x2 = atol(width->get_text()) +
660                 mwindow->edl->session->crop_x1;
661         mwindow->edl->session->crop_y2 = atol(height->get_text()) +
662                 mwindow->edl->session->crop_y1;
663         update();
664         mwindow->cwindow->gui->canvas->redraw(1);
665 }
666
667 void CWindowCropGUI::update()
668 {
669         x1->update((int64_t)mwindow->edl->session->crop_x1);
670         y1->update((int64_t)mwindow->edl->session->crop_y1);
671         width->update((int64_t)mwindow->edl->session->crop_x2 -
672                 mwindow->edl->session->crop_x1);
673         height->update((int64_t)mwindow->edl->session->crop_y2 -
674                 mwindow->edl->session->crop_y1);
675 }
676
677
678 CWindowEyedropGUI::CWindowEyedropGUI(MWindow *mwindow, CWindowTool *thread)
679  : CWindowToolGUI(mwindow, thread, _(PROGRAM_NAME ": Color"), xS(220), yS(290))
680 {
681 }
682
683 CWindowEyedropGUI::~CWindowEyedropGUI()
684 {
685 }
686
687 void CWindowEyedropGUI::create_objects()
688 {
689         int xs10 = xS(10), ys10 = yS(10);
690         int margin = mwindow->theme->widget_border;
691         int x = xs10 + margin;
692         int y = ys10 + margin;
693         int x2 = xS(70), x3 = x2 + xS(60);
694         lock_window("CWindowEyedropGUI::create_objects");
695         BC_Title *title0, *title1, *title2, *title3, *title4, *title5, *title6, *title7;
696         add_subwindow(title0 = new BC_Title(x, y,_("X,Y:")));
697         y += title0->get_h() + margin;
698         add_subwindow(title7 = new BC_Title(x, y, _("Radius:")));
699         y += BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) + margin;
700
701         add_subwindow(title1 = new BC_Title(x, y, _("Red:")));
702         y += title1->get_h() + margin;
703         add_subwindow(title2 = new BC_Title(x, y, _("Green:")));
704         y += title2->get_h() + margin;
705         add_subwindow(title3 = new BC_Title(x, y, _("Blue:")));
706         y += title3->get_h() + margin;
707
708         add_subwindow(title4 = new BC_Title(x, y, "Y:"));
709         y += title4->get_h() + margin;
710         add_subwindow(title5 = new BC_Title(x, y, "U:"));
711         y += title5->get_h() + margin;
712         add_subwindow(title6 = new BC_Title(x, y, "V:"));
713
714         add_subwindow(current = new BC_Title(x2, title0->get_y(), ""));
715
716         radius = new CWindowCoord(this, x2, title7->get_y(),
717                 mwindow->edl->session->eyedrop_radius);
718         radius->create_objects();
719         radius->set_boundaries((int64_t)0, (int64_t)255);
720
721         add_subwindow(red = new BC_Title(x2, title1->get_y(), "0"));
722         add_subwindow(green = new BC_Title(x2, title2->get_y(), "0"));
723         add_subwindow(blue = new BC_Title(x2, title3->get_y(), "0"));
724         add_subwindow(rgb_hex = new BC_Title(x3, red->get_y(), "#000000"));
725
726         add_subwindow(this->y = new BC_Title(x2, title4->get_y(), "0"));
727         add_subwindow(this->u = new BC_Title(x2, title5->get_y(), "0"));
728         add_subwindow(this->v = new BC_Title(x2, title6->get_y(), "0"));
729         add_subwindow(yuv_hex = new BC_Title(x3, this->y->get_y(), "#000000"));
730
731         y = title6->get_y() + this->v->get_h() + 2*margin;
732         add_subwindow(sample = new BC_SubWindow(x, y, xS(50), yS(50)));
733         y += sample->get_h() + margin;
734         add_subwindow(use_max = new CWindowEyedropCheckBox(mwindow, this, x, y));
735         update();
736         unlock_window();
737 }
738
739 void CWindowEyedropGUI::update()
740 {
741         char string[BCTEXTLEN];
742         sprintf(string, "%d, %d",
743                 thread->gui->eyedrop_x,
744                 thread->gui->eyedrop_y);
745         current->update(string);
746
747         radius->update((int64_t)mwindow->edl->session->eyedrop_radius);
748
749         LocalSession *local_session = mwindow->edl->local_session;
750         int use_max = local_session->use_max;
751         float r = use_max ? local_session->red_max : local_session->red;
752         float g = use_max ? local_session->green_max : local_session->green;
753         float b = use_max ? local_session->blue_max : local_session->blue;
754         this->red->update(r);
755         this->green->update(g);
756         this->blue->update(b);
757
758         int rx = 255*r + 0.5;  bclamp(rx,0,255);
759         int gx = 255*g + 0.5;  bclamp(gx,0,255);
760         int bx = 255*b + 0.5;  bclamp(bx,0,255);
761         char rgb_text[BCSTRLEN];
762         sprintf(rgb_text, "#%02x%02x%02x", rx, gx, bx);
763         rgb_hex->update(rgb_text);
764         
765         float y, u, v;
766         YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v);
767         this->y->update(y);
768         this->u->update(u += 0.5);
769         this->v->update(v += 0.5);
770
771         int yx = 255*y + 0.5;  bclamp(yx,0,255);
772         int ux = 255*u + 0.5;  bclamp(ux,0,255);
773         int vx = 255*v + 0.5;  bclamp(vx,0,255);
774         char yuv_text[BCSTRLEN];
775         sprintf(yuv_text, "#%02x%02x%02x", yx, ux, vx);
776         yuv_hex->update(yuv_text);
777
778         int rgb = (rx << 16) | (gx << 8) | (bx << 0);
779         sample->set_color(rgb);
780         sample->draw_box(0, 0, sample->get_w(), sample->get_h());
781         sample->set_color(BLACK);
782         sample->draw_rectangle(0, 0, sample->get_w(), sample->get_h());
783         sample->flash();
784 }
785
786 void CWindowEyedropGUI::handle_event()
787 {
788         int new_radius = atoi(radius->get_text());
789         if(new_radius != mwindow->edl->session->eyedrop_radius)
790         {
791                 CWindowGUI *gui = mwindow->cwindow->gui;
792                 if(gui->eyedrop_visible)
793                 {
794                         gui->lock_window("CWindowEyedropGUI::handle_event");
795 // hide it
796                         int rerender;
797                         gui->canvas->do_eyedrop(rerender, 0, 1);
798                 }
799
800                 mwindow->edl->session->eyedrop_radius = new_radius;
801
802                 if(gui->eyedrop_visible)
803                 {
804 // draw it
805                         int rerender;
806                         gui->canvas->do_eyedrop(rerender, 0, 1);
807                         gui->unlock_window();
808                 }
809         }
810 }
811
812
813
814 /* Buttons to control Keyframe-Curve-Mode for Projector or Camera */
815
816 // Configuration for all possible Keyframe Curve Mode toggles
817 struct _CVD {
818         FloatAuto::t_mode mode;
819         bool use_camera;
820         const char* icon_id;
821         const char* tooltip;
822 };
823
824 const _CVD Camera_Crv_Smooth =
825         {       FloatAuto::SMOOTH,
826                 true,
827                 "tan_smooth",
828                 N_("\"smooth\" Curve on current Camera Keyframes")
829         };
830 const _CVD Camera_Crv_Linear =
831         {       FloatAuto::LINEAR,
832                 true,
833                 "tan_linear",
834                 N_("\"linear\" Curve on current Camera Keyframes")
835         };
836 const _CVD Projector_Crv_Smooth =
837         {       FloatAuto::SMOOTH,
838                 false,
839                 "tan_smooth",
840                 N_("\"smooth\" Curve on current Projector Keyframes")
841         };
842 const _CVD Projector_Crv_Linear =
843         {       FloatAuto::LINEAR,
844                 false,
845                 "tan_linear",
846                 N_("\"linear\" Curve on current Projector Keyframes")
847         };
848
849 // Implementation Class für Keyframe Curve Mode buttons
850 //
851 // This button reflects the state of the "current" keyframe
852 // (the nearest keyframe on the left) for all three automation
853 // lines together. Clicking on this button (re)sets the curve
854 // mode for the three "current" keyframes simultanously, but
855 // never creates a new keyframe.
856 //
857 class CWindowCurveToggle : public BC_Toggle
858 {
859 public:
860         CWindowCurveToggle(_CVD mode, MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
861         void check_toggle_state(FloatAuto *x, FloatAuto *y, FloatAuto *z);
862         int handle_event();
863 private:
864         _CVD cfg;
865         MWindow *mwindow;
866         CWindowToolGUI *gui;
867 };
868
869
870 CWindowCurveToggle::CWindowCurveToggle(_CVD mode, MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
871  : BC_Toggle(x, y, mwindow->theme->get_image_set(mode.icon_id), false),
872    cfg(mode)
873 {
874         this->gui = gui;
875         this->mwindow = mwindow;
876         set_tooltip(_(cfg.tooltip));
877 }
878
879 void CWindowCurveToggle::check_toggle_state(FloatAuto *x, FloatAuto *y, FloatAuto *z)
880 {
881 // the toggle state is only set to ON if all
882 // three automation lines have the same curve mode.
883 // For mixed states the toggle stays off.
884         set_value( x->curve_mode == this->cfg.mode &&
885                    y->curve_mode == this->cfg.mode &&
886                    z->curve_mode == this->cfg.mode
887                    ,true // redraw to show new state
888                 );
889 }
890
891 int CWindowCurveToggle::handle_event()
892 {
893         Track *track = mwindow->cwindow->calculate_affected_track();
894         if(track) {
895                 FloatAuto *x=0, *y=0, *z=0;
896                 mwindow->cwindow->calculate_affected_autos(track,
897                         &x, &y, &z, cfg.use_camera, 0,0,0); // don't create new keyframe
898                 if( x ) x->change_curve_mode(cfg.mode);
899                 if( y ) y->change_curve_mode(cfg.mode);
900                 if( z ) z->change_curve_mode(cfg.mode);
901
902                 gui->update();
903                 gui->update_preview();
904         }
905
906         return 1;
907 }
908
909
910 CWindowEyedropCheckBox::CWindowEyedropCheckBox(MWindow *mwindow,
911         CWindowEyedropGUI *gui, int x, int y)
912  : BC_CheckBox(x, y, mwindow->edl->local_session->use_max, _("Use maximum"))
913 {
914         this->mwindow = mwindow;
915         this->gui = gui;
916 }
917
918 int CWindowEyedropCheckBox::handle_event()
919 {
920         mwindow->edl->local_session->use_max = get_value();
921         
922         gui->update();
923         return 1;
924 }
925
926
927 CWindowCameraGUI::CWindowCameraGUI(MWindow *mwindow, CWindowTool *thread)
928  : CWindowToolGUI(mwindow, thread, _(PROGRAM_NAME ": Camera"), xS(340), yS(170))
929 {
930 }
931 CWindowCameraGUI::~CWindowCameraGUI()
932 {
933 }
934
935 void CWindowCameraGUI::create_objects()
936 {
937         int xs10 = xS(10), xs15 = xS(15);
938         int ys10 = yS(10), ys30 = yS(30);
939         int x = xs10, y = ys10;
940         Track *track = mwindow->cwindow->calculate_affected_track();
941         FloatAuto *x_auto = 0, *y_auto = 0, *z_auto = 0;
942         BC_Title *title;
943         BC_Button *button;
944
945         lock_window("CWindowCameraGUI::create_objects");
946         if( track ) {
947                 mwindow->cwindow->calculate_affected_autos(track,
948                         &x_auto, &y_auto, &z_auto, 1, 0, 0, 0);
949         }
950
951         add_subwindow(title = new BC_Title(x, y, "X:"));
952         int x1 = x + title->get_w() + xS(3);
953         float xvalue = x_auto ? x_auto->get_value() : 0;
954         this->x = new CWindowCoord(this, x1, y, xvalue, AUTOMATION_CAMERA_X);
955         this->x->create_objects();
956         this->x->range->set_tooltip(_("expand X range"));
957         y += ys30;
958         add_subwindow(title = new BC_Title(x = xs10, y, "Y:"));
959         float yvalue = y_auto ? y_auto->get_value() : 0;
960         this->y = new CWindowCoord(this, x1, y, yvalue, AUTOMATION_CAMERA_Y);
961         this->y->create_objects();
962         this->y->range->set_tooltip(_("expand Y range"));
963         y += ys30;
964         add_subwindow(title = new BC_Title(x = xs10, y, "Z:"));
965         x += title->get_w();
966         float zvalue = z_auto ? z_auto->get_value() : 1;
967         this->z = new CWindowCoord(this, x1, y, zvalue, AUTOMATION_CAMERA_Z);
968         this->z->create_objects();
969         this->z->set_increment(0.01);
970         this->z->range->set_tooltip(_("expand Zoom range"));
971
972         y += ys30;
973         x1 = xs10;
974         add_subwindow(button = new CWindowCameraLeft(mwindow, this, x1, y));
975         x1 += button->get_w();
976         add_subwindow(button = new CWindowCameraCenter(mwindow, this, x1, y));
977         x1 += button->get_w();
978         add_subwindow(button = new CWindowCameraRight(mwindow, this, x1, y));
979 // additional Buttons to control the curve mode of the "current" keyframe
980         x1 += button->get_w() + xs15;
981         add_subwindow(this->t_smooth = new CWindowCurveToggle(Camera_Crv_Smooth, mwindow, this, x1, y));
982         x1 += button->get_w() + xs10;
983         add_subwindow(this->t_linear = new CWindowCurveToggle(Camera_Crv_Linear, mwindow, this, x1, y));
984
985         y += button->get_h();
986         x1 = xs10;
987         add_subwindow(button = new CWindowCameraTop(mwindow, this, x1, y));
988         x1 += button->get_w();
989         add_subwindow(button = new CWindowCameraMiddle(mwindow, this, x1, y));
990         x1 += button->get_w();
991         add_subwindow(button = new CWindowCameraBottom(mwindow, this, x1, y));
992         x1 += button->get_w() + xs15;
993         add_subwindow(this->add_keyframe = new CWindowCameraAddKeyframe(mwindow, this, x1, y));
994         x1 += button->get_w() + xs10;
995         add_subwindow(this->reset = new CWindowCameraReset(mwindow, this, x1, y));
996
997 // fill in current auto keyframe values, set toggle states.
998         this->update();
999         unlock_window();
1000 }
1001
1002 void CWindowCameraGUI::handle_event()
1003 {
1004         FloatAuto *x_auto = 0;
1005         FloatAuto *y_auto = 0;
1006         FloatAuto *z_auto = 0;
1007         Track *track = mwindow->cwindow->calculate_affected_track();
1008         if(track)
1009         {
1010                 mwindow->undo->update_undo_before(_("camera"), this);
1011                 if(event_caller == x)
1012                 {
1013                         x_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1014                                 track->automation->autos[AUTOMATION_CAMERA_X], 1);
1015                         if(x_auto)
1016                         {
1017                                 x_auto->set_value(atof(x->get_text()));
1018                                 update();
1019                                 update_preview();
1020                         }
1021                 }
1022                 else
1023                 if(event_caller == y)
1024                 {
1025                         y_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1026                                 track->automation->autos[AUTOMATION_CAMERA_Y], 1);
1027                         if(y_auto)
1028                         {
1029                                 y_auto->set_value(atof(y->get_text()));
1030                                 update();
1031                                 update_preview();
1032                         }
1033                 }
1034                 else
1035                 if(event_caller == z)
1036                 {
1037                         z_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1038                                 track->automation->autos[AUTOMATION_CAMERA_Z], 1);
1039                         if(z_auto)
1040                         {
1041                                 float zoom = atof(z->get_text());
1042                                 if(zoom > 100.) zoom = 100.;
1043                                 else
1044                                 if(zoom < 0.01) zoom = 0.01;
1045         // Doesn't allow user to enter from scratch
1046         //              if(zoom != atof(z->get_text()))
1047         //                      z->update(zoom);
1048
1049                                 z_auto->set_value(zoom);
1050                                 mwindow->gui->lock_window("CWindowCameraGUI::handle_event");
1051                                 mwindow->gui->draw_overlays(1);
1052                                 mwindow->gui->unlock_window();
1053                                 update();
1054                                 update_preview();
1055                         }
1056                 }
1057
1058                 mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
1059         }
1060 }
1061
1062 void CWindowCameraGUI::update()
1063 {
1064         FloatAuto *x_auto = 0;
1065         FloatAuto *y_auto = 0;
1066         FloatAuto *z_auto = 0;
1067         Track *track = mwindow->cwindow->calculate_affected_track();
1068         if( track ) {
1069                 mwindow->cwindow->calculate_affected_autos(track,
1070                         &x_auto, &y_auto, &z_auto, 1, 0, 0, 0);
1071         }
1072
1073         if( x_auto ) {
1074                 float xvalue = x_auto->get_value();
1075                 x->update_gui(xvalue);
1076         }
1077         if( y_auto ) {
1078                 float yvalue = y_auto->get_value();
1079                 y->update_gui(yvalue);
1080         }
1081         if( z_auto ) {
1082                 float zvalue = z_auto->get_value();
1083                 z->update_gui(zvalue);
1084                 thread->gui->lock_window("CWindowCameraGUI::update");
1085                 thread->gui->composite_panel->cpanel_zoom->update(zvalue);
1086                 thread->gui->unlock_window();
1087         }
1088
1089         if( x_auto && y_auto && z_auto ) {
1090                 t_smooth->check_toggle_state(x_auto, y_auto, z_auto);
1091                 t_linear->check_toggle_state(x_auto, y_auto, z_auto);
1092         }
1093 }
1094
1095
1096
1097
1098 CWindowCameraLeft::CWindowCameraLeft(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
1099  : BC_Button(x, y, mwindow->theme->get_image_set("left_justify"))
1100 {
1101         this->gui = gui;
1102         this->mwindow = mwindow;
1103         set_tooltip(_("Left justify"));
1104 }
1105 int CWindowCameraLeft::handle_event()
1106 {
1107         FloatAuto *x_auto = 0;
1108         FloatAuto *z_auto = 0;
1109         Track *track = mwindow->cwindow->calculate_affected_track();
1110         if( track ) {
1111                 mwindow->cwindow->calculate_affected_autos(track,
1112                         &x_auto, 0, &z_auto, 1, 1, 0, 0);
1113         }
1114
1115         if(x_auto && z_auto)
1116         {
1117                 int w = 0, h = 0;
1118                 track->get_source_dimensions(
1119                         mwindow->edl->local_session->get_selectionstart(1),
1120                         w, h);
1121
1122                 if(w && h)
1123                 {
1124                         mwindow->undo->update_undo_before(_("camera"), 0);
1125                         x_auto->set_value(
1126                                 (double)track->track_w / z_auto->get_value() / 2 -
1127                                 (double)w / 2);
1128                         mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
1129                         gui->update();
1130                         gui->update_preview();
1131                 }
1132         }
1133
1134         return 1;
1135 }
1136
1137
1138 CWindowCameraCenter::CWindowCameraCenter(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
1139  : BC_Button(x, y, mwindow->theme->get_image_set("center_justify"))
1140 {
1141         this->gui = gui;
1142         this->mwindow = mwindow;
1143         set_tooltip(_("Center horizontal"));
1144 }
1145 int CWindowCameraCenter::handle_event()
1146 {
1147         FloatAuto *x_auto = 0;
1148         Track *track = mwindow->cwindow->calculate_affected_track();
1149         if(track)
1150                 x_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1151                         track->automation->autos[AUTOMATION_CAMERA_X], 1);
1152
1153         if(x_auto)
1154         {
1155                 mwindow->undo->update_undo_before(_("camera"), 0);
1156                 x_auto->set_value(0);
1157                 gui->update();
1158                 gui->update_preview();
1159                 mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
1160         }
1161
1162         return 1;
1163 }
1164
1165
1166 CWindowCameraRight::CWindowCameraRight(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
1167  : BC_Button(x, y, mwindow->theme->get_image_set("right_justify"))
1168 {
1169         this->gui = gui;
1170         this->mwindow = mwindow;
1171         set_tooltip(_("Right justify"));
1172 }
1173 int CWindowCameraRight::handle_event()
1174 {
1175         FloatAuto *x_auto = 0;
1176         FloatAuto *z_auto = 0;
1177         Track *track = mwindow->cwindow->calculate_affected_track();
1178         if( track ) {
1179                 mwindow->cwindow->calculate_affected_autos(track,
1180                         &x_auto, 0, &z_auto, 1, 1, 0, 0);
1181         }
1182
1183         if(x_auto && z_auto)
1184         {
1185                 int w = 0, h = 0;
1186                 track->get_source_dimensions(
1187                         mwindow->edl->local_session->get_selectionstart(1),
1188                         w, h);
1189
1190                 if(w && h)
1191                 {
1192                         mwindow->undo->update_undo_before(_("camera"), 0);
1193                         x_auto->set_value( -((double)track->track_w / z_auto->get_value() / 2 -
1194                                 (double)w / 2));
1195                         gui->update();
1196                         gui->update_preview();
1197                         mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
1198                 }
1199         }
1200
1201         return 1;
1202 }
1203
1204
1205 CWindowCameraTop::CWindowCameraTop(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
1206  : BC_Button(x, y, mwindow->theme->get_image_set("top_justify"))
1207 {
1208         this->gui = gui;
1209         this->mwindow = mwindow;
1210         set_tooltip(_("Top justify"));
1211 }
1212 int CWindowCameraTop::handle_event()
1213 {
1214         FloatAuto *y_auto = 0;
1215         FloatAuto *z_auto = 0;
1216         Track *track = mwindow->cwindow->calculate_affected_track();
1217         if( track ) {
1218                 mwindow->cwindow->calculate_affected_autos(track,
1219                         0, &y_auto, &z_auto, 1, 0, 1, 0);
1220         }
1221
1222         if(y_auto && z_auto)
1223         {
1224                 int w = 0, h = 0;
1225                 track->get_source_dimensions(
1226                         mwindow->edl->local_session->get_selectionstart(1),
1227                         w, h);
1228
1229                 if(w && h)
1230                 {
1231                         mwindow->undo->update_undo_before(_("camera"), 0);
1232                         y_auto->set_value((double)track->track_h / z_auto->get_value() / 2 -
1233                                 (double)h / 2);
1234                         gui->update();
1235                         gui->update_preview();
1236                         mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
1237                 }
1238         }
1239
1240         return 1;
1241 }
1242
1243
1244 CWindowCameraMiddle::CWindowCameraMiddle(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
1245  : BC_Button(x, y, mwindow->theme->get_image_set("middle_justify"))
1246 {
1247         this->gui = gui;
1248         this->mwindow = mwindow;
1249         set_tooltip(_("Center vertical"));
1250 }
1251 int CWindowCameraMiddle::handle_event()
1252 {
1253         FloatAuto *y_auto = 0;
1254         Track *track = mwindow->cwindow->calculate_affected_track();
1255         if(track)
1256                 y_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1257                         track->automation->autos[AUTOMATION_CAMERA_Y], 1);
1258
1259         if(y_auto)
1260         {
1261                 mwindow->undo->update_undo_before(_("camera"), 0);
1262                 y_auto->set_value(0);
1263                 gui->update();
1264                 gui->update_preview();
1265                 mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
1266         }
1267
1268         return 1;
1269 }
1270
1271
1272 CWindowCameraBottom::CWindowCameraBottom(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
1273  : BC_Button(x, y, mwindow->theme->get_image_set("bottom_justify"))
1274 {
1275         this->gui = gui;
1276         this->mwindow = mwindow;
1277         set_tooltip(_("Bottom justify"));
1278 }
1279 int CWindowCameraBottom::handle_event()
1280 {
1281         FloatAuto *y_auto = 0;
1282         FloatAuto *z_auto = 0;
1283         Track *track = mwindow->cwindow->calculate_affected_track();
1284         if( track ) {
1285                 mwindow->cwindow->calculate_affected_autos(track,
1286                         0, &y_auto, &z_auto, 1, 0, 1, 0);
1287         }
1288
1289         if(y_auto && z_auto)
1290         {
1291                 int w = 0, h = 0;
1292                 track->get_source_dimensions(
1293                         mwindow->edl->local_session->get_selectionstart(1),
1294                         w, h);
1295
1296                 if(w && h)
1297                 {
1298                         mwindow->undo->update_undo_before(_("camera"), 0);
1299                         y_auto->set_value(-((double)track->track_h / z_auto->get_value() / 2 -
1300                                 (double)h / 2));
1301                         gui->update();
1302                         gui->update_preview();
1303                         mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
1304                 }
1305         }
1306
1307         return 1;
1308 }
1309
1310 CWindowCameraAddKeyframe::CWindowCameraAddKeyframe(MWindow *mwindow,
1311                 CWindowToolGUI *gui, int x, int y)
1312  : BC_Button(x, y, mwindow->theme->get_image_set("keyframe_button"))
1313 {
1314         this->mwindow = mwindow;
1315         this->gui = gui;
1316         set_tooltip(_("Add Keyframe: Shift-F11"));
1317 }
1318
1319 int CWindowCameraAddKeyframe::handle_event()
1320 {
1321         return gui->press(&CWindowCanvas::camera_keyframe);
1322 }
1323
1324 CWindowCameraReset::CWindowCameraReset(MWindow *mwindow,
1325                 CWindowToolGUI *gui, int x, int y)
1326  : BC_Button(x, y, mwindow->theme->get_image_set("reset_button"))
1327 {
1328         this->mwindow = mwindow;
1329         this->gui = gui;
1330         set_tooltip(_("Reset Camera: F11"));
1331 }
1332
1333 int CWindowCameraReset::handle_event()
1334 {
1335         return gui->press(&CWindowCanvas::reset_camera);
1336 }
1337
1338
1339 CWindowProjectorGUI::CWindowProjectorGUI(MWindow *mwindow, CWindowTool *thread)
1340  : CWindowToolGUI(mwindow, thread, _(PROGRAM_NAME ": Projector"), xS(340), yS(170))
1341 {
1342 }
1343 CWindowProjectorGUI::~CWindowProjectorGUI()
1344 {
1345 }
1346 void CWindowProjectorGUI::create_objects()
1347 {
1348         int xs10 = xS(10), xs15 = xS(15);
1349         int ys30 = yS(30);
1350         int x = xs10, y = yS(10);
1351         Track *track = mwindow->cwindow->calculate_affected_track();
1352         FloatAuto *x_auto = 0;
1353         FloatAuto *y_auto = 0;
1354         FloatAuto *z_auto = 0;
1355         BC_Title *title;
1356         BC_Button *button;
1357
1358         lock_window("CWindowProjectorGUI::create_objects");
1359         if( track ) {
1360                 mwindow->cwindow->calculate_affected_autos(track,
1361                         &x_auto, &y_auto, &z_auto, 0, 0, 0, 0);
1362         }
1363
1364         add_subwindow(title = new BC_Title(x = xs10, y, "X:"));
1365         int x1 = x + title->get_w() + xS(3);
1366         float xvalue = x_auto ? x_auto->get_value() : 0;
1367         this->x = new CWindowCoord(this, x1, y, xvalue, AUTOMATION_PROJECTOR_X);
1368         this->x->create_objects();
1369         this->x->range->set_tooltip(_("expand X range"));
1370         y += ys30;
1371         add_subwindow(title = new BC_Title(x = xs10, y, "Y:"));
1372         float yvalue = y_auto ? y_auto->get_value() : 0;
1373         this->y = new CWindowCoord(this, x1, y, yvalue, AUTOMATION_PROJECTOR_Y);
1374         this->y->create_objects();
1375         this->y->range->set_tooltip(_("expand Y range"));
1376         y += ys30;
1377         add_subwindow(title = new BC_Title(x = xs10, y, "Z:"));
1378         float zvalue = z_auto ? z_auto->get_value() : 1;
1379         this->z = new CWindowCoord(this, x1, y, zvalue, AUTOMATION_PROJECTOR_Z);
1380         this->z->create_objects();
1381         this->z->range->set_tooltip(_("expand Zoom range"));
1382         this->z->set_increment(0.01);
1383
1384         y += ys30;
1385         x1 = xs10;
1386         add_subwindow(button = new CWindowProjectorLeft(mwindow, this, x1, y));
1387         x1 += button->get_w();
1388         add_subwindow(button = new CWindowProjectorCenter(mwindow, this, x1, y));
1389         x1 += button->get_w();
1390         add_subwindow(button = new CWindowProjectorRight(mwindow, this, x1, y));
1391 // additional Buttons to control the curve mode of the "current" keyframe
1392         x1 += button->get_w() + xs15;
1393         add_subwindow(this->t_smooth = new CWindowCurveToggle(Projector_Crv_Smooth, mwindow, this, x1, y));
1394         x1 += button->get_w() + xs10;
1395         add_subwindow(this->t_linear = new CWindowCurveToggle(Projector_Crv_Linear, mwindow, this, x1, y));
1396
1397         y += button->get_h();
1398         x1 = xs10;
1399         add_subwindow(button = new CWindowProjectorTop(mwindow, this, x1, y));
1400         x1 += button->get_w();
1401         add_subwindow(button = new CWindowProjectorMiddle(mwindow, this, x1, y));
1402         x1 += button->get_w();
1403         add_subwindow(button = new CWindowProjectorBottom(mwindow, this, x1, y));
1404         x1 += button->get_w() + xs15;
1405         add_subwindow(this->add_keyframe = new CWindowProjectorAddKeyframe(mwindow, this, x1, y));
1406         x1 += button->get_w() + xs10;
1407         add_subwindow(this->reset = new CWindowProjectorReset(mwindow, this, x1, y));
1408
1409 // fill in current auto keyframe values, set toggle states.
1410         this->update();
1411         unlock_window();
1412 }
1413
1414 void CWindowProjectorGUI::handle_event()
1415 {
1416         FloatAuto *x_auto = 0;
1417         FloatAuto *y_auto = 0;
1418         FloatAuto *z_auto = 0;
1419         Track *track = mwindow->cwindow->calculate_affected_track();
1420
1421         if(track)
1422         {
1423                 mwindow->undo->update_undo_before(_("projector"), this);
1424                 if(event_caller == x)
1425                 {
1426                         x_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1427                                 track->automation->autos[AUTOMATION_PROJECTOR_X], 1);
1428                         if(x_auto)
1429                         {
1430                                 x_auto->set_value(atof(x->get_text()));
1431                                 update();
1432                                 update_preview();
1433                         }
1434                 }
1435                 else
1436                 if(event_caller == y)
1437                 {
1438                         y_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1439                                 track->automation->autos[AUTOMATION_PROJECTOR_Y], 1);
1440                         if(y_auto)
1441                         {
1442                                 y_auto->set_value(atof(y->get_text()));
1443                                 update();
1444                                 update_preview();
1445                         }
1446                 }
1447                 else
1448                 if(event_caller == z)
1449                 {
1450                         z_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1451                                 track->automation->autos[AUTOMATION_PROJECTOR_Z], 1);
1452                         if(z_auto)
1453                         {
1454                                 float zoom = atof(z->get_text());
1455                                 if(zoom > 100.) zoom = 100.;
1456                                 else if(zoom < 0.01) zoom = 0.01;
1457 //                      if (zoom != atof(z->get_text()))
1458 //                              z->update(zoom);
1459                                 z_auto->set_value(zoom);
1460
1461                                 mwindow->gui->lock_window("CWindowProjectorGUI::handle_event");
1462                                 mwindow->gui->draw_overlays(1);
1463                                 mwindow->gui->unlock_window();
1464
1465                                 update();
1466                                 update_preview();
1467                         }
1468                 }
1469                 mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
1470         }
1471 }
1472
1473 void CWindowProjectorGUI::update()
1474 {
1475         FloatAuto *x_auto = 0;
1476         FloatAuto *y_auto = 0;
1477         FloatAuto *z_auto = 0;
1478         Track *track = mwindow->cwindow->calculate_affected_track();
1479         if( track ) {
1480                 mwindow->cwindow->calculate_affected_autos(track,
1481                         &x_auto, &y_auto, &z_auto, 0, 0, 0, 0);
1482         }
1483
1484         if( x_auto ) {
1485                 float xvalue = x_auto->get_value();
1486                 x->update_gui(xvalue);
1487         }
1488         if( y_auto ) {
1489                 float yvalue = y_auto->get_value();
1490                 y->update_gui(yvalue);
1491         }
1492         if( z_auto ) {
1493                 float zvalue = z_auto->get_value();
1494                 z->update_gui(zvalue);
1495                 thread->gui->lock_window("CWindowProjectorGUI::update");
1496                 thread->gui->composite_panel->cpanel_zoom->update(zvalue);
1497                 thread->gui->unlock_window();
1498         }
1499
1500         if( x_auto && y_auto && z_auto ) {
1501                 t_smooth->check_toggle_state(x_auto, y_auto, z_auto);
1502                 t_linear->check_toggle_state(x_auto, y_auto, z_auto);
1503         }
1504 }
1505
1506 CWindowProjectorLeft::CWindowProjectorLeft(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
1507  : BC_Button(x, y, mwindow->theme->get_image_set("left_justify"))
1508 {
1509         this->gui = gui;
1510         this->mwindow = mwindow;
1511         set_tooltip(_("Left justify"));
1512 }
1513 int CWindowProjectorLeft::handle_event()
1514 {
1515         FloatAuto *x_auto = 0;
1516         FloatAuto *z_auto = 0;
1517         Track *track = mwindow->cwindow->calculate_affected_track();
1518         if( track ) {
1519                 mwindow->cwindow->calculate_affected_autos(track,
1520                         &x_auto, 0, &z_auto, 0, 1, 0, 0);
1521         }
1522         if(x_auto && z_auto)
1523         {
1524                 mwindow->undo->update_undo_before(_("projector"), 0);
1525                 x_auto->set_value( (double)track->track_w * z_auto->get_value() / 2 -
1526                         (double)mwindow->edl->session->output_w / 2 );
1527                 gui->update();
1528                 gui->update_preview();
1529                 mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
1530         }
1531
1532         return 1;
1533 }
1534
1535
1536 CWindowProjectorCenter::CWindowProjectorCenter(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
1537  : BC_Button(x, y, mwindow->theme->get_image_set("center_justify"))
1538 {
1539         this->gui = gui;
1540         this->mwindow = mwindow;
1541         set_tooltip(_("Center horizontal"));
1542 }
1543 int CWindowProjectorCenter::handle_event()
1544 {
1545         FloatAuto *x_auto = 0;
1546         Track *track = mwindow->cwindow->calculate_affected_track();
1547         if(track)
1548                 x_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1549                         track->automation->autos[AUTOMATION_PROJECTOR_X], 1);
1550
1551         if(x_auto)
1552         {
1553                 mwindow->undo->update_undo_before(_("projector"), 0);
1554                 x_auto->set_value(0);
1555                 gui->update();
1556                 gui->update_preview();
1557                 mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
1558         }
1559
1560         return 1;
1561 }
1562
1563
1564 CWindowProjectorRight::CWindowProjectorRight(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
1565  : BC_Button(x, y, mwindow->theme->get_image_set("right_justify"))
1566 {
1567         this->gui = gui;
1568         this->mwindow = mwindow;
1569         set_tooltip(_("Right justify"));
1570 }
1571 int CWindowProjectorRight::handle_event()
1572 {
1573         FloatAuto *x_auto = 0;
1574         FloatAuto *z_auto = 0;
1575         Track *track = mwindow->cwindow->calculate_affected_track();
1576         if( track ) {
1577                 mwindow->cwindow->calculate_affected_autos(track,
1578                         &x_auto, 0, &z_auto, 0, 1, 0, 0);
1579         }
1580
1581         if(x_auto && z_auto)
1582         {
1583                 mwindow->undo->update_undo_before(_("projector"), 0);
1584                 x_auto->set_value( -((double)track->track_w * z_auto->get_value() / 2 -
1585                         (double)mwindow->edl->session->output_w / 2));
1586                 gui->update();
1587                 gui->update_preview();
1588                 mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
1589         }
1590
1591         return 1;
1592 }
1593
1594
1595 CWindowProjectorTop::CWindowProjectorTop(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
1596  : BC_Button(x, y, mwindow->theme->get_image_set("top_justify"))
1597 {
1598         this->gui = gui;
1599         this->mwindow = mwindow;
1600         set_tooltip(_("Top justify"));
1601 }
1602 int CWindowProjectorTop::handle_event()
1603 {
1604         FloatAuto *y_auto = 0;
1605         FloatAuto *z_auto = 0;
1606         Track *track = mwindow->cwindow->calculate_affected_track();
1607         if( track ) {
1608                 mwindow->cwindow->calculate_affected_autos(track,
1609                         0, &y_auto, &z_auto, 0, 0, 1, 0);
1610         }
1611
1612         if(y_auto && z_auto)
1613         {
1614                 mwindow->undo->update_undo_before(_("projector"), 0);
1615                 y_auto->set_value( (double)track->track_h * z_auto->get_value() / 2 -
1616                         (double)mwindow->edl->session->output_h / 2 );
1617                 gui->update();
1618                 gui->update_preview();
1619                 mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
1620         }
1621
1622         return 1;
1623 }
1624
1625
1626 CWindowProjectorMiddle::CWindowProjectorMiddle(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
1627  : BC_Button(x, y, mwindow->theme->get_image_set("middle_justify"))
1628 {
1629         this->gui = gui;
1630         this->mwindow = mwindow;
1631         set_tooltip(_("Center vertical"));
1632 }
1633 int CWindowProjectorMiddle::handle_event()
1634 {
1635         FloatAuto *y_auto = 0;
1636         Track *track = mwindow->cwindow->calculate_affected_track();
1637         if(track)
1638                 y_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
1639                         track->automation->autos[AUTOMATION_PROJECTOR_Y], 1);
1640
1641         if(y_auto)
1642         {
1643                 mwindow->undo->update_undo_before(_("projector"), 0);
1644                 y_auto->set_value(0);
1645                 gui->update();
1646                 gui->update_preview();
1647                 mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
1648         }
1649
1650         return 1;
1651 }
1652
1653
1654 CWindowProjectorBottom::CWindowProjectorBottom(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
1655  : BC_Button(x, y, mwindow->theme->get_image_set("bottom_justify"))
1656 {
1657         this->gui = gui;
1658         this->mwindow = mwindow;
1659         set_tooltip(_("Bottom justify"));
1660 }
1661 int CWindowProjectorBottom::handle_event()
1662 {
1663         FloatAuto *y_auto = 0;
1664         FloatAuto *z_auto = 0;
1665         Track *track = mwindow->cwindow->calculate_affected_track();
1666         if( track ) {
1667                 mwindow->cwindow->calculate_affected_autos(track,
1668                         0, &y_auto, &z_auto, 0, 0, 1, 0);
1669         }
1670
1671         if(y_auto && z_auto)
1672         {
1673                 mwindow->undo->update_undo_before(_("projector"), 0);
1674                 y_auto->set_value( -((double)track->track_h * z_auto->get_value() / 2 -
1675                         (double)mwindow->edl->session->output_h / 2));
1676                 gui->update();
1677                 gui->update_preview();
1678                 mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
1679         }
1680
1681         return 1;
1682 }
1683
1684 CWindowProjectorAddKeyframe::CWindowProjectorAddKeyframe(MWindow *mwindow,
1685                 CWindowToolGUI *gui, int x, int y)
1686  : BC_Button(x, y, mwindow->theme->get_image_set("keyframe_button"))
1687 {
1688         this->mwindow = mwindow;
1689         this->gui = gui;
1690         set_tooltip(_("Add Keyframe: Shift-F12"));
1691 }
1692
1693 int CWindowProjectorAddKeyframe::handle_event()
1694 {
1695         return gui->press(&CWindowCanvas::projector_keyframe);
1696 }
1697
1698 CWindowProjectorReset::CWindowProjectorReset(MWindow *mwindow,
1699                 CWindowToolGUI *gui, int x, int y)
1700  : BC_Button(x, y, mwindow->theme->get_image_set("reset_button"))
1701 {
1702         this->mwindow = mwindow;
1703         this->gui = gui;
1704         set_tooltip(_("Reset Projector: F12"));
1705 }
1706
1707 int CWindowProjectorReset::handle_event()
1708 {
1709         return gui->press(&CWindowCanvas::reset_projector);
1710 }
1711
1712 int CWindowToolGUI::press(void (CWindowCanvas::*fn)())
1713 {
1714         unlock_window();
1715         CWindowGUI *cw_gui = thread->gui;
1716         cw_gui->lock_window("CWindowGUI::press");
1717         (cw_gui->canvas->*fn)();
1718         cw_gui->unlock_window();
1719         lock_window("CWindowToolGUI::press");
1720         return 1;
1721 }
1722
1723 CWindowMaskOnTrack::CWindowMaskOnTrack(MWindow *mwindow, CWindowMaskGUI *gui,
1724                 int x, int y, int w, const char *text)
1725  : BC_PopupTextBox(gui, 0, text, x, y, w, yS(120))
1726 {
1727         this->mwindow = mwindow;
1728         this->gui = gui;
1729 }
1730
1731 CWindowMaskOnTrack::~CWindowMaskOnTrack()
1732 {
1733 }
1734
1735 int CWindowMaskOnTrack::handle_event()
1736 {
1737         CWindowMaskItem *track_item = 0;
1738         int k = get_number(), track_id = -1;
1739 //printf("selected %d = %s\n", k, k<0 ? "()" : track_items[k]->get_text());
1740         if( k >= 0 ) {
1741                 track_item = (CWindowMaskItem *)track_items[k];
1742                 Track *track = track_item ? mwindow->edl->tracks->get_track_by_id(track_item->id) : 0;
1743                 if( track && track->record ) track_id = track->get_id();
1744         }
1745         else
1746                 track_id = mwindow->cwindow->mask_track_id;
1747         set_back_color(track_id >= 0 ?
1748                 gui->get_resources()->text_background :
1749                 gui->get_resources()->text_background_disarmed);
1750         if( mwindow->cwindow->mask_track_id != track_id )
1751                 gui->mask_on_track->update(track_item ? track_item->get_text() : "");
1752         mwindow->cwindow->mask_track_id = track_id;
1753         mwindow->edl->local_session->solo_track_id = -1;
1754         gui->mask_solo_track->update(0);
1755         gui->update();
1756         gui->update_preview(1);
1757         return 1;
1758 }
1759
1760 void CWindowMaskOnTrack::update_items()
1761 {
1762         track_items.remove_all_objects();
1763         int high_color = gui->get_resources()->button_highlighted;
1764         for( Track *track=mwindow->edl->tracks->first; track; track=track->next ) {
1765                 if( track->data_type != TRACK_VIDEO ) continue;
1766                 MaskAutos *mask_autos = (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
1767                 int color = !track->record ? RED : mask_autos->first ?  high_color : -1;
1768                 MaskAuto *mask_auto = (MaskAuto*)mask_autos->default_auto;
1769                 for( int i=0; color<0 && i<mask_auto->masks.size(); ++i )
1770                         if( mask_auto->masks[i]->points.size() > 0 ) color = high_color;
1771                 track_items.append(new CWindowMaskItem(track->title, track->get_id(), color));
1772         }
1773         update_list(&track_items);
1774 }
1775
1776 CWindowMaskTrackTumbler::CWindowMaskTrackTumbler(MWindow *mwindow, CWindowMaskGUI *gui,
1777                 int x, int y)
1778  : BC_Tumbler(x, y)
1779 {
1780         this->mwindow = mwindow;
1781         this->gui = gui;
1782 }
1783 CWindowMaskTrackTumbler::~CWindowMaskTrackTumbler()
1784 {
1785 }
1786
1787 int CWindowMaskTrackTumbler::handle_up_event()
1788 {
1789         return do_event(1);
1790 }
1791
1792 int CWindowMaskTrackTumbler::handle_down_event()
1793 {
1794         return do_event(-1);
1795 }
1796
1797 int CWindowMaskTrackTumbler::do_event(int dir)
1798 {
1799         CWindowMaskItem *track_item = 0;
1800         CWindowMaskItem **items = (CWindowMaskItem**)&gui->mask_on_track->track_items[0];
1801         int n = gui->mask_on_track->track_items.size();
1802         int id = mwindow->cwindow->mask_track_id;
1803         if( n > 0 ) {   
1804                 int k = n;
1805                 while( --k >= 0 && items[k]->id != id );
1806                 if( k >= 0 ) {
1807                         k += dir;
1808                         bclamp(k, 0, n-1);
1809                         track_item = items[k];
1810                 }
1811                 else
1812                         track_item = items[0];
1813         }
1814         Track *track = track_item ? mwindow->edl->tracks->get_track_by_id(track_item->id) : 0;
1815         int track_id = track_item && track && track->record ? track_item->id : -1;
1816         gui->mask_on_track->set_back_color(track_id >= 0 ?
1817                 gui->get_resources()->text_background :
1818                 gui->get_resources()->text_background_disarmed);
1819         gui->mask_on_track->update(track_item ? track_item->get_text() : "");
1820         mwindow->cwindow->mask_track_id = track_item ? track_item->id : -1;
1821         mwindow->edl->local_session->solo_track_id = -1;
1822         gui->mask_solo_track->update(0);
1823         gui->update();
1824         gui->update_preview(1);
1825         return 1;
1826 }
1827
1828
1829 CWindowMaskName::CWindowMaskName(MWindow *mwindow, CWindowMaskGUI *gui,
1830                 int x, int y, const char *text)
1831  : BC_PopupTextBox(gui, 0, text, x, y, xS(100), yS(160))
1832 {
1833         this->mwindow = mwindow;
1834         this->gui = gui;
1835 }
1836
1837 CWindowMaskName::~CWindowMaskName()
1838 {
1839 }
1840
1841 int CWindowMaskName::handle_event()
1842 {
1843         Track *track;
1844         MaskAutos *autos;
1845         MaskAuto *keyframe;
1846         SubMask *mask;
1847         MaskPoint *point;
1848 //printf("CWindowMaskGUI::update 1\n");
1849         gui->get_keyframe(track, autos, keyframe, mask, point, 0);
1850         if( track ) {
1851                 int k = get_number();
1852                 if( k < 0 ) k = mwindow->edl->session->cwindow_mask;
1853                 else mwindow->edl->session->cwindow_mask = k;
1854                 if( k >= 0 && k < mask_items.size() ) {
1855                         mask_items[k]->set_text(get_text());
1856                         update_list(&mask_items);
1857                 }
1858 #ifdef USE_KEYFRAME_SPANNING
1859                 MaskAuto temp_keyframe(mwindow->edl, autos);
1860                 temp_keyframe.copy_data(keyframe);
1861                 SubMask *submask = temp_keyframe.get_submask(mwindow->edl->session->cwindow_mask);
1862                 memset(submask->name, 0, sizeof(submask->name));
1863                 strncpy(submask->name, get_text(), sizeof(submask->name)-1);
1864                 ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->update_parameter(&temp_keyframe);
1865 #else
1866                 for(MaskAuto *current = (MaskAuto*)autos->default_auto; current; ) {
1867                         SubMask *submask = current->get_submask(mwindow->edl->session->cwindow_mask);
1868                         memset(submask->name, 0, sizeof(submask->name));
1869                         strncpy(submask->name, get_text(), sizeof(submask->name)-1);
1870                         current = current == (MaskAuto*)autos->default_auto ?
1871                                 (MaskAuto*)autos->first : (MaskAuto*)NEXT;
1872                 }
1873 #endif
1874                 gui->update();
1875                 gui->update_preview();
1876         }
1877         return 1;
1878 }
1879
1880 void CWindowMaskName::update_items(MaskAuto *keyframe)
1881 {
1882         mask_items.remove_all_objects();
1883         int sz = !keyframe ? 0 : keyframe->masks.size();
1884         for( int i=0; i<SUBMASKS; ++i ) {
1885                 char text[BCSTRLEN];  memset(text, 0, sizeof(text));
1886                 if( i < sz ) {
1887                         SubMask *sub_mask = keyframe->masks.get(i);
1888                         strncpy(text, sub_mask->name, sizeof(text)-1);
1889                 }
1890                 else
1891                         sprintf(text, "%d", i);
1892                 mask_items.append(new CWindowMaskItem(text));
1893         }
1894         update_list(&mask_items);
1895 }
1896
1897
1898 CWindowMaskButton::CWindowMaskButton(MWindow *mwindow, CWindowMaskGUI *gui,
1899                  int x, int y, int no, int v)
1900  : BC_CheckBox(x, y, v)
1901 {
1902         this->mwindow = mwindow;
1903         this->gui = gui;
1904         this->no = no;
1905 }
1906
1907 CWindowMaskButton::~CWindowMaskButton()
1908 {
1909 }
1910
1911 int CWindowMaskButton::handle_event()
1912 {
1913         mwindow->edl->session->cwindow_mask = no;
1914         gui->mask_name->update(gui->mask_name->mask_items[no]->get_text());
1915         gui->update();
1916         gui->update_preview();
1917         return 1;
1918 }
1919
1920 CWindowMaskThumbler::CWindowMaskThumbler(MWindow *mwindow, CWindowMaskGUI *gui,
1921                 int x, int y)
1922  : BC_Tumbler(x, y)
1923 {
1924         this->mwindow = mwindow;
1925         this->gui = gui;
1926 }
1927
1928 CWindowMaskThumbler::~CWindowMaskThumbler()
1929 {
1930 }
1931
1932 int CWindowMaskThumbler::handle_up_event()
1933 {
1934         return do_event(1);
1935 }
1936
1937 int CWindowMaskThumbler::handle_down_event()
1938 {
1939         return do_event(-1);
1940 }
1941
1942 int CWindowMaskThumbler::do_event(int dir)
1943 {
1944         int k = mwindow->edl->session->cwindow_mask;
1945         if( (k+=dir) >= SUBMASKS ) k = 0;
1946         else if( k < 0 ) k = SUBMASKS-1;
1947         mwindow->edl->session->cwindow_mask = k;
1948         gui->mask_name->update(gui->mask_name->mask_items[k]->get_text());
1949         gui->update();
1950         gui->update_preview();
1951         return 1;
1952 }
1953
1954 CWindowMaskEnable::CWindowMaskEnable(MWindow *mwindow, CWindowMaskGUI *gui,
1955                  int x, int y, int no, int v)
1956  : BC_CheckBox(x, y, v)
1957 {
1958         this->mwindow = mwindow;
1959         this->gui = gui;
1960         this->no = no;
1961 }
1962
1963 CWindowMaskEnable::~CWindowMaskEnable()
1964 {
1965 }
1966
1967 int CWindowMaskEnable::handle_event()
1968 {
1969         Track *track = mwindow->cwindow->calculate_mask_track();
1970         if( track ) {
1971                 mwindow->undo->update_undo_before(_("mask enable"), this);
1972                 int bit = 1 << no;
1973                 if( get_value() )
1974                         track->masks |= bit;
1975                 else
1976                         track->masks &= ~bit;
1977                 gui->update();
1978                 gui->update_preview(1);
1979                 mwindow->undo->update_undo_after(_("mask enable"), LOAD_PATCHES);
1980         }
1981         return 1;
1982 }
1983
1984 CWindowMaskUnclear::CWindowMaskUnclear(MWindow *mwindow,
1985         CWindowMaskGUI *gui, int x, int y)
1986  : BC_Button(x, y, mwindow->theme->get_image_set("unclear_button"))
1987 {
1988         this->mwindow = mwindow;
1989         this->gui = gui;
1990         set_tooltip(_("Show/Hide mask"));
1991 }
1992
1993 int CWindowMaskUnclear::handle_event()
1994 {
1995         Track *track = mwindow->cwindow->calculate_mask_track();
1996         if( track ) {
1997                 mwindow->undo->update_undo_before(_("mask enables"), this);
1998                 int m = (1<<SUBMASKS)-1;
1999                 if( track->masks == m )
2000                         track->masks = 0;
2001                 else
2002                         track->masks = m;
2003                 for( int i=0; i<SUBMASKS; ++i )
2004                         gui->mask_enables[i]->update((track->masks>>i) & 1);
2005                 gui->update_preview(1);
2006                 mwindow->undo->update_undo_after(_("mask enables"), LOAD_PATCHES);
2007         }
2008         return 1;
2009 }
2010
2011 CWindowMaskSoloTrack::CWindowMaskSoloTrack(MWindow *mwindow,
2012         CWindowMaskGUI *gui, int x, int y, int v)
2013  : BC_CheckBox(x, y, v, _("Solo"))
2014 {
2015         this->mwindow = mwindow;
2016         this->gui = gui;
2017         set_tooltip(_("Solo video track"));
2018 }
2019
2020 int CWindowMaskSoloTrack::handle_event()
2021 {
2022         mwindow->edl->local_session->solo_track_id =
2023                 get_value() ? mwindow->cwindow->mask_track_id : -1;
2024         gui->update_preview(1);
2025         return 1;
2026 }
2027
2028 int CWindowMaskSoloTrack::calculate_w(BC_WindowBase *gui)
2029 {
2030         int w = 0, h = 0;
2031         calculate_extents(gui, &w, &h, _("Solo"));
2032         return w;
2033 }
2034
2035 CWindowMaskDelMask::CWindowMaskDelMask(MWindow *mwindow,
2036         CWindowMaskGUI *gui, int x, int y)
2037  : BC_GenericButton(x, y, _("Delete"))
2038 {
2039         this->mwindow = mwindow;
2040         this->gui = gui;
2041         set_tooltip(_("Delete mask"));
2042 }
2043
2044 int CWindowMaskDelMask::handle_event()
2045 {
2046         MaskAutos *autos;
2047         MaskAuto *keyframe;
2048         Track *track;
2049         MaskPoint *point;
2050         SubMask *mask;
2051         int total_points;
2052
2053 // Get existing keyframe
2054         gui->get_keyframe(track, autos, keyframe, mask, point, 0);
2055
2056         if( track ) {
2057                 mwindow->undo->update_undo_before(_("mask delete"), 0);
2058
2059 #ifdef USE_KEYFRAME_SPANNING
2060 // Create temp keyframe
2061                 MaskAuto temp_keyframe(mwindow->edl, autos);
2062                 temp_keyframe.copy_data(keyframe);
2063                 SubMask *submask = temp_keyframe.get_submask(mwindow->edl->session->cwindow_mask);
2064                 submask->points.remove_all_objects();
2065                 total_points = 0;
2066 // Commit change to span of keyframes
2067                 ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->update_parameter(&temp_keyframe);
2068 #else
2069                 for(MaskAuto *current = (MaskAuto*)autos->default_auto; current; ) {
2070                         SubMask *submask = current->get_submask(mwindow->edl->session->cwindow_mask);
2071                         submask->points.remove_all_objects();
2072                         current = current == (MaskAuto*)autos->default_auto ?
2073                                 (MaskAuto*)autos->first : (MaskAuto*)NEXT;
2074                 }
2075                 total_points = 0;
2076 #endif
2077                 if( mwindow->cwindow->gui->affected_point >= total_points )
2078                         mwindow->cwindow->gui->affected_point =
2079                                 total_points > 0 ? total_points-1 : 0;
2080
2081                 gui->update();
2082                 gui->update_preview();
2083                 mwindow->undo->update_undo_after(_("mask delete"), LOAD_AUTOMATION);
2084         }
2085
2086         return 1;
2087 }
2088
2089 CWindowMaskDelPoint::CWindowMaskDelPoint(MWindow *mwindow,
2090         CWindowMaskGUI *gui, int x, int y)
2091  : BC_GenericButton(x, y, _("Delete"))
2092 {
2093         this->mwindow = mwindow;
2094         this->gui = gui;
2095         set_tooltip(_("Delete point"));
2096 }
2097
2098 int CWindowMaskDelPoint::handle_event()
2099 {
2100         MaskAutos *autos;
2101         MaskAuto *keyframe;
2102         Track *track;
2103         MaskPoint *point;
2104         SubMask *mask;
2105         int total_points;
2106
2107 // Get existing keyframe
2108         gui->get_keyframe(track, autos, keyframe, mask, point, 0);
2109         if( track ) {
2110                 mwindow->undo->update_undo_before(_("point delete"), 0);
2111
2112 #ifdef USE_KEYFRAME_SPANNING
2113 // Create temp keyframe
2114                 MaskAuto temp_keyframe(mwindow->edl, autos);
2115                 temp_keyframe.copy_data(keyframe);
2116 // Update parameter
2117                 SubMask *submask = temp_keyframe.get_submask(mwindow->edl->session->cwindow_mask);
2118                 int i = mwindow->cwindow->gui->affected_point;
2119                 for( ; i<submask->points.total-1; ++i )
2120                         *submask->points.values[i] = *submask->points.values[i+1];
2121                 if( submask->points.total > 0 ) {
2122                         point = submask->points.values[submask->points.total-1];
2123                         submask->points.remove_object(point);
2124                 }
2125                 total_points = submask->points.total;
2126
2127 // Commit change to span of keyframes
2128                 ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->update_parameter(&temp_keyframe);
2129 #else
2130                 total_points = 0;
2131                 MaskAuto *current = (MaskAuto*)autos->default_auto;
2132                 while( current ) {
2133                         SubMask *submask = current->get_submask(mwindow->edl->session->cwindow_mask);
2134                         int i = mwindow->cwindow->gui->affected_point;
2135                         for( ; i<submask->points.total-1; ++i )
2136                                 *submask->points.values[i] = *submask->points.values[i+1];
2137                         if( submask->points.total > 0 ) {
2138                                 point = submask->points.values[submask->points.total-1];
2139                                 submask->points.remove_object(point);
2140                         }
2141                         total_points = submask->points.total;
2142                         current = current == (MaskAuto*)autos->default_auto ?
2143                                 (MaskAuto*)autos->first : (MaskAuto*)NEXT;
2144                 }
2145 #endif
2146                 if( mwindow->cwindow->gui->affected_point >= total_points )
2147                         mwindow->cwindow->gui->affected_point =
2148                                 total_points > 0 ? total_points-1 : 0;
2149
2150                 gui->update();
2151                 gui->update_preview();
2152                 mwindow->undo->update_undo_after(_("point delete"), LOAD_AUTOMATION);
2153         }
2154
2155         return 1;
2156 }
2157
2158
2159 CWindowMaskAffectedPoint::CWindowMaskAffectedPoint(MWindow *mwindow,
2160         CWindowMaskGUI *gui, int x, int y)
2161  : BC_TumbleTextBox(gui,
2162                 (int64_t)mwindow->cwindow->gui->affected_point,
2163                 (int64_t)0, INT64_MAX, x, y, xS(70))
2164 {
2165         this->mwindow = mwindow;
2166         this->gui = gui;
2167 }
2168
2169 CWindowMaskAffectedPoint::~CWindowMaskAffectedPoint()
2170 {
2171 }
2172
2173 int CWindowMaskAffectedPoint::handle_event()
2174 {
2175         int total_points = 0;
2176         int affected_point = atol(get_text());
2177         Track *track = mwindow->cwindow->calculate_mask_track();
2178         if(track) {
2179                 MaskAutos *autos = (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
2180                 MaskAuto *keyframe = (MaskAuto*)mwindow->cwindow->calculate_affected_auto(autos, 0);
2181                 if( keyframe ) {
2182                         SubMask *mask = keyframe->get_submask(mwindow->edl->session->cwindow_mask);
2183                         total_points = mask->points.size();
2184                 }
2185         }
2186         int active_point = affected_point;
2187         if( affected_point >= total_points )
2188                 affected_point = total_points - 1;
2189         if( affected_point < 0 )
2190                 affected_point = 0;
2191         if( active_point != affected_point )
2192                 update((int64_t)affected_point);
2193         mwindow->cwindow->gui->affected_point = affected_point;
2194         gui->update();
2195         gui->update_preview();
2196         return 1;
2197 }
2198
2199
2200 CWindowMaskFocus::CWindowMaskFocus(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y)
2201  : BC_CheckBox(x, y, gui->focused, _("Focus"))
2202 {
2203         this->mwindow = mwindow;
2204         this->gui = gui;
2205         set_tooltip(_("Center for rotate/scale"));
2206 }
2207
2208 CWindowMaskFocus::~CWindowMaskFocus()
2209 {
2210 }
2211
2212 int CWindowMaskFocus::handle_event()
2213 {
2214         gui->focused = get_value();
2215         gui->update();
2216         gui->update_preview();
2217         return 1;
2218 }
2219
2220 int CWindowMaskFocus::calculate_w(CWindowMaskGUI *gui)
2221 {
2222         int w, h;
2223         calculate_extents(gui, &w, &h, _("Focus"));
2224         return w;
2225 }
2226
2227 CWindowMaskScaleXY::CWindowMaskScaleXY(MWindow *mwindow, CWindowMaskGUI *gui,
2228                 int x, int y, VFrame **data, int v, int id, const char *tip)
2229  : BC_Toggle(x, y, data, v)
2230 {
2231         this->id = id;
2232         this->mwindow = mwindow;
2233         this->gui = gui;
2234         set_tooltip(tip);
2235 }
2236
2237 CWindowMaskScaleXY::~CWindowMaskScaleXY()
2238 {
2239 }
2240
2241 int CWindowMaskScaleXY::handle_event()
2242 {
2243         gui->scale_mode = id;
2244         gui->mask_scale_x->update(id == MASK_SCALE_X);
2245         gui->mask_scale_y->update(id == MASK_SCALE_Y);
2246         gui->mask_scale_xy->update(id == MASK_SCALE_XY);
2247         return 1;
2248 }
2249
2250 CWindowMaskHelp::CWindowMaskHelp(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y)
2251  : BC_CheckBox(x, y, 0, _("Help"))
2252 {
2253         this->mwindow = mwindow;
2254         this->gui = gui;
2255         set_tooltip(_("Show help text"));
2256 }
2257
2258 CWindowMaskHelp::~CWindowMaskHelp()
2259 {
2260 }
2261
2262 int CWindowMaskHelp::handle_event()
2263 {
2264         gui->helped = get_value();
2265         gui->resize_window(gui->get_w(),
2266                 gui->helped ? gui->help_h : gui->help_y);
2267         gui->update();
2268         return 1;
2269 }
2270
2271 CWindowMaskDrawMarkers::CWindowMaskDrawMarkers(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y)
2272  : BC_CheckBox(x, y, gui->markers, _("Markers"))
2273 {
2274         this->mwindow = mwindow;
2275         this->gui = gui;
2276         set_tooltip("Display points");
2277 }
2278
2279 CWindowMaskDrawMarkers::~CWindowMaskDrawMarkers()
2280 {
2281 }
2282
2283 int CWindowMaskDrawMarkers::handle_event()
2284 {
2285         gui->markers = get_value();
2286         gui->update();
2287         gui->update_preview();
2288         return 1;
2289 }
2290
2291 CWindowMaskDrawBoundary::CWindowMaskDrawBoundary(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y)
2292  : BC_CheckBox(x, y, gui->boundary, _("Boundary"))
2293 {
2294         this->mwindow = mwindow;
2295         this->gui = gui;
2296         set_tooltip("Display mask outline");
2297 }
2298
2299 CWindowMaskDrawBoundary::~CWindowMaskDrawBoundary()
2300 {
2301 }
2302
2303 int CWindowMaskDrawBoundary::handle_event()
2304 {
2305         gui->boundary = get_value();
2306         gui->update();
2307         gui->update_preview();
2308         return 1;
2309 }
2310
2311
2312 CWindowMaskFeather::CWindowMaskFeather(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y)
2313  : BC_TumbleTextBox(gui, 0, INT_MIN, INT_MAX, x, y, xS(64), 2)
2314 {
2315         this->mwindow = mwindow;
2316         this->gui = gui;
2317 }
2318 CWindowMaskFeather::~CWindowMaskFeather()
2319 {
2320 }
2321
2322 int CWindowMaskFeather::update(float v)
2323 {
2324         gui->feather_slider->update(v);
2325         return BC_TumbleTextBox::update(v);
2326 }
2327
2328 int CWindowMaskFeather::update_value(float v)
2329 {
2330         MaskAutos *autos;
2331         MaskAuto *keyframe;
2332         Track *track;
2333         MaskPoint *point;
2334         SubMask *mask;
2335 #ifdef USE_KEYFRAME_SPANNING
2336         int create_it = 0;
2337 #else
2338         int create_it = 1;
2339 #endif
2340
2341         mwindow->undo->update_undo_before(_("mask feather"), this);
2342
2343 // Get existing keyframe
2344         gui->get_keyframe(track, autos, keyframe,
2345                         mask, point, create_it);
2346         if( track ) {
2347                 int gang = gui->gang_feather->get_value();
2348 #ifdef USE_KEYFRAME_SPANNING
2349                 MaskAuto temp_keyframe(mwindow->edl, autos);
2350                 temp_keyframe.copy_data(keyframe);
2351                 keyframe = &temp_keyframe;
2352 #endif
2353                 float change = v - mask->feather;
2354                 int k = mwindow->edl->session->cwindow_mask;
2355                 int n = gang ? keyframe->masks.size() : k+1;
2356                 for( int i=gang? 0 : k; i<n; ++i ) {
2357                         if( !gui->mask_enables[i]->get_value() ) continue;
2358                         SubMask *sub_mask = keyframe->get_submask(i);
2359                         float feather = sub_mask->feather + change;
2360                         sub_mask->feather = feather;
2361                 }
2362 #ifdef USE_KEYFRAME_SPANNING
2363                 autos->update_parameter(keyframe);
2364 #endif
2365                 gui->update_preview();
2366         }
2367
2368         mwindow->undo->update_undo_after(_("mask feather"), LOAD_AUTOMATION);
2369         return 1;
2370 }
2371
2372 int CWindowMaskFeather::handle_event()
2373 {
2374         float v = atof(get_text());
2375         if( fabsf(v) > MAX_FEATHER )
2376                 BC_TumbleTextBox::update((float)(v>=0 ? MAX_FEATHER : -MAX_FEATHER));
2377         gui->feather_slider->update(v);
2378         return gui->feather->update_value(v);
2379 }
2380
2381 CWindowMaskFeatherSlider::CWindowMaskFeatherSlider(MWindow *mwindow,
2382                 CWindowMaskGUI *gui, int x, int y, int w, float v)
2383  : BC_FSlider(x, y, 0, w, w, -FEATHER_MAX-5, FEATHER_MAX+5, v)
2384 {
2385         this->mwindow = mwindow;
2386         this->gui = gui;
2387         set_precision(0.01);
2388         timer = new Timer();
2389         stick = 0;
2390         last_v = 0;
2391         max = FEATHER_MAX;
2392 }
2393
2394 CWindowMaskFeatherSlider::~CWindowMaskFeatherSlider()
2395 {
2396         delete timer;
2397 }
2398
2399 int CWindowMaskFeatherSlider::handle_event()
2400 {
2401         int sticky = 0;
2402         float v = get_value();
2403         if( fabsf(v) > MAX_FEATHER )
2404                 v = v>=0 ? MAX_FEATHER : -MAX_FEATHER;
2405         if( stick && timer->get_difference() >= 250 )
2406                 stick = 0; // no events for .25 sec
2407         if( stick && (last_v * (v-last_v)) < 0 )
2408                 stick = 0; // dv changed direction
2409         if( stick ) {
2410                 if( --stick > 0 ) {
2411                         timer->update();
2412                         update(last_v);
2413                         return 1;
2414                 }
2415                 if( last_v ) {
2416                         max *= 1.25;
2417                         if( max > MAX_FEATHER ) max = MAX_FEATHER;
2418                         update(get_w(), v=last_v, -max-5, max+5);
2419                         button_release_event();
2420                 }
2421         }
2422         else if( v > max ) { v = max;  sticky = 24; }
2423         else if( v < -max ) { v = -max; sticky = 24; }
2424         else if( v>=0 ? last_v<0 : last_v>=0 ) { v = 0;  sticky = 16; }
2425         if( sticky ) { update(v);  stick = sticky;  timer->update(); }
2426         last_v = v;
2427         gui->feather->BC_TumbleTextBox::update(v);
2428         return gui->feather->update_value(v);
2429 }
2430
2431 int CWindowMaskFeatherSlider::update(float v)
2432 {
2433         float vv = fabsf(v);
2434         if( vv > MAX_FEATHER ) vv = MAX_FEATHER;
2435         while( max < vv ) max *= 1.25;
2436         return update(get_w(), v, -max-5, max+5);
2437 }
2438 int CWindowMaskFeatherSlider::update(int r, float v, float mn, float mx)
2439 {
2440         return BC_FSlider::update(r, v, mn, mx);
2441 }
2442
2443 CWindowMaskFade::CWindowMaskFade(MWindow *mwindow, CWindowMaskGUI *gui, int x, int y)
2444  : BC_TumbleTextBox(gui, 0, -100.f, 100.f, x, y, xS(64), 2)
2445 {
2446         this->mwindow = mwindow;
2447         this->gui = gui;
2448 }
2449 CWindowMaskFade::~CWindowMaskFade()
2450 {
2451 }
2452
2453 int CWindowMaskFade::update(float v)
2454 {
2455         gui->fade_slider->update(v);
2456         return BC_TumbleTextBox::update(v);
2457 }
2458
2459 int CWindowMaskFade::update_value(float v)
2460 {
2461         MaskAutos *autos;
2462         MaskAuto *keyframe;
2463         Track *track;
2464         MaskPoint *point;
2465         SubMask *mask;
2466 #ifdef USE_KEYFRAME_SPANNING
2467         int create_it = 0;
2468 #else
2469         int create_it = 1;
2470 #endif
2471
2472         mwindow->undo->update_undo_before(_("mask fade"), this);
2473
2474 // Get existing keyframe
2475         gui->get_keyframe(track, autos, keyframe,
2476                         mask, point, create_it);
2477         if( track ) {
2478                 int gang = gui->gang_fader->get_value();
2479 #ifdef USE_KEYFRAME_SPANNING
2480                 MaskAuto temp_keyframe(mwindow->edl, autos);
2481                 temp_keyframe.copy_data(keyframe);
2482                 keyframe = &temp_keyframe;
2483 #endif
2484                 float change = v - mask->fader;
2485                 int k = mwindow->edl->session->cwindow_mask;
2486                 int n = gang ? keyframe->masks.size() : k+1;
2487                 for( int i=gang? 0 : k; i<n; ++i ) {
2488                         if( !gui->mask_enables[i]->get_value() ) continue;
2489                         SubMask *sub_mask = keyframe->get_submask(i);
2490                         float fader = sub_mask->fader + change;
2491                         bclamp(fader, -100.f, 100.f);
2492                         sub_mask->fader = fader;
2493                 }
2494 #ifdef USE_KEYFRAME_SPANNING
2495                 autos->update_parameter(keyframe);
2496 #endif
2497                 gui->update_preview();
2498         }
2499
2500         mwindow->undo->update_undo_after(_("mask fade"), LOAD_AUTOMATION);
2501         return 1;
2502 }
2503
2504 int CWindowMaskFade::handle_event()
2505 {
2506         float v = atof(get_text());
2507         gui->fade_slider->update(v);
2508         return gui->fade->update_value(v);
2509 }
2510
2511 CWindowMaskFadeSlider::CWindowMaskFadeSlider(MWindow *mwindow, CWindowMaskGUI *gui,
2512                 int x, int y, int w)
2513  : BC_ISlider(x, y, 0, w, w, -200, 200, 0)
2514 {
2515         this->mwindow = mwindow;
2516         this->gui = gui;
2517         timer = new Timer();
2518         stick = 0;
2519         last_v = 0;
2520 }
2521
2522 CWindowMaskFadeSlider::~CWindowMaskFadeSlider()
2523 {
2524         delete timer;
2525 }
2526
2527 int CWindowMaskFadeSlider::handle_event()
2528 {
2529         float v = 100*get_value()/200;
2530         if( stick > 0 ) {
2531                 int64_t ms = timer->get_difference();
2532                 if( ms < 250 && --stick > 0 ) {
2533                         if( get_value() == 0 ) return 1;
2534                         update(v = 0);
2535                 }
2536                 else {
2537                         stick = 0;
2538                         last_v = v;
2539                 }
2540         }
2541         else if( (last_v>=0 && v<0) || (last_v<0 && v>=0) ) {
2542                 stick = 16;
2543                 v = 0;
2544         }
2545         else
2546                 last_v = v;
2547         timer->update();
2548         gui->fade->BC_TumbleTextBox::update(v);
2549         return gui->fade->update_value(v);
2550 }
2551
2552 int CWindowMaskFadeSlider::update(int64_t v)
2553 {
2554         return BC_ISlider::update(200*v/100);
2555 }
2556
2557 CWindowMaskGangFader::CWindowMaskGangFader(MWindow *mwindow,
2558                 CWindowMaskGUI *gui, int x, int y)
2559  : BC_Toggle(x, y, mwindow->theme->get_image_set("gangpatch_data"), 0)
2560 {
2561         this->mwindow = mwindow;
2562         this->gui = gui;
2563         set_tooltip(_("Gang fader"));
2564 }
2565
2566 CWindowMaskGangFader::~CWindowMaskGangFader()
2567 {
2568 }
2569
2570 int CWindowMaskGangFader::handle_event()
2571 {
2572         return 1;
2573 }
2574
2575 CWindowMaskGangFocus::CWindowMaskGangFocus(MWindow *mwindow,
2576                 CWindowMaskGUI *gui, int x, int y)
2577  : BC_Toggle(x, y, mwindow->theme->get_image_set("gangpatch_data"), 0)
2578 {
2579         this->mwindow = mwindow;
2580         this->gui = gui;
2581         set_tooltip(_("Gang rotate/scale/translate"));
2582 }
2583
2584 CWindowMaskGangFocus::~CWindowMaskGangFocus()
2585 {
2586 }
2587
2588 int CWindowMaskGangFocus::handle_event()
2589 {
2590         return 1;
2591 }
2592
2593 CWindowMaskGangPoint::CWindowMaskGangPoint(MWindow *mwindow,
2594                 CWindowMaskGUI *gui, int x, int y)
2595  : BC_Toggle(x, y, mwindow->theme->get_image_set("gangpatch_data"), 0)
2596 {
2597         this->mwindow = mwindow;
2598         this->gui = gui;
2599         set_tooltip(_("Gang points"));
2600 }
2601
2602 CWindowMaskGangPoint::~CWindowMaskGangPoint()
2603 {
2604 }
2605
2606 int CWindowMaskGangPoint::handle_event()
2607 {
2608         return 1;
2609 }
2610
2611
2612 CWindowMaskSmoothButton::CWindowMaskSmoothButton(MWindow *mwindow, CWindowMaskGUI *gui,
2613                 const char *tip, int type, int on, int x, int y, const char *images)
2614  : BC_Button(x, y, mwindow->theme->get_image_set(images))
2615 {
2616         this->mwindow = mwindow;
2617         this->gui = gui;
2618         this->type = type;
2619         this->on = on;
2620         set_tooltip(tip);
2621 }
2622
2623 int CWindowMaskSmoothButton::handle_event()
2624 {
2625         return gui->smooth_mask(type, on);
2626 }
2627
2628 CWindowMaskBeforePlugins::CWindowMaskBeforePlugins(CWindowMaskGUI *gui, int x, int y)
2629  : BC_CheckBox(x, y, 1, _("Apply mask before plugins"))
2630 {
2631         this->gui = gui;
2632 }
2633
2634 int CWindowMaskBeforePlugins::handle_event()
2635 {
2636         Track *track;
2637         MaskAutos *autos;
2638         MaskAuto *keyframe;
2639         SubMask *mask;
2640         MaskPoint *point;
2641         gui->get_keyframe(track, autos, keyframe, mask, point, 1);
2642
2643         if (keyframe) {
2644                 int v = get_value();
2645 #ifdef USE_KEYFRAME_SPANNING
2646                 MaskAuto temp_keyframe(gui->mwindow->edl, autos);
2647                 temp_keyframe.copy_data(keyframe);
2648                 temp_keyframe.apply_before_plugins = v;
2649                 autos->update_parameter(&temp_keyframe);
2650 #else
2651                 keyframe->apply_before_plugins = v;
2652 #endif
2653                 gui->update_preview();
2654         }
2655         return 1;
2656 }
2657
2658
2659 CWindowDisableOpenGLMasking::CWindowDisableOpenGLMasking(CWindowMaskGUI *gui, int x, int y)
2660  : BC_CheckBox(x, y, 1, _("Disable OpenGL masking"))
2661 {
2662         this->gui = gui;
2663 }
2664
2665 int CWindowDisableOpenGLMasking::handle_event()
2666 {
2667         Track *track;
2668         MaskAutos *autos;
2669         MaskAuto *keyframe;
2670         SubMask *mask;
2671         MaskPoint *point;
2672         gui->get_keyframe(track, autos, keyframe, mask, point, 1);
2673
2674         if( keyframe ) {
2675                 int v = get_value();
2676 #ifdef USE_KEYFRAME_SPANNING
2677                 MaskAuto temp_keyframe(gui->mwindow->edl, autos);
2678                 temp_keyframe.copy_data(keyframe);
2679                 temp_keyframe.disable_opengl_masking = v;
2680                 autos->update_parameter(&temp_keyframe);
2681 #else
2682                 keyframe->disable_opengl_masking = v;
2683 #endif
2684                 gui->update_preview();
2685         }
2686         return 1;
2687 }
2688
2689
2690 CWindowMaskClrMask::CWindowMaskClrMask(MWindow *mwindow,
2691                 CWindowMaskGUI *gui, int x, int y)
2692  : BC_Button(x, y, mwindow->theme->get_image_set("reset_button"))
2693 {
2694         this->mwindow = mwindow;
2695         this->gui = gui;
2696         set_tooltip(_("Delete all masks"));
2697 }
2698
2699 CWindowMaskClrMask::~CWindowMaskClrMask()
2700 {
2701 }
2702
2703 int CWindowMaskClrMask::calculate_w(MWindow *mwindow)
2704 {
2705         VFrame *vfrm = *mwindow->theme->get_image_set("reset_button");
2706         return vfrm->get_w();
2707 }
2708
2709 int CWindowMaskClrMask::handle_event()
2710 {
2711         MaskAutos *autos;
2712         MaskAuto *keyframe;
2713         Track *track;
2714         MaskPoint *point;
2715         SubMask *mask;
2716
2717 // Get existing keyframe
2718         gui->get_keyframe(track, autos, keyframe, mask, point, 0);
2719
2720         if( track ) {
2721                 mwindow->undo->update_undo_before(_("del masks"), 0);
2722                 ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->clear_all();
2723                 mwindow->undo->update_undo_after(_("del masks"), LOAD_AUTOMATION);
2724         }
2725
2726         gui->update();
2727         gui->update_preview(1);
2728         return 1;
2729 }
2730
2731 CWindowMaskGangFeather::CWindowMaskGangFeather(MWindow *mwindow,
2732                 CWindowMaskGUI *gui, int x, int y)
2733  : BC_Toggle(x, y, mwindow->theme->get_image_set("gangpatch_data"), 0)
2734 {
2735         this->mwindow = mwindow;
2736         this->gui = gui;
2737         set_tooltip(_("Gang feather"));
2738 }
2739
2740 CWindowMaskGangFeather::~CWindowMaskGangFeather()
2741 {
2742 }
2743
2744 int CWindowMaskGangFeather::handle_event()
2745 {
2746         return 1;
2747 }
2748
2749 CWindowMaskGUI::CWindowMaskGUI(MWindow *mwindow, CWindowTool *thread)
2750  : CWindowToolGUI(mwindow, thread,
2751         _(PROGRAM_NAME ": Mask"), xS(440), yS(700))
2752 {
2753         this->mwindow = mwindow;
2754         this->thread = thread;
2755         active_point = 0;
2756         fade = 0;
2757         feather = 0;
2758         focused = 0;
2759         scale_mode = 2;
2760         markers = 1;
2761         boundary = 1;
2762         preset_dialog = 0;
2763 }
2764 CWindowMaskGUI::~CWindowMaskGUI()
2765 {
2766         lock_window("CWindowMaskGUI::~CWindowMaskGUI");
2767         done_event();
2768         delete active_point;
2769         delete fade;
2770         delete feather;
2771         unlock_window();
2772         delete preset_dialog;
2773 }
2774
2775 void CWindowMaskGUI::create_objects()
2776 {
2777         int t[SUBMASKS];
2778         Theme *theme = mwindow->theme;
2779         int xs10 = xS(10), ys10 = yS(10);
2780         int x = xs10, y = ys10;
2781         int margin = theme->widget_border;
2782         int clr_w = CWindowMaskClrMask::calculate_w(mwindow);
2783         int clr_x = get_w()-x - clr_w;
2784
2785         lock_window("CWindowMaskGUI::create_objects");
2786         BC_TitleBar *title_bar;
2787         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xS(20), xS(10),
2788                         _("Masks on Track")));
2789         y += title_bar->get_h() + margin;
2790         BC_Title *title;
2791         add_subwindow(title = new BC_Title(x,y, _("Track:")));
2792         int x1 = x + xS(90), ww = clr_x-2*margin - x1;
2793         for( int i=0,n=sizeof(t)/sizeof(t[0]); i<n; ++i ) t[i] = x1+(i*ww)/n;
2794         int del_x = t[5];
2795         Track *track = mwindow->cwindow->calculate_affected_track();
2796         const char *text = track ? track->title : "";
2797         mwindow->cwindow->mask_track_id = track ? track->get_id() : -1;
2798         mask_on_track = new CWindowMaskOnTrack(mwindow, this, x1, y, xS(100), text);
2799         mask_on_track->create_objects();
2800         mask_on_track->set_tooltip(_("Video track"));
2801         int x2 = x1 + mask_on_track->get_w();
2802         add_subwindow(mask_track_tumbler = new CWindowMaskTrackTumbler(mwindow, this, x2, y));
2803         mwindow->edl->local_session->solo_track_id = -1;
2804         add_subwindow(mask_solo_track = new CWindowMaskSoloTrack(mwindow, this, del_x, y, 0));
2805         y += mask_on_track->get_h() + margin;
2806         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xS(20), xS(10),
2807                         _("Masks")));
2808         y += title_bar->get_h() + margin;
2809         add_subwindow(title = new BC_Title(x, y, _("Mask:")));
2810         mask_name = new CWindowMaskName(mwindow, this, x1, y, "");
2811         mask_name->create_objects();
2812         mask_name->set_tooltip(_("Mask name"));
2813         add_subwindow(mask_clr = new CWindowMaskClrMask(mwindow, this, clr_x, y));
2814         add_subwindow(mask_del = new CWindowMaskDelMask(mwindow, this, del_x, y));
2815         y += mask_name->get_h() + 2*margin;
2816         BC_Bar *bar;
2817 //      add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
2818 //      y += bar->get_h() + 2*margin;
2819
2820         add_subwindow(title = new BC_Title(x, y, _("Select:")));
2821         int bw = 0, bh = 0;
2822         BC_CheckBox::calculate_extents(this, &bw, &bh);
2823         for( int i=0; i<SUBMASKS; ++i ) {
2824                 int v = i == mwindow->edl->session->cwindow_mask ? 1 : 0;
2825                 mask_buttons[i] = new CWindowMaskButton(mwindow, this, t[i], y, i, v);
2826                 add_subwindow(mask_buttons[i]);
2827         }
2828         add_subwindow(mask_thumbler = new CWindowMaskThumbler(mwindow, this, clr_x, y));
2829         y += bh + margin;
2830         for( int i=0; i<SUBMASKS; ++i ) {
2831                 char text[BCSTRLEN];  sprintf(text, "%d", i);
2832                 int tx = (bw - get_text_width(MEDIUMFONT, text)) / 2;
2833                 mask_blabels[i] = new BC_Title(t[i]+tx, y, text);
2834                 add_subwindow(mask_blabels[i]);
2835         }
2836         y += mask_blabels[0]->get_h() + margin;
2837         add_subwindow(title = new BC_Title(x, y, _("Enable:")));
2838         for( int i=0; i<SUBMASKS; ++i ) {
2839                 mask_enables[i] = new CWindowMaskEnable(mwindow, this, t[i], y, i, 1);
2840                 add_subwindow(mask_enables[i]);
2841         }
2842         add_subwindow(mask_unclr = new CWindowMaskUnclear(mwindow, this, clr_x, y));
2843         y += mask_enables[0]->get_h() + 2*margin;
2844         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xS(20), xS(10),
2845                         _("Preset Shapes")));
2846         y += title_bar->get_h() + margin;
2847         add_subwindow(mask_shape_sqr = new CWindowMaskShape(mwindow, this,
2848                 "mask_prst_sqr_images", MASK_SHAPE_SQUARE, t[0], y, _("Square")));
2849         add_subwindow(mask_shape_crc = new CWindowMaskShape(mwindow, this,
2850                 "mask_prst_crc_images", MASK_SHAPE_CIRCLE, t[1], y, _("Circle")));
2851         add_subwindow(mask_shape_tri = new CWindowMaskShape(mwindow, this,
2852                 "mask_prst_tri_images", MASK_SHAPE_TRIANGLE, t[2], y, _("Triangle")));
2853         add_subwindow(mask_shape_ovl = new CWindowMaskShape(mwindow, this,
2854                 "mask_prst_ovl_images", MASK_SHAPE_OVAL, t[3], y, _("Oval")));
2855         add_subwindow(mask_load_list = new CWindowMaskLoadList(mwindow, this));
2856         add_subwindow(mask_load = new CWindowMaskLoad(mwindow, this, t[5], y, xS(80)));
2857         add_subwindow(mask_save = new CWindowMaskSave(mwindow, this, t[6], y, xS(80)));
2858         add_subwindow(mask_delete = new CWindowMaskDelete(mwindow, this, t[7], y, xS(80)));
2859         y += mask_load->get_h() + 2*margin;
2860         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xS(20), xS(10),
2861                         _("Position & Scale")));
2862         y += title_bar->get_h() + 2*margin;
2863         add_subwindow(mask_center = new CWindowMaskCenter(mwindow, this, t[0], y, xS(80)));
2864         add_subwindow(mask_normal = new CWindowMaskNormal(mwindow, this, t[1], y, xS(80)));
2865
2866         add_subwindow(mask_scale_x = new CWindowMaskScaleXY(mwindow, this,
2867                 t[5], y, theme->get_image_set("mask_scale_x"), 0, MASK_SCALE_X, _("xlate/scale x")));
2868         add_subwindow(mask_scale_y = new CWindowMaskScaleXY(mwindow, this,
2869                 t[6], y, theme->get_image_set("mask_scale_y"), 0, MASK_SCALE_Y, _("xlate/scale y")));
2870         add_subwindow(mask_scale_xy = new CWindowMaskScaleXY(mwindow, this,
2871                 t[7], y, theme->get_image_set("mask_scale_xy"), 1, MASK_SCALE_XY, _("xlate/scale xy")));
2872         y += mask_center->get_h() + 2*margin;
2873         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xS(20), xS(10),
2874                         _("Fade & Feather")));
2875         y += title_bar->get_h() + 2*margin;
2876
2877         add_subwindow(title = new BC_Title(x, y, _("Fade:")));
2878         fade = new CWindowMaskFade(mwindow, this, x1, y);
2879         fade->create_objects();
2880         x2 = x1 + fade->get_w() + 2*margin;
2881         int w2 = clr_x-2*margin - x2;
2882         add_subwindow(fade_slider = new CWindowMaskFadeSlider(mwindow, this, x2, y, w2));
2883         add_subwindow(gang_fader = new CWindowMaskGangFader(mwindow, this, clr_x, y));
2884         y += fade->get_h() + margin;
2885         add_subwindow(title = new BC_Title(x, y, _("Feather:")));
2886         feather = new CWindowMaskFeather(mwindow, this, x1, y);
2887         feather->create_objects();
2888         w2 = clr_x - 2*margin - x2;
2889         feather_slider = new CWindowMaskFeatherSlider(mwindow, this, x2, y, w2, 0);
2890         add_subwindow(feather_slider);
2891         add_subwindow(gang_feather = new CWindowMaskGangFeather(mwindow, this, clr_x, y));
2892         y += feather->get_h() + 2*margin;
2893         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xS(20), xS(10),
2894                         _("Mask Points")));
2895         y += title_bar->get_h() + margin;
2896
2897         add_subwindow(title = new BC_Title(x, y, _("Point:")));
2898         active_point = new CWindowMaskAffectedPoint(mwindow, this, t[0], y);
2899         active_point->create_objects();
2900 // typ=0, this mask, this point
2901         add_subwindow(mask_pnt_linear = new CWindowMaskSmoothButton(mwindow, this,
2902                 _("linear point"), 0, 0, t[3], y, "mask_pnt_linear_images"));
2903         add_subwindow(mask_pnt_smooth = new CWindowMaskSmoothButton(mwindow, this,
2904                 _("smooth point"), 0, 1, t[4], y, "mask_pnt_smooth_images"));
2905         add_subwindow(del_point = new CWindowMaskDelPoint(mwindow, this, del_x, y));
2906         add_subwindow(gang_point = new CWindowMaskGangPoint(mwindow, this, clr_x, y));
2907         y += active_point->get_h() + margin;
2908         add_subwindow(title = new BC_Title(x, y, "X:"));
2909         this->x = new CWindowCoord(this, t[0], y, (float)0.0);
2910         this->x->create_objects();
2911 // typ>0, this mask, all points
2912         add_subwindow(mask_crv_linear = new CWindowMaskSmoothButton(mwindow, this,
2913                 _("linear curve"), 1, 0, t[3], y, "mask_crv_linear_images"));
2914         add_subwindow(mask_crv_smooth = new CWindowMaskSmoothButton(mwindow, this,
2915                 _("smooth curve"), 1, 1, t[4], y, "mask_crv_smooth_images"));
2916         add_subwindow(draw_markers = new CWindowMaskDrawMarkers(mwindow, this, del_x, y));
2917         y += this->x->get_h() + margin;
2918         add_subwindow(title = new BC_Title(x, y, "Y:"));
2919         this->y = new CWindowCoord(this, x1, y, (float)0.0);
2920         this->y->create_objects();
2921 // typ<0, all masks, all points
2922         add_subwindow(mask_all_linear = new CWindowMaskSmoothButton(mwindow, this,
2923                 _("linear all"), -1, 0, t[3], y, "mask_all_linear_images"));
2924         add_subwindow(mask_all_smooth = new CWindowMaskSmoothButton(mwindow, this,
2925                 _("smooth all"), -1, 1, t[4], y, "mask_all_smooth_images"));
2926         add_subwindow(draw_boundary = new CWindowMaskDrawBoundary(mwindow, this, del_x, y));
2927         y += this->y->get_h() + 2*margin;
2928         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xS(20), xS(10),
2929                         _("Pivot Point")));
2930         y += title_bar->get_h() + margin;
2931
2932         add_subwindow(title = new BC_Title(x, y, "X:"));
2933         float cx = mwindow->edl->session->output_w / 2.f;
2934         focus_x = new CWindowCoord(this, x1, y, cx);
2935         focus_x->create_objects();
2936         add_subwindow(focus = new CWindowMaskFocus(mwindow, this, del_x, y));
2937         add_subwindow(gang_focus = new CWindowMaskGangFocus(mwindow, this, clr_x, y));
2938         y += focus_x->get_h() + margin;
2939         add_subwindow(title = new BC_Title(x, y, "Y:"));
2940         float cy = mwindow->edl->session->output_h / 2.f;
2941         focus_y = new CWindowCoord(this, x1, y, cy);
2942         focus_y->create_objects();
2943         y += focus_y->get_h() + 2*margin;
2944         add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
2945         y += bar->get_h() + margin;
2946         add_subwindow(this->apply_before_plugins = new CWindowMaskBeforePlugins(this, x, y));
2947         y += this->apply_before_plugins->get_h();
2948         add_subwindow(this->disable_opengl_masking = new CWindowDisableOpenGLMasking(this, x, y));
2949         add_subwindow(help = new CWindowMaskHelp(mwindow, this, del_x, y));
2950         y += this->disable_opengl_masking->get_h() + 2*margin;
2951         help_y = y;
2952         add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
2953         y += bar->get_h() + 2*margin;
2954         add_subwindow(title = new BC_Title(x, y, _(
2955                 "Shift+LMB: move an end point\n"
2956                 "Ctrl+LMB: move a control point\n"
2957                 "Alt+LMB: to drag translate the mask\n"
2958                 "Shift+MMB: Set Pivot Point at pointer\n"
2959                 "Wheel: rotate around Pivot Point\n"
2960                 "Shift+Wheel: scale around Pivot Point\n"
2961                 "Ctrl+Wheel: rotate/scale around pointer")));
2962         help_h = y + title->get_h() + 2*margin;
2963         update();
2964         resize_window(get_w(), help_y);
2965         unlock_window();
2966 }
2967
2968 int CWindowMaskGUI::close_event()
2969 {
2970         done_event();
2971         return CWindowToolGUI::close_event();
2972 }
2973
2974 void CWindowMaskGUI::done_event()
2975 {
2976         if( mwindow->in_destructor ) return;
2977         int &solo_track_id = mwindow->edl->local_session->solo_track_id;
2978         if( solo_track_id >= 0 ) {
2979                 solo_track_id = -1;
2980                 update_preview();
2981         }
2982 }
2983
2984 void CWindowMaskGUI::get_keyframe(Track* &track,
2985                 MaskAutos* &autos, MaskAuto* &keyframe,
2986                 SubMask* &mask, MaskPoint* &point, int create_it)
2987 {
2988         autos = 0;
2989         keyframe = 0;
2990
2991         track = mwindow->cwindow->calculate_mask_track();
2992         if( !track )
2993                 track = mwindow->cwindow->calculate_affected_track();
2994                 
2995         if(track) {
2996                 autos = (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
2997                 keyframe = (MaskAuto*)mwindow->cwindow->calculate_affected_auto(
2998                         autos,
2999                         create_it);
3000         }
3001
3002         mask = !keyframe ? 0 :
3003                 keyframe->get_submask(mwindow->edl->session->cwindow_mask);
3004
3005         point = 0;
3006         if( keyframe ) {
3007                 if( mwindow->cwindow->gui->affected_point < mask->points.total &&
3008                         mwindow->cwindow->gui->affected_point >= 0 ) {
3009                         point = mask->points.values[mwindow->cwindow->gui->affected_point];
3010                 }
3011         }
3012 }
3013
3014 void CWindowMaskGUI::update()
3015 {
3016         Track *track;
3017         MaskAutos *autos;
3018         MaskAuto *keyframe;
3019         SubMask *mask;
3020         MaskPoint *point;
3021 //printf("CWindowMaskGUI::update 1\n");
3022         get_keyframe(track, autos, keyframe, mask, point, 0);
3023         mwindow->cwindow->mask_track_id = track ? track->get_id() : -1;
3024         mask_on_track->set_back_color(!track || track->record ?
3025                 get_resources()->text_background :
3026                 get_resources()->text_background_disarmed);
3027         mask_on_track->update_items();
3028         mask_on_track->update(!track ? "" : track->title);
3029         mask_name->update_items(keyframe);
3030         const char *text = "";
3031         int sz = !keyframe ? 0 : keyframe->masks.size();
3032         int k = mwindow->edl->session->cwindow_mask;
3033         if( k >= 0 && k < sz )
3034                 text = keyframe->masks[k]->name;
3035         else
3036                 k = mwindow->edl->session->cwindow_mask = 0;
3037         mask_name->update(text);
3038         update_buttons(keyframe, k);
3039         if( point ) {
3040                 x->update(point->x);
3041                 y->update(point->y);
3042         }
3043         if( track ) {
3044                 double position = mwindow->edl->local_session->get_selectionstart(1);
3045                 int64_t position_i = track->to_units(position, 0);
3046                 feather->update(autos->get_feather(position_i, k, PLAY_FORWARD));
3047                 fade->update(autos->get_fader(position_i, k, PLAY_FORWARD));
3048                 int show_mask = track->masks;
3049                 for( int i=0; i<SUBMASKS; ++i )
3050                         mask_enables[i]->update((show_mask>>i) & 1);
3051         }
3052         if( keyframe ) {
3053                 apply_before_plugins->update(keyframe->apply_before_plugins);
3054                 disable_opengl_masking->update(keyframe->disable_opengl_masking);
3055         }
3056         active_point->update((int64_t)mwindow->cwindow->gui->affected_point);
3057 }
3058
3059 void CWindowMaskGUI::handle_event()
3060 {
3061         int redraw = 0;
3062         if( event_caller == this->focus_x ||
3063             event_caller == this->focus_y ) {
3064                 redraw = 1;
3065         }
3066         else if( event_caller == this->x ||
3067                  event_caller == this->y ) {
3068                 Track *track;
3069                 MaskAuto *keyframe;
3070                 MaskAutos *autos;
3071                 SubMask *mask;
3072                 MaskPoint *point;
3073                 get_keyframe(track, autos, keyframe, mask, point, 0);
3074
3075                 mwindow->undo->update_undo_before(_("mask point"), this);
3076
3077                 if( point ) {
3078                         float px = atof(x->get_text());
3079                         float py = atof(y->get_text());
3080                         float dx = px - point->x, dy = py - point->y;
3081 #ifdef USE_KEYFRAME_SPANNING
3082 // Create temp keyframe
3083                         MaskAuto temp_keyframe(mwindow->edl, autos);
3084                         temp_keyframe.copy_data(keyframe);
3085 // Get affected point in temp keyframe
3086                         mask = temp_keyframe.get_submask(mwindow->edl->session->cwindow_mask);
3087 #endif
3088                         MaskPoints &points = mask->points;
3089                         int gang = gang_point->get_value();
3090                         int k = mwindow->cwindow->gui->affected_point;
3091                         int n = gang ? points.size() : k+1;
3092                         for( int i=gang? 0 : k; i<n; ++i ) {
3093                                 if( i < 0 || i >= points.size() ) continue;
3094                                 MaskPoint *point = points[i];
3095                                 point->x += dx;  point->y += dy;
3096                         }
3097 #ifdef USE_KEYFRAME_SPANNING
3098 // Commit to spanned keyframes
3099                         autos->update_parameter(&temp_keyframe);
3100 #endif
3101                 }
3102                 mwindow->undo->update_undo_after(_("mask point"), LOAD_AUTOMATION);
3103                 redraw = 1;
3104         }
3105
3106         if( redraw )
3107                 update_preview();
3108 }
3109
3110 void CWindowMaskGUI::set_focused(int v, float cx, float cy)
3111 {
3112         CWindowGUI *cgui = mwindow->cwindow->gui;
3113         cgui->unlock_window();
3114         lock_window("CWindowMaskGUI::set_focused");
3115         if( focused != v )
3116                 focus->update(focused = v);
3117         focus_x->update(cx);
3118         focus_y->update(cy);
3119         unlock_window();
3120         cgui->lock_window("CWindowCanvas::set_focused");
3121 }
3122
3123 void CWindowMaskGUI::update_buttons(MaskAuto *keyframe, int k)
3124 {
3125         int text_color = get_resources()->default_text_color;
3126         int high_color = get_resources()->button_highlighted;
3127         for( int i=0; i<SUBMASKS; ++i ) {
3128                 int color = text_color;
3129                 if( keyframe ) {
3130                         SubMask *submask = keyframe->get_submask(i);
3131                         if( submask && submask->points.size() )
3132                                 color = high_color;
3133                 }
3134                 mask_blabels[i]->set_color(color);
3135                 mask_buttons[i]->update(i==k ? 1 : 0);
3136         }
3137 }
3138
3139 // typ=0, this mask, this point
3140 // typ>0, this mask, all points
3141 // typ<0, all masks, all points
3142 // dxy= on? pt[+1]-pt[-1] : dxy=0
3143 int CWindowMaskGUI::smooth_mask(int typ, int on)
3144 {
3145         MaskAutos *autos;
3146         MaskAuto *keyframe;
3147         Track *track;
3148         MaskPoint *point;
3149         SubMask *mask;
3150 #ifdef USE_KEYFRAME_SPANNING
3151         int create_it = 0;
3152 #else
3153         int create_it = 1;
3154 #endif
3155
3156         mwindow->undo->update_undo_before(_("mask smooth"), this);
3157
3158 // Get existing keyframe
3159         get_keyframe(track, autos, keyframe,
3160                         mask, point, create_it);
3161         if( track ) {
3162 #ifdef USE_KEYFRAME_SPANNING
3163                 MaskAuto temp_keyframe(mwindow->edl, autos);
3164                 temp_keyframe.copy_data(keyframe);
3165                 keyframe = &temp_keyframe;
3166 #endif
3167                 int k = mwindow->edl->session->cwindow_mask;
3168                 int n = typ>=0 ? k+1 : keyframe->masks.size();
3169                 for( int j=typ<0? 0 : k; j<n; ++j ) {
3170                         if( !mask_enables[j]->get_value() ) continue;
3171                         SubMask *sub_mask = keyframe->get_submask(j);
3172                         MaskPoints &points = sub_mask->points;
3173                         int psz = points.size();
3174                         if( psz < 3 ) continue;
3175                         int l = mwindow->cwindow->gui->affected_point;
3176                         if( l > psz ) l = psz;
3177                         int m = typ ? psz : l+1;
3178                         for( int i=typ ? 0 : l; i<m; ++i ) {
3179                                 int i0 = i-1, i1 = i+1;
3180                                 if( i0 < 0 ) i0 = psz-1;
3181                                 if( i1 >= psz ) i1 = 0;
3182                                 MaskPoint *p0 = points[i0];
3183                                 MaskPoint *p  = points[i];
3184                                 MaskPoint *p1 = points[i1];
3185                                 float dx = !on ? 0 : p1->x - p0->x;
3186                                 float dy = !on ? 0 : p1->y - p0->y;
3187                                 p->control_x1 = -dx/4;  p->control_y1 = -dy/4;
3188                                 p->control_x2 =  dx/4;  p->control_y2 =  dy/4;
3189                         }
3190                 }
3191 #ifdef USE_KEYFRAME_SPANNING
3192                 autos->update_parameter(keyframe);
3193 #endif
3194                 update_preview();
3195         }
3196
3197         mwindow->undo->update_undo_after(_("mask smooth"), LOAD_AUTOMATION);
3198         return 1;
3199 }
3200
3201 int CWindowMaskGUI::save_mask(const char *nm)
3202 {
3203         int k = mwindow->edl->session->cwindow_mask;
3204         MaskAutos *autos;
3205         MaskAuto *keyframe;
3206         Track *track;
3207         MaskPoint *point;
3208         SubMask *mask;
3209         get_keyframe(track, autos, keyframe, mask, point, 0);
3210         if( !track ) return 0;
3211         SubMask *sub_mask = keyframe->get_submask(k);
3212         ArrayList<SubMask *> masks;
3213         load_masks(masks);
3214         int i = masks.size();
3215         while( --i >= 0 ) {
3216                 if( strcmp(masks[i]->name, nm) ) continue;
3217                 masks.remove_object_number(i++);
3218         }
3219         mask = new SubMask(0, -1);
3220         strncpy(mask->name, nm, sizeof(mask->name)-1);
3221         mask->copy_from(*sub_mask, 0);
3222         masks.append(mask);
3223         save_masks(masks);
3224         masks.remove_all_objects();
3225         return 1;
3226 }
3227
3228 int CWindowMaskGUI::del_mask(const char *nm)
3229 {
3230         ArrayList<SubMask *> masks;
3231         load_masks(masks);
3232         int i = masks.size();
3233         while( --i >= 0 ) {
3234                 if( strcmp(masks[i]->name, nm) ) continue;
3235                 masks.remove_object_number(i++);
3236         }
3237         save_masks(masks);
3238         masks.remove_all_objects();
3239         return 1;
3240 }
3241
3242 int CWindowMaskGUI::center_mask()
3243 {
3244         int k = mwindow->edl->session->cwindow_mask;
3245         MaskAutos *autos;
3246         MaskAuto *keyframe;
3247         Track *track;
3248         MaskPoint *point;
3249         SubMask *mask;
3250 #ifdef USE_KEYFRAME_SPANNING
3251         int create_it = 0;
3252 #else
3253         int create_it = 1;
3254 #endif
3255         get_keyframe(track, autos, keyframe,
3256                         mask, point, create_it);
3257         if( !track ) return 0;
3258         mwindow->undo->update_undo_before(_("mask center"), this);
3259
3260 // Get existing keyframe
3261 #ifdef USE_KEYFRAME_SPANNING
3262         MaskAuto temp_keyframe(mwindow->edl, autos);
3263         temp_keyframe.copy_data(keyframe);
3264         keyframe = &temp_keyframe;
3265 #endif
3266         SubMask *sub_mask = keyframe->get_submask(k);
3267         MaskPoints &points = sub_mask->points;
3268         int psz = points.size();
3269         if( psz > 0 ) {
3270                 float cx = 0, cy = 0;
3271                 for( int i=0; i<psz; ++i ) {
3272                         MaskPoint *p  = points[i];
3273                         cx += p->x;  cy += p->y;
3274                 }
3275                 cx /= psz;  cy /= psz;
3276                 cx -= mwindow->edl->session->output_w / 2.f;
3277                 cy -= mwindow->edl->session->output_h / 2.f;
3278                 for( int i=0; i<psz; ++i ) {
3279                         MaskPoint *p  = points[i];
3280                         p->x -= cx;  p->y -= cy;
3281                 }
3282         }
3283 #ifdef USE_KEYFRAME_SPANNING
3284         autos->update_parameter(keyframe);
3285 #endif
3286         update_preview();
3287         mwindow->undo->update_undo_after(_("mask center"), LOAD_AUTOMATION);
3288         return 1;
3289 }
3290
3291 int CWindowMaskGUI::normal_mask()
3292 {
3293         int k = mwindow->edl->session->cwindow_mask;
3294         MaskAutos *autos;
3295         MaskAuto *keyframe;
3296         Track *track;
3297         MaskPoint *point;
3298         SubMask *mask;
3299 #ifdef USE_KEYFRAME_SPANNING
3300         int create_it = 0;
3301 #else
3302         int create_it = 1;
3303 #endif
3304 // Get existing keyframe
3305         get_keyframe(track, autos, keyframe,
3306                         mask, point, create_it);
3307         if( !track ) return 0;
3308         mwindow->undo->update_undo_before(_("mask normal"), this);
3309
3310 #ifdef USE_KEYFRAME_SPANNING
3311         MaskAuto temp_keyframe(mwindow->edl, autos);
3312         temp_keyframe.copy_data(keyframe);
3313         keyframe = &temp_keyframe;
3314 #endif
3315         SubMask *sub_mask = keyframe->get_submask(k);
3316         MaskPoints &points = sub_mask->points;
3317         int psz = points.size();
3318         float cx = 0, cy = 0;
3319         double dr = 0;
3320         if( psz > 0 ) {
3321                 for( int i=0; i<psz; ++i ) {
3322                         MaskPoint *p  = points[i];
3323                         cx += p->x;  cy += p->y;
3324                 }
3325                 cx /= psz;  cy /= psz;
3326                 for( int i=0; i<psz; ++i ) {
3327                         MaskPoint *p  = points[i];
3328                         float dx = fabsf(p->x-cx), dy = fabsf(p->y-cy);
3329                         double d = sqrt(dx*dx + dy*dy);
3330                         if( dr < d ) dr = d;
3331                 }
3332         }
3333         if( dr > 0 ) {
3334                 float out_w = mwindow->edl->session->output_w;
3335                 float out_h = mwindow->edl->session->output_h;
3336                 float r = bmax(out_w, out_h);
3337                 float s = r / (4 * dr * sqrt(2.));
3338                 for( int i=0; i<psz; ++i ) {
3339                         MaskPoint *p  = points[i];
3340                         float x = p->x, y = p->y;
3341                         p->x = (x-cx) * s + cx;
3342                         p->y = (y-cy) * s + cy;
3343                         p->control_x1 *= s;  p->control_y1 *= s;
3344                         p->control_x2 *= s;  p->control_y2 *= s;
3345                 }
3346         }
3347 #ifdef USE_KEYFRAME_SPANNING
3348         autos->update_parameter(keyframe);
3349 #endif
3350         update_preview();
3351
3352         mwindow->undo->update_undo_after(_("mask normal"), LOAD_AUTOMATION);
3353         return 1;
3354 }
3355
3356
3357 CWindowMaskLoadList::CWindowMaskLoadList(MWindow *mwindow, CWindowMaskGUI *gui)
3358  : BC_ListBox(-1, -1, 1, 1, LISTBOX_TEXT, 0, 0, 0, 1, 0, 1)
3359 {
3360         this->mwindow = mwindow;
3361         this->gui = gui;
3362         set_use_button(0);
3363 }
3364
3365 CWindowMaskLoadList::~CWindowMaskLoadList()
3366 {
3367 }
3368
3369
3370 int CWindowMaskLoadList::handle_event()
3371 {
3372         MaskAutos *autos;
3373         MaskAuto *keyframe;
3374         Track *track;
3375         MaskPoint *point;
3376         SubMask *mask;
3377 #ifdef USE_KEYFRAME_SPANNING
3378         int create_it = 0;
3379 #else
3380         int create_it = 1;
3381 #endif
3382
3383         mwindow->undo->update_undo_before(_("mask shape"), this);
3384
3385 // Get existing keyframe
3386         gui->get_keyframe(track, autos, keyframe,
3387                         mask, point, create_it);
3388         CWindowMaskItem *item = (CWindowMaskItem *) get_selection(0, 0);
3389         if( track && item ) {
3390 #ifdef USE_KEYFRAME_SPANNING
3391                 MaskAuto temp_keyframe(mwindow->edl, autos);
3392                 temp_keyframe.copy_data(keyframe);
3393                 keyframe = &temp_keyframe;
3394                 mask = temp_keyframe.get_submask(mwindow->edl->session->cwindow_mask);
3395 #endif
3396                 ArrayList<SubMask *> masks;
3397                 gui->load_masks(masks);
3398                 mask->copy_from(*masks[item->id], 0);
3399                 masks.remove_all_objects();
3400 #ifdef USE_KEYFRAME_SPANNING
3401                 autos->update_parameter(keyframe);
3402 #endif
3403                 gui->update();
3404                 gui->update_preview(1);
3405         }
3406         mwindow->undo->update_undo_after(_("mask shape"), LOAD_AUTOMATION);
3407         return 1;
3408 }
3409
3410 void CWindowMaskLoadList::create_objects()
3411 {
3412         shape_items.remove_all_objects();
3413         ArrayList<SubMask *> masks;
3414         gui->load_masks(masks);
3415         for( int i=0; i<masks.size(); ++i )
3416                 shape_items.append(new CWindowMaskItem(masks[i]->name, i));
3417         masks.remove_all_objects();
3418         update(&shape_items, 0, 0, 1);
3419 }
3420
3421 CWindowMaskLoad::CWindowMaskLoad(MWindow *mwindow,
3422         CWindowMaskGUI *gui, int x, int y, int w)
3423  : BC_Button(x, y, mwindow->theme->get_image_set("mask_prst_load_images"))
3424 {
3425         this->mwindow = mwindow;
3426         this->gui = gui;
3427         set_tooltip(_("Load preset"));
3428 }
3429
3430 int CWindowMaskLoad::handle_event()
3431 {
3432         gui->mask_load_list->create_objects();
3433         int px, py;
3434         get_abs_cursor(px, py);
3435         return gui->mask_load_list->activate(px, py, xS(120),yS(160));
3436 }
3437
3438
3439 CWindowMaskSave::CWindowMaskSave(MWindow *mwindow,
3440         CWindowMaskGUI *gui, int x, int y, int w)
3441  : BC_Button(x, y, mwindow->theme->get_image_set("mask_prst_save_images"))
3442 {
3443         this->mwindow = mwindow;
3444         this->gui = gui;
3445         set_tooltip(_("Save preset"));
3446 }
3447
3448 CWindowMaskSave::~CWindowMaskSave()
3449 {
3450 }
3451
3452 int CWindowMaskSave::handle_event()
3453 {
3454         Track *track;
3455         MaskAutos *autos;
3456         MaskAuto *keyframe;
3457         SubMask *mask;
3458         MaskPoint *point;
3459         gui->get_keyframe(track, autos, keyframe, mask, point, 0);
3460         if( track ) {
3461                 int sx = 0, sy = 0;
3462                 gui->get_abs_cursor(sx, sy);
3463                 if( !gui->preset_dialog )
3464                         gui->preset_dialog = new CWindowMaskPresetDialog(mwindow, gui);
3465                 gui->preset_dialog->start_dialog(sx, sy, keyframe);
3466         }
3467         return 1;
3468 }
3469
3470 CWindowMaskPresetDialog::CWindowMaskPresetDialog(MWindow *mwindow, CWindowMaskGUI *gui)
3471  : BC_DialogThread()
3472 {
3473         this->mwindow = mwindow;
3474         this->gui = gui;
3475         pgui = 0;
3476 }
3477
3478 CWindowMaskPresetDialog::~CWindowMaskPresetDialog()
3479 {
3480         close_window();
3481 }
3482
3483 void CWindowMaskPresetDialog::handle_close_event(int result)
3484 {
3485         pgui = 0;
3486 }
3487
3488 void CWindowMaskPresetDialog::handle_done_event(int result)
3489 {
3490         if( result ) return;
3491         const char *nm = pgui->preset_text->get_text();
3492         if( keyframe )
3493                 gui->save_mask(nm);
3494         else
3495                 gui->del_mask(nm);
3496 }
3497
3498 BC_Window* CWindowMaskPresetDialog::new_gui()
3499 {
3500         pgui = new CWindowMaskPresetGUI(this, sx, sy,
3501                 keyframe ? _(PROGRAM_NAME ": Save Mask") :
3502                            _(PROGRAM_NAME ": Delete Mask"));
3503         pgui->create_objects();
3504         return pgui;
3505 }
3506
3507 void CWindowMaskPresetDialog::start_dialog(int sx, int sy, MaskAuto *keyframe)
3508 {
3509         close_window();
3510         this->sx = sx;  this->sy = sy;
3511         this->keyframe = keyframe;
3512         start();
3513 }
3514
3515 CWindowMaskPresetGUI::CWindowMaskPresetGUI(CWindowMaskPresetDialog *preset_dialog,
3516                         int x, int y, const char *title)
3517  : BC_Window(title, x, y, xS(320), yS(100), xS(320), yS(100), 0, 0, 1)
3518 {
3519         this->preset_dialog = preset_dialog;
3520 }
3521
3522 void CWindowMaskPresetGUI::create_objects()
3523 {
3524         int xs8 = xS(8), xs10 = xS(10);
3525         int ys10 = yS(10);
3526         int x = xs10, y = ys10;
3527         lock_window("CWindowMaskPresetGUI::create_objects");
3528         BC_Title *title;
3529         add_subwindow(title = new BC_Title(x, y,
3530                 preset_dialog->keyframe ? _("Save mask:") : _("Delete mask:")));
3531         int x1 = x + title->get_w() + xs8;
3532         int x2 = get_w() - x - xs8 - x1 -
3533                 BC_WindowBase::get_resources()->listbox_button[0]->get_w();
3534         CWindowMaskGUI *gui = preset_dialog->gui;
3535         preset_text = new CWindowMaskPresetText(this,
3536                 x1, y, x2, yS(120), gui->mask_name->get_text());
3537         preset_text->create_objects();
3538         preset_text->set_tooltip(_("Mask name"));
3539         preset_text->update_items();
3540         add_subwindow(new BC_OKButton(this));
3541         add_subwindow(new BC_CancelButton(this));
3542         show_window();
3543         raise_window();
3544         unlock_window();
3545 }
3546
3547 CWindowMaskPresetText::CWindowMaskPresetText(CWindowMaskPresetGUI *pgui,
3548                 int x, int y, int w, int h, const char *text)
3549  : BC_PopupTextBox(pgui, 0, text, x, y, w, h)
3550 {
3551         this->pgui = pgui;
3552 }
3553
3554 int CWindowMaskPresetText::handle_event()
3555 {
3556         int k = get_number();
3557         if( k >= 0 && k<mask_items.size() )
3558                 update(mask_items[k]->get_text());
3559         return 1;
3560 }
3561
3562 void CWindowMaskPresetText::update_items()
3563 {
3564         mask_items.remove_all_objects();
3565         ArrayList<SubMask *> masks;
3566         pgui->preset_dialog->gui->load_masks(masks);
3567         for( int i=0; i<masks.size(); ++i ) {
3568                 char text[BCSTRLEN];  memset(text, 0, sizeof(text));
3569                 strncpy(text, masks[i]->name, sizeof(text)-1);
3570                 mask_items.append(new CWindowMaskItem(text));
3571         }
3572         masks.remove_all_objects();
3573         update_list(&mask_items);
3574 }
3575
3576
3577 CWindowMaskDelete::CWindowMaskDelete(MWindow *mwindow,
3578         CWindowMaskGUI *gui, int x, int y, int w)
3579  : BC_Button(x, y, mwindow->theme->get_image_set("mask_prst_trsh_images"))
3580 {
3581         this->mwindow = mwindow;
3582         this->gui = gui;
3583         set_tooltip(_("Delete preset"));
3584 }
3585
3586 int CWindowMaskDelete::handle_event()
3587 {
3588         int sx = 0, sy = 0;
3589         gui->get_abs_cursor(sx, sy);
3590         if( !gui->preset_dialog )
3591                 gui->preset_dialog = new CWindowMaskPresetDialog(mwindow, gui);
3592         gui->preset_dialog->start_dialog(sx, sy, 0);
3593         return 1;
3594 }
3595
3596
3597 CWindowMaskCenter::CWindowMaskCenter(MWindow *mwindow,
3598         CWindowMaskGUI *gui, int x, int y, int w)
3599  : BC_Button(x, y, mwindow->theme->get_image_set("mask_pstn_cen_images"))
3600 {
3601         this->mwindow = mwindow;
3602         this->gui = gui;
3603         set_tooltip(_("center mask"));
3604 }
3605
3606 int CWindowMaskCenter::handle_event()
3607 {
3608         return gui->center_mask();
3609 }
3610
3611
3612 CWindowMaskNormal::CWindowMaskNormal(MWindow *mwindow,
3613         CWindowMaskGUI *gui, int x, int y, int w)
3614  : BC_Button(x, y, mwindow->theme->get_image_set("mask_pstn_nrm_images"))
3615 {
3616         this->mwindow = mwindow;
3617         this->gui = gui;
3618         set_tooltip(_("normalize mask"));
3619 }
3620
3621 int CWindowMaskNormal::handle_event()
3622 {
3623         return gui->normal_mask();
3624 }
3625
3626
3627 CWindowMaskShape::CWindowMaskShape(MWindow *mwindow, CWindowMaskGUI *gui,
3628                 const char *images, int shape, int x, int y, const char *tip)
3629  : BC_Button(x, y, mwindow->theme->get_image_set(images))
3630 {
3631         this->mwindow = mwindow;
3632         this->gui = gui;
3633         this->shape = shape;
3634         set_tooltip(tip);
3635 }
3636
3637 CWindowMaskShape::~CWindowMaskShape()
3638 {
3639 }
3640
3641 void CWindowMaskShape::builtin_shape(int i, SubMask *sub_mask)
3642 {
3643         int out_w = mwindow->edl->session->output_w;
3644         int out_h = mwindow->edl->session->output_h;
3645         float cx = out_w/2.f, cy = out_h/2.f;
3646         float r = bmax(cx, cy) / 4.f;
3647         double c = 4*(sqrt(2.)-1)/3; // bezier aprox circle
3648         float r2 = r / 2.f, rc = r*c, r4 = r / 4.f;
3649         MaskPoint *pt = 0;
3650         MaskPoints &points = sub_mask->points;
3651         points.remove_all_objects();
3652         switch( i ) {
3653         case MASK_SHAPE_SQUARE:
3654                 points.append(pt = new MaskPoint());
3655                 pt->x = cx - r;  pt->y = cy - r;
3656                 points.append(pt = new MaskPoint());
3657                 pt->x = cx + r;  pt->y = cy - r;
3658                 points.append(pt = new MaskPoint());
3659                 pt->x = cx + r;  pt->y = cy + r;
3660                 points.append(pt = new MaskPoint());
3661                 pt->x = cx - r;  pt->y = cy + r;
3662                 break;
3663         case MASK_SHAPE_CIRCLE:
3664                 points.append(pt = new MaskPoint());
3665                 pt->x = cx - r;  pt->y = cy - r;
3666                 pt->control_x1 = -rc;  pt->control_y1 =  rc;
3667                 pt->control_x2 =  rc;  pt->control_y2 = -rc;
3668                 points.append(pt = new MaskPoint());
3669                 pt->x = cx + r;  pt->y = cy - r;
3670                 pt->control_x1 = -rc;  pt->control_y1 = -rc;
3671                 pt->control_x2 =  rc;  pt->control_y2 =  rc;
3672                 points.append(pt = new MaskPoint());
3673                 pt->x = cx + r;  pt->y = cy + r;
3674                 pt->control_x1 =  rc;  pt->control_y1 = -rc;
3675                 pt->control_x2 = -rc;  pt->control_y2 =  rc;
3676                 points.append(pt = new MaskPoint());
3677                 pt->x = cx - r;  pt->y = cy + r;
3678                 pt->control_x1 =  rc;  pt->control_y1 =  rc;
3679                 pt->control_x2 = -rc;  pt->control_y2 = -rc;
3680                 break;
3681         case MASK_SHAPE_TRIANGLE:
3682                 points.append(pt = new MaskPoint());
3683                 pt->x = cx + 0;  pt->y = cy - r*(sqrt(3.)-1.);
3684                 points.append(pt = new MaskPoint());
3685                 pt->x = cx + r;  pt->y = cy + r;
3686                 points.append(pt = new MaskPoint());
3687                 pt->x = cx - r;  pt->y = cy + r;
3688                 break;
3689         case MASK_SHAPE_OVAL:
3690                 points.append(pt = new MaskPoint());
3691                 pt->x = cx - r;  pt->y = cy - r2;
3692                 pt->control_x1 = -r2;  pt->control_y1 =  r4;
3693                 pt->control_x2 =  r2;  pt->control_y2 = -r4;
3694                 points.append(pt = new MaskPoint());
3695                 pt->x = cx + r;  pt->y = cy - r2;
3696                 pt->control_x1 = -r2;  pt->control_y1 = -r4;
3697                 pt->control_x2 =  r2;  pt->control_y2 =  r4;
3698                 points.append(pt = new MaskPoint());
3699                 pt->x = cx + r;  pt->y = cy + r2;
3700                 pt->control_x1 =  r2;  pt->control_y1 = -r4;
3701                 pt->control_x2 = -r2;  pt->control_y2 =  r4;
3702                 points.append(pt = new MaskPoint());
3703                 pt->x = cx - r;  pt->y = cy + r2;
3704                 pt->control_x1 =  r2;  pt->control_y1 =  r4;
3705                 pt->control_x2 = -r2;  pt->control_y2 = -r4;
3706                 break;
3707         }
3708 }
3709
3710 int CWindowMaskShape::handle_event()
3711 {
3712         MaskAutos *autos;
3713         MaskAuto *keyframe;
3714         Track *track;
3715         MaskPoint *point;
3716         SubMask *mask;
3717 #ifdef USE_KEYFRAME_SPANNING
3718         int create_it = 0;
3719 #else
3720         int create_it = 1;
3721 #endif
3722
3723         mwindow->undo->update_undo_before(_("mask shape"), this);
3724
3725 // Get existing keyframe
3726         gui->get_keyframe(track, autos, keyframe,
3727                         mask, point, create_it);
3728         if( track ) {
3729 #ifdef USE_KEYFRAME_SPANNING
3730                 MaskAuto temp_keyframe(mwindow->edl, autos);
3731                 temp_keyframe.copy_data(keyframe);
3732                 keyframe = &temp_keyframe;
3733                 mask = temp_keyframe.get_submask(mwindow->edl->session->cwindow_mask);
3734 #endif
3735                 if( mask ) {
3736                         builtin_shape(shape, mask);
3737 #ifdef USE_KEYFRAME_SPANNING
3738                         autos->update_parameter(keyframe);
3739 #endif
3740                         gui->update();
3741                         gui->update_preview(1);
3742                 }
3743         }
3744         mwindow->undo->update_undo_after(_("mask shape"), LOAD_AUTOMATION);
3745         return 1;
3746 }
3747
3748 void CWindowMaskGUI::load_masks(ArrayList<SubMask *> &masks)
3749 {
3750         char path[BCTEXTLEN];
3751         sprintf(path, "%s/%s", File::get_config_path(), MASKS_FILE);
3752         FileSystem fs;
3753         fs.complete_path(path);
3754         FileXML file;
3755         file.read_from_file(path, 1);
3756
3757         masks.remove_all_objects();
3758         int result;
3759         while( !(result = file.read_tag()) ) {
3760                 if( file.tag.title_is("MASK") ) {
3761                         SubMask *sub_mask = new SubMask(0, -1);
3762                         char name[BCTEXTLEN];  name[0] = 0;
3763                         file.tag.get_property("NAME", name);
3764                         strncpy(sub_mask->name, name, sizeof(sub_mask->name));
3765                         sub_mask->load(&file);
3766                         masks.append(sub_mask);
3767                 }
3768         }
3769 }
3770
3771 void CWindowMaskGUI::save_masks(ArrayList<SubMask *> &masks)
3772 {
3773         FileXML file;
3774         for( int i=0; i<masks.size(); ++i ) {
3775                 SubMask *sub_mask = masks[i];
3776                 sub_mask->copy(&file);
3777         }
3778         file.terminate_string();
3779
3780         char path[BCTEXTLEN];
3781         sprintf(path, "%s/%s", File::get_config_path(), MASKS_FILE);
3782         FileSystem fs;
3783         fs.complete_path(path);
3784         file.write_to_file(path);
3785 }
3786
3787
3788 CWindowRulerGUI::CWindowRulerGUI(MWindow *mwindow, CWindowTool *thread)
3789  : CWindowToolGUI(mwindow, thread, _(PROGRAM_NAME ": Ruler"), xS(320), yS(240))
3790 {
3791 }
3792
3793 CWindowRulerGUI::~CWindowRulerGUI()
3794 {
3795 }
3796
3797 void CWindowRulerGUI::create_objects()
3798 {
3799         int xs10 = xS(10), xs200 = xS(200);
3800         int ys5 = yS(5), ys10 = yS(10);
3801         int x = xs10, y = ys10, x1 = xS(100);
3802         BC_Title *title;
3803
3804         lock_window("CWindowRulerGUI::create_objects");
3805         add_subwindow(title = new BC_Title(x, y, _("Current:")));
3806         add_subwindow(current = new BC_TextBox(x1, y, xs200, 1, ""));
3807         y += title->get_h() + ys5;
3808         add_subwindow(title = new BC_Title(x, y, _("Point 1:")));
3809         add_subwindow(point1 = new BC_TextBox(x1, y, xs200, 1, ""));
3810         y += title->get_h() + ys5;
3811         add_subwindow(title = new BC_Title(x, y, _("Point 2:")));
3812         add_subwindow(point2 = new BC_TextBox(x1, y, xs200, 1, ""));
3813         y += title->get_h() + ys5;
3814         add_subwindow(title = new BC_Title(x, y, _("Deltas:")));
3815         add_subwindow(deltas = new BC_TextBox(x1, y, xs200, 1, ""));
3816         y += title->get_h() + ys5;
3817         add_subwindow(title = new BC_Title(x, y, _("Distance:")));
3818         add_subwindow(distance = new BC_TextBox(x1, y, xs200, 1, ""));
3819         y += title->get_h() + ys5;
3820         add_subwindow(title = new BC_Title(x, y, _("Angle:")));
3821         add_subwindow(angle = new BC_TextBox(x1, y, xs200, 1, ""));
3822         y += title->get_h() + ys10;
3823         char string[BCTEXTLEN];
3824         sprintf(string,
3825                  _("Press Ctrl to lock ruler to the\nnearest 45%c%c angle."),
3826                 0xc2, 0xb0); // degrees utf
3827         add_subwindow(title = new BC_Title(x,
3828                 y,
3829                 string));
3830         y += title->get_h() + ys10;
3831         sprintf(string, _("Press Alt to translate the ruler."));
3832         add_subwindow(title = new BC_Title(x,
3833                 y,
3834                 string));
3835         update();
3836         unlock_window();
3837 }
3838
3839 void CWindowRulerGUI::update()
3840 {
3841         char string[BCTEXTLEN];
3842         int cx = mwindow->session->cwindow_output_x;
3843         int cy = mwindow->session->cwindow_output_y;
3844         sprintf(string, "%d, %d", cx, cy);
3845         current->update(string);
3846         double x1 = mwindow->edl->session->ruler_x1;
3847         double y1 = mwindow->edl->session->ruler_y1;
3848         sprintf(string, "%.0f, %.0f", x1, y1);
3849         point1->update(string);
3850         double x2 = mwindow->edl->session->ruler_x2;
3851         double y2 = mwindow->edl->session->ruler_y2;
3852         sprintf(string, "%.0f, %.0f", x2, y2);
3853         point2->update(string);
3854         double dx = x2 - x1, dy = y2 - y1;
3855         sprintf(string, "%s%.0f, %s%.0f", (dx>=0? "+":""), dx, (dy>=0? "+":""), dy);
3856         deltas->update(string);
3857         double d = sqrt(dx*dx + dy*dy);
3858         sprintf(string, _("%0.01f pixels"), d);
3859         distance->update(string);
3860         double a = d > 0 ? (atan2(-dy, dx) * 180/M_PI) : 0.;
3861         sprintf(string, "%0.02f %c%c", a, 0xc2, 0xb0);
3862         angle->update(string);
3863 }
3864
3865 void CWindowRulerGUI::handle_event()
3866 {
3867 }
3868