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