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