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