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