fix popup_menu icon placement, add layout_name feature, preset edit resize wdw fix...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / canvas.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008-2017 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcsignals.h"
23 #include "canvas.h"
24 #include "clip.h"
25 #include "edl.h"
26 #include "edlsession.h"
27 #include "keys.h"
28 #include "language.h"
29 #include "mainsession.h"
30 #include "mwindowgui.h"
31 #include "mutex.h"
32 #include "mwindow.h"
33 #include "playback3d.h"
34 #include "videodevice.h"
35 #include "vframe.h"
36
37
38
39 Canvas::Canvas(MWindow *mwindow,
40         BC_WindowBase *subwindow,
41         int x,
42         int y,
43         int w,
44         int h,
45         int output_w,
46         int output_h,
47         int use_scrollbars)
48 {
49         reset();
50
51         if(w < 10) w = 10;
52         if(h < 10) h = 10;
53         this->mwindow = mwindow;
54         this->subwindow = subwindow;
55         this->x = x;
56         this->y = y;
57         this->w = w;
58         this->h = h;
59         this->output_w = output_w;
60         this->output_h = output_h;
61         this->use_scrollbars = use_scrollbars;
62         this->canvas_auxwindow = 0;
63         this->scr_w0 = subwindow->get_screen_w(0, 0);
64         this->root_w = subwindow->get_root_w(0);
65         this->root_h = subwindow->get_root_h(0);
66         canvas_lock = new Mutex("Canvas::canvas_lock", 1);
67 }
68
69 Canvas::~Canvas()
70 {
71         if(refresh_frame) delete refresh_frame;
72         delete canvas_menu;
73         if(yscroll) delete yscroll;
74         if(xscroll) delete xscroll;
75         delete canvas_subwindow;
76         delete canvas_fullscreen;
77         delete canvas_lock;
78 }
79
80 void Canvas::reset()
81 {
82         use_scrollbars = 0;
83         output_w = 0;
84         output_h = 0;
85         xscroll = 0;
86         yscroll = 0;
87         refresh_frame = 0;
88         canvas_subwindow = 0;
89         canvas_fullscreen = 0;
90         is_processing = 0;
91         is_fullscreen = 0;
92         cursor_inside = 0;
93 }
94
95 void Canvas::lock_canvas(const char *location)
96 {
97         canvas_lock->lock(location);
98 }
99
100 void Canvas::unlock_canvas()
101 {
102         canvas_lock->unlock();
103 }
104
105 int Canvas::is_locked()
106 {
107         return canvas_lock->is_locked();
108 }
109
110
111 BC_WindowBase* Canvas::get_canvas()
112 {
113         if(get_fullscreen() && canvas_fullscreen)
114                 return canvas_fullscreen;
115         return canvas_auxwindow ? canvas_auxwindow : canvas_subwindow;
116 }
117
118 void Canvas::use_auxwindow(BC_WindowBase *aux)
119 {
120         canvas_auxwindow = aux;
121 }
122
123 void Canvas::use_cwindow()
124 {
125         canvas_menu->use_cwindow();
126         fullscreen_menu->use_cwindow();
127 }
128
129 void Canvas::use_rwindow()
130 {
131         canvas_menu->use_rwindow();
132 }
133
134 void Canvas::use_vwindow()
135 {
136         canvas_menu->use_vwindow();
137 }
138
139 int Canvas::get_fullscreen()
140 {
141         return is_fullscreen;
142 }
143
144 void Canvas::set_fullscreen(int value)
145 {
146         is_fullscreen = value;
147 }
148
149 // Get dimensions given a zoom
150 void Canvas::calculate_sizes(float aspect_ratio,
151         int output_w,
152         int output_h,
153         float zoom,
154         int &w,
155         int &h)
156 {
157 // Horizontal stretch
158         if((float)output_w / output_h <= aspect_ratio)
159         {
160                 w = (int)((float)output_h * aspect_ratio * zoom);
161                 h = (int)((float)output_h * zoom);
162         }
163         else
164 // Vertical stretch
165         {
166                 h = (int)((float)output_w / aspect_ratio * zoom);
167                 w = (int)((float)output_w * zoom);
168         }
169 }
170
171 float Canvas::get_x_offset(EDL *edl,
172         int single_channel,
173         float zoom_x,
174         float conformed_w,
175         float conformed_h)
176 {
177         if(use_scrollbars)
178         {
179                 if(xscroll)
180                 {
181 // If the projection is smaller than the canvas, this forces it in the center.
182 //                      if(conformed_w < w_visible)
183 //                              return -(float)(w_visible - conformed_w) / 2;
184
185                         return (float)get_xscroll();
186                 }
187                 else
188                         return ((float)-get_canvas()->get_w() / zoom_x +
189                                 edl->session->output_w) / 2;
190         }
191         else
192         {
193                 int out_w, out_h;
194                 int canvas_w = get_canvas()->get_w();
195                 int canvas_h = get_canvas()->get_h();
196                 out_w = canvas_w;
197                 out_h = canvas_h;
198
199                 if((float)out_w / out_h > conformed_w / conformed_h)
200                 {
201                         out_w = (int)(out_h * conformed_w / conformed_h + 0.5);
202                 }
203
204                 if(out_w < canvas_w)
205                         return -(canvas_w - out_w) / 2 / zoom_x;
206         }
207
208         return 0;
209 }
210
211 float Canvas::get_y_offset(EDL *edl,
212         int single_channel,
213         float zoom_y,
214         float conformed_w,
215         float conformed_h)
216 {
217         if(use_scrollbars)
218         {
219                 if(yscroll)
220                 {
221 // If the projection is smaller than the canvas, this forces it in the center.
222 //                      if(conformed_h < h_visible)
223 //                              return -(float)(h_visible - conformed_h) / 2;
224
225                         return (float)get_yscroll();
226                 }
227                 else
228                         return ((float)-get_canvas()->get_h() / zoom_y +
229                                 edl->session->output_h) / 2;
230         }
231         else
232         {
233                 int out_w, out_h;
234                 int canvas_w = get_canvas()->get_w();
235                 int canvas_h = get_canvas()->get_h();
236                 out_w = canvas_w;
237                 out_h = canvas_h;
238
239                 if((float)out_w / out_h <= conformed_w / conformed_h)
240                 {
241                         out_h = (int)((float)out_w / (conformed_w / conformed_h) + 0.5);
242                 }
243
244 //printf("Canvas::get_y_offset 1 %d %d %f\n", out_h, canvas_h, -((float)canvas_h - out_h) / 2);
245                 if(out_h < canvas_h)
246                         return -((float)canvas_h - out_h) / 2 / zoom_y;
247         }
248
249         return 0;
250 }
251
252 // This may not be used anymore
253 void Canvas::check_boundaries(EDL *edl, int &x, int &y, float &zoom)
254 {
255         if(x + w_visible > w_needed) x = w_needed - w_visible;
256         if(y + h_visible > h_needed) y = h_needed - h_visible;
257
258         if(x < 0) x = 0;
259         if(y < 0) y = 0;
260 }
261
262 void Canvas::update_scrollbars(int flush)
263 {
264         if(use_scrollbars)
265         {
266                 if(xscroll) xscroll->update_length(w_needed, get_xscroll(), w_visible, flush);
267                 if(yscroll) yscroll->update_length(h_needed, get_yscroll(), h_visible, flush);
268         }
269 }
270
271 void Canvas::get_zooms(EDL *edl,
272         int single_channel,
273         float &zoom_x,
274         float &zoom_y,
275         float &conformed_w,
276         float &conformed_h)
277 {
278         edl->calculate_conformed_dimensions(single_channel,
279                 conformed_w,
280                 conformed_h);
281
282         if(use_scrollbars)
283         {
284                 zoom_x = get_zoom() *
285                         conformed_w /
286                         edl->session->output_w;
287                 zoom_y = get_zoom() *
288                         conformed_h /
289                         edl->session->output_h;
290         }
291         else
292         {
293                 int out_w, out_h;
294                 int canvas_w = get_canvas()->get_w();
295                 int canvas_h = get_canvas()->get_h();
296
297                 out_w = canvas_w;
298                 out_h = canvas_h;
299
300                 if((float)out_w / out_h > conformed_w / conformed_h)
301                 {
302                         out_w = (int)((float)out_h * conformed_w / conformed_h + 0.5);
303                 }
304                 else
305                 {
306                         out_h = (int)((float)out_w / (conformed_w / conformed_h) + 0.5);
307                 }
308
309                 zoom_x = (float)out_w / edl->session->output_w;
310                 zoom_y = (float)out_h / edl->session->output_h;
311 //printf("get zooms 2 %d %d %f %f\n", canvas_w, canvas_h, conformed_w, conformed_h);
312         }
313 }
314
315 // Convert a coordinate on the canvas to a coordinate on the output
316 void Canvas::canvas_to_output(EDL *edl, int single_channel, float &x, float &y)
317 {
318         float zoom_x, zoom_y, conformed_w, conformed_h;
319         get_zooms(edl, single_channel, zoom_x, zoom_y, conformed_w, conformed_h);
320
321 //printf("Canvas::canvas_to_output y=%f zoom_y=%f y_offset=%f\n",
322 //      y, zoom_y, get_y_offset(edl, single_channel, zoom_y, conformed_w, conformed_h));
323
324         x = (float)x / zoom_x + get_x_offset(edl, single_channel, zoom_x, conformed_w, conformed_h);
325         y = (float)y / zoom_y + get_y_offset(edl, single_channel, zoom_y, conformed_w, conformed_h);
326 }
327
328 void Canvas::output_to_canvas(EDL *edl, int single_channel, float &x, float &y)
329 {
330         float zoom_x, zoom_y, conformed_w, conformed_h;
331         get_zooms(edl, single_channel, zoom_x, zoom_y, conformed_w, conformed_h);
332
333 //printf("Canvas::output_to_canvas x=%f zoom_x=%f x_offset=%f\n", x, zoom_x, get_x_offset(edl, single_channel, zoom_x, conformed_w));
334
335         x = (float)zoom_x * (x - get_x_offset(edl, single_channel, zoom_x, conformed_w, conformed_h));
336         y = (float)zoom_y * (y - get_y_offset(edl, single_channel, zoom_y, conformed_w, conformed_h));
337 }
338
339
340
341 void Canvas::get_transfers(EDL *edl,
342         float &output_x1, float &output_y1, float &output_x2, float &output_y2,
343         float &canvas_x1, float &canvas_y1, float &canvas_x2, float &canvas_y2,
344         int canvas_w, int canvas_h)
345 {
346 //printf("Canvas::get_transfers %d canvas_w=%d canvas_h=%d\n", 
347 // __LINE__,  canvas_w, canvas_h);
348 // automatic canvas size detection
349         if(canvas_w < 0) canvas_w = get_canvas()->get_w();
350         if(canvas_h < 0) canvas_h = get_canvas()->get_h();
351
352 // Canvas is zoomed to a portion of the output frame
353         if(use_scrollbars)
354         {
355                 float in_x1, in_y1, in_x2, in_y2;
356                 float out_x1, out_y1, out_x2, out_y2;
357                 float zoom_x, zoom_y, conformed_w, conformed_h;
358
359                 get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
360                 out_x1 = 0;
361                 out_y1 = 0;
362                 out_x2 = canvas_w;
363                 out_y2 = canvas_h;
364                 in_x1 = 0;
365                 in_y1 = 0;
366                 in_x2 = canvas_w;
367                 in_y2 = canvas_h;
368
369                 canvas_to_output(edl, 0, in_x1, in_y1);
370                 canvas_to_output(edl, 0, in_x2, in_y2);
371
372 //printf("Canvas::get_transfers 1 %.0f %.0f %.0f %.0f -> %.0f %.0f %.0f %.0f\n",
373 //in_x1, in_y1, in_x2, in_y2, out_x1, out_y1, out_x2, out_y2);
374
375                 if(in_x1 < 0)
376                 {
377                         out_x1 += -in_x1 * zoom_x;
378                         in_x1 = 0;
379                 }
380
381                 if(in_y1 < 0)
382                 {
383                         out_y1 += -in_y1 * zoom_y;
384                         in_y1 = 0;
385                 }
386
387                 int output_w = get_output_w(edl);
388                 int output_h = get_output_h(edl);
389
390                 if(in_x2 > output_w)
391                 {
392                         out_x2 -= (in_x2 - output_w) * zoom_x;
393                         in_x2 = output_w;
394                 }
395
396                 if(in_y2 > output_h)
397                 {
398                         out_y2 -= (in_y2 - output_h) * zoom_y;
399                         in_y2 = output_h;
400                 }
401 // printf("Canvas::get_transfers 2 %.0f %.0f %.0f %.0f -> %.0f %.0f %.0f %.0f\n",
402 //                      in_x1, in_y1, in_x2, in_y2, out_x1, out_y1, out_x2, out_y2);
403
404                 output_x1 = in_x1;
405                 output_y1 = in_y1;
406                 output_x2 = in_x2;
407                 output_y2 = in_y2;
408                 canvas_x1 = out_x1;
409                 canvas_y1 = out_y1;
410                 canvas_x2 = out_x2;
411                 canvas_y2 = out_y2;
412
413 // Center on canvas
414 //              if(!scrollbars_exist())
415 //              {
416 //                      out_x = canvas_w / 2 - out_w / 2;
417 //                      out_y = canvas_h / 2 - out_h / 2;
418 //              }
419
420         }
421         else
422 // The output frame is normalized to the canvas
423         {
424 // Default canvas coords fill the entire canvas
425                 canvas_x1 = 0;
426                 canvas_y1 = 0;
427                 canvas_x2 = canvas_w;
428                 canvas_y2 = canvas_h;
429
430                 if(edl)
431                 {
432 // Use EDL aspect ratio to shrink one of the canvas dimensions
433                         float out_w = canvas_x2 - canvas_x1;
434                         float out_h = canvas_y2 - canvas_y1;
435                         if(out_w / out_h > edl->get_aspect_ratio())
436                         {
437                                 out_w = (int)(out_h * edl->get_aspect_ratio() + 0.5);
438                                 canvas_x1 = canvas_w / 2 - out_w / 2;
439                         }
440                         else
441                         {
442                                 out_h = (int)(out_w / edl->get_aspect_ratio() + 0.5);
443                                 canvas_y1 = canvas_h / 2 - out_h / 2;
444 // printf("Canvas::get_transfers %d canvas_h=%d out_h=%f canvas_y1=%f\n",
445 // __LINE__,
446 // canvas_h,
447 // out_h,
448 // canvas_y1);
449                         }
450                         canvas_x2 = canvas_x1 + out_w;
451                         canvas_y2 = canvas_y1 + out_h;
452
453 // Get output frame coords from EDL
454                         output_x1 = 0;
455                         output_y1 = 0;
456                         output_x2 = get_output_w(edl);
457                         output_y2 = get_output_h(edl);
458                 }
459                 else
460 // No EDL to get aspect ratio or output frame coords from
461                 {
462                         output_x1 = 0;
463                         output_y1 = 0;
464                         output_x2 = this->output_w;
465                         output_y2 = this->output_h;
466                 }
467         }
468
469 // Clamp to minimum value
470         output_x1 = MAX(0, output_x1);
471         output_y1 = MAX(0, output_y1);
472         output_x2 = MAX(output_x1, output_x2);
473         output_y2 = MAX(output_y1, output_y2);
474         canvas_x1 = MAX(0, canvas_x1);
475         canvas_y1 = MAX(0, canvas_y1);
476         canvas_x2 = MAX(canvas_x1, canvas_x2);
477         canvas_y2 = MAX(canvas_y1, canvas_y2);
478 // printf("Canvas::get_transfers %d %f,%f %f,%f -> %f,%f %f,%f\n",
479 // __LINE__,
480 // output_x1,
481 // output_y1,
482 // output_x2,
483 // output_y2,
484 // canvas_x1,
485 // canvas_y1,
486 // canvas_x2,
487 // canvas_y2);
488 }
489
490 int Canvas::scrollbars_exist()
491 {
492         return(use_scrollbars && (xscroll || yscroll));
493 }
494
495 int Canvas::get_output_w(EDL *edl)
496 {
497         return !edl ? 0 : edl->session->output_w;
498 }
499
500 int Canvas::get_output_h(EDL *edl)
501 {
502         return !edl ? 0 : edl->session->output_h;
503 }
504
505
506
507 void Canvas::get_scrollbars(EDL *edl,
508         int &canvas_x,
509         int &canvas_y,
510         int &canvas_w,
511         int &canvas_h)
512 {
513         int need_xscroll = 0;
514         int need_yscroll = 0;
515 //      int done = 0;
516         float zoom_x, zoom_y, conformed_w, conformed_h;
517
518         if(edl)
519         {
520                 w_needed = edl->session->output_w;
521                 h_needed = edl->session->output_h;
522                 w_visible = w_needed;
523                 h_visible = h_needed;
524         }
525 //printf("Canvas::get_scrollbars 1 %d %d\n", get_xscroll(), get_yscroll());
526
527         if( use_scrollbars ) {
528                 w_needed = edl->session->output_w;
529                 h_needed = edl->session->output_h;
530                 get_zooms(edl, 0, zoom_x, zoom_y, conformed_w, conformed_h);
531 //printf("Canvas::get_scrollbars 2 %d %d\n", get_xscroll(), get_yscroll());
532
533                 w_visible = (int)(canvas_w / zoom_x);
534                 h_visible = (int)(canvas_h / zoom_y);
535                 if( w_needed > w_visible ) {
536                         need_xscroll = 1;
537                         canvas_h -= BC_ScrollBar::get_span(SCROLL_HORIZ);
538                 }
539
540                 if( h_needed > h_visible ) {
541                         need_yscroll = 1;
542                         canvas_w -= BC_ScrollBar::get_span(SCROLL_VERT);
543                 }
544 //printf("Canvas::get_scrollbars %d %d %d %d %d %d\n", canvas_w, canvas_h, w_needed, h_needed, w_visible, h_visible);
545 //printf("Canvas::get_scrollbars 3 %d %d\n", get_xscroll(), get_yscroll());
546
547                 w_visible = (int)(canvas_w / zoom_x);
548                 h_visible = (int)(canvas_h / zoom_y);
549         }
550
551         if( need_xscroll ) {
552                 if( !xscroll ) {
553                         xscroll = new CanvasXScroll(edl, this, canvas_x, canvas_y + canvas_h,
554                                         w_needed, get_xscroll(), w_visible, canvas_w);
555                         subwindow->add_subwindow(xscroll);
556                         xscroll->show_window(0);
557                 }
558                 else
559                         xscroll->reposition_window(canvas_x, canvas_y + canvas_h, canvas_w);
560
561                 if( xscroll->get_length() != w_needed ||
562                     xscroll->get_handlelength() != w_visible )
563                         xscroll->update_length(w_needed, get_xscroll(), w_visible, 0);
564         }
565         else if( xscroll ) {
566                 delete xscroll;  xscroll = 0;
567         }
568 //printf("Canvas::get_scrollbars 4 %d %d\n", get_xscroll(), get_yscroll());
569
570         if( need_yscroll ) {
571                 if( !yscroll ) {
572                         yscroll = new CanvasYScroll(edl, this, canvas_x + canvas_w, canvas_y,
573                                         h_needed, get_yscroll(), h_visible, canvas_h);
574                         subwindow->add_subwindow(yscroll);
575                         yscroll->show_window(0);
576                 }
577                 else
578                         yscroll->reposition_window(canvas_x + canvas_w, canvas_y, canvas_h);
579
580                 if( yscroll->get_length() != edl->session->output_h ||
581                     yscroll->get_handlelength() != h_visible )
582                         yscroll->update_length(h_needed, get_yscroll(), h_visible, 0);
583         }
584         else if( yscroll ) {
585                 delete yscroll;  yscroll = 0;
586         }
587 //printf("Canvas::get_scrollbars 5 %d %d\n", get_xscroll(), get_yscroll());
588 }
589
590
591 void Canvas::update_geometry(EDL *edl, int x, int y, int w, int h)
592 {
593         int redraw = 0;
594         if( this->x != x || this->y != y ||
595             this->w != w || this->h != h ) redraw = 1;
596         if( !redraw ) {
597                 int vx = x, vy = y, vw = w, vh = h;
598                 get_scrollbars(edl, vx, vy, vw, vh);
599                 if( vx != view_x || vy != view_y ||
600                     vw != view_w || vh != view_h ) redraw = 1;
601         }
602         if( !redraw ) return;
603         reposition_window(edl, x, y, w, y);
604 }
605
606 void Canvas::reposition_window(EDL *edl, int x, int y, int w, int h)
607 {
608         this->x = view_x = x;  this->y = view_y = y;
609         this->w = view_w = w;  this->h = view_h = h;
610 //printf("Canvas::reposition_window 1\n");
611         get_scrollbars(edl, view_x, view_y, view_w, view_h);
612 //printf("Canvas::reposition_window %d %d %d %d\n", view_x, view_y, view_w, view_h);
613         if(canvas_subwindow)
614         {
615                 canvas_subwindow->reposition_window(view_x, view_y, view_w, view_h);
616
617 // Need to clear out the garbage in the back
618                 if(canvas_subwindow->get_video_on())
619                 {
620                         canvas_subwindow->set_color(BLACK);
621                         canvas_subwindow->draw_box(0, 0,
622                                 get_canvas()->get_w(), get_canvas()->get_h());
623                         canvas_subwindow->flash(0);
624                 }
625         }
626         draw_refresh(0);
627 }
628
629 void Canvas::set_cursor(int cursor)
630 {
631         get_canvas()->set_cursor(cursor, 0, 1);
632 }
633
634 int Canvas::get_cursor_x()
635 {
636         return get_canvas()->get_cursor_x();
637 }
638
639 int Canvas::get_cursor_y()
640 {
641         return get_canvas()->get_cursor_y();
642 }
643
644 int Canvas::get_buttonpress()
645 {
646         return get_canvas()->get_buttonpress();
647 }
648
649
650 void Canvas::create_objects(EDL *edl)
651 {
652         view_x = x;
653         view_y = y;
654         view_w = w;
655         view_h = h;
656         get_scrollbars(edl, view_x, view_y, view_w, view_h);
657
658         subwindow->unlock_window();
659         create_canvas();
660         subwindow->lock_window("Canvas::create_objects");
661
662         subwindow->add_subwindow(canvas_menu = new CanvasPopup(this));
663         canvas_menu->create_objects();
664
665         subwindow->add_subwindow(fullscreen_menu = new CanvasFullScreenPopup(this));
666         fullscreen_menu->create_objects();
667
668 }
669
670 int Canvas::button_press_event()
671 {
672         int result = 0;
673
674         if(get_canvas()->get_buttonpress() == 3)
675         {
676                 if(get_fullscreen())
677                         fullscreen_menu->activate_menu();
678                 else
679                         canvas_menu->activate_menu();
680                 result = 1;
681         }
682
683         return result;
684 }
685
686 void Canvas::start_single()
687 {
688         is_processing = 1;
689         status_event();
690 }
691
692 void Canvas::stop_single()
693 {
694         is_processing = 0;
695         status_event();
696 }
697
698 void Canvas::start_video()
699 {
700         if(get_canvas())
701         {
702                 get_canvas()->start_video();
703                 status_event();
704         }
705 }
706
707 void Canvas::stop_video()
708 {
709         if(get_canvas())
710         {
711                 get_canvas()->stop_video();
712                 status_event();
713         }
714 }
715
716
717 void Canvas::start_fullscreen()
718 {
719         set_fullscreen(1);
720         create_canvas();
721 }
722
723 void Canvas::stop_fullscreen()
724 {
725         set_fullscreen(0);
726         create_canvas();
727 }
728
729 void Canvas::create_canvas()
730 {
731         int video_on = 0;
732         lock_canvas("Canvas::create_canvas");
733
734
735         if(!get_fullscreen())
736 // Enter windowed
737         {
738                 if(canvas_fullscreen)
739                 {
740                         video_on = canvas_fullscreen->get_video_on();
741                         canvas_fullscreen->stop_video();
742                         canvas_fullscreen->lock_window("Canvas::create_canvas 2");
743                         canvas_fullscreen->hide_window();
744                         canvas_fullscreen->unlock_window();
745                 }
746
747                 if(!canvas_auxwindow && !canvas_subwindow)
748                 {
749                         subwindow->add_subwindow(canvas_subwindow = new CanvasOutput(this,
750                                 view_x,
751                                 view_y,
752                                 view_w,
753                                 view_h));
754                 }
755         }
756         else
757 // Enter fullscreen
758         {
759                 BC_WindowBase *wdw = canvas_auxwindow ?
760                         canvas_auxwindow : canvas_subwindow;
761                 if(wdw)
762                 {
763                         video_on = wdw->get_video_on();
764                         wdw->stop_video();
765                 }
766
767                 int x, y, w, h;
768                 wdw->get_fullscreen_geometry(x, y, w, h);
769
770                 if( canvas_fullscreen ) {
771                         if( x != canvas_fullscreen->get_x() ||
772                             y != canvas_fullscreen->get_y() ||
773                             w != canvas_fullscreen->get_w() ||
774                             h != canvas_fullscreen->get_h() ) {
775                                 delete canvas_fullscreen;
776                                 canvas_fullscreen = 0;
777                         }
778                 }
779                 if( !canvas_fullscreen )
780                         canvas_fullscreen = new CanvasFullScreen(this, w, h);
781                 canvas_fullscreen->show_window();
782                 canvas_fullscreen->sync_display();
783                 canvas_fullscreen->reposition_window(x, y);
784         }
785
786         if( !video_on ) {
787                 get_canvas()->lock_window("Canvas::create_canvas 1");
788                 draw_refresh();
789                 get_canvas()->unlock_window();
790         }
791
792         if( video_on )
793                 get_canvas()->start_video();
794
795         get_canvas()->lock_window("Canvas::create_canvas 2");
796         get_canvas()->focus();
797         get_canvas()->unlock_window();
798
799         unlock_canvas();
800 }
801
802
803
804 int Canvas::cursor_leave_event_base(BC_WindowBase *caller)
805 {
806         int result = 0;
807         if(cursor_inside) result = cursor_leave_event();
808         cursor_inside = 0;
809         return result;
810 }
811
812 int Canvas::cursor_enter_event_base(BC_WindowBase *caller)
813 {
814         int result = 0;
815         if(caller->is_event_win() && caller->cursor_inside())
816         {
817                 cursor_inside = 1;
818                 result = cursor_enter_event();
819         }
820         return result;
821 }
822
823 int Canvas::button_press_event_base(BC_WindowBase *caller)
824 {
825         if(caller->is_event_win() && caller->cursor_inside())
826         {
827                 return button_press_event();
828         }
829         return 0;
830 }
831
832 int Canvas::keypress_event(BC_WindowBase *caller)
833 {
834         int key = caller->get_keypress();
835         switch( key ) {
836         case 'f':
837                 caller->unlock_window();
838                 if(get_fullscreen())
839                         stop_fullscreen();
840                 else
841                         start_fullscreen();
842                 caller->lock_window("Canvas::keypress_event 1");
843                 break;
844         case ESC:
845                 caller->unlock_window();
846                 if(get_fullscreen())
847                         stop_fullscreen();
848                 caller->lock_window("Canvas::keypress_event 2");
849                 break;
850         default:
851                 return 0;
852         }
853         return 1;
854 }
855
856 void Canvas::update_refresh(VideoDevice *device, VFrame *output_frame)
857 {
858         int best_color_model = output_frame->get_color_model();
859         int use_opengl =
860                 device->out_config->driver == PLAYBACK_X11_GL &&
861                 output_frame->get_opengl_state() != VFrame::RAM;
862
863 // OpenGL does YUV->RGB in the compositing step
864         if( use_opengl )
865                 best_color_model = BC_RGB888;
866         else if( BC_CModels::has_alpha(best_color_model) ) {
867                 best_color_model =
868                         BC_CModels::is_float(best_color_model ) ?
869                                 BC_RGB_FLOAT :
870                         BC_CModels::is_yuv(best_color_model ) ?
871                                 ( BC_CModels::calculate_pixelsize(best_color_model) > 8 ?
872                                         BC_YUV161616 : BC_YUV888 ) :
873                                 ( BC_CModels::calculate_pixelsize(best_color_model) > 8 ?
874                                         BC_RGB161616 : BC_RGB888 ) ;
875         }
876         int out_w = output_frame->get_w();
877         int out_h = output_frame->get_h();
878         if( refresh_frame &&
879            (refresh_frame->get_w() != out_w ||
880             refresh_frame->get_h() != out_h ||
881             refresh_frame->get_color_model() != best_color_model ) ) {
882 // x11 direct render uses BC_BGR8888, use tranfer_from to remap
883                 delete refresh_frame;  refresh_frame = 0;
884         }
885
886         if( !refresh_frame ) {
887                 refresh_frame =
888                         new VFrame(out_w, out_h, best_color_model);
889         }
890
891         if( use_opengl ) {
892                 get_canvas()->unlock_window();
893                 unlock_canvas();
894
895                 mwindow->playback_3d->copy_from(this, refresh_frame, output_frame, 0);
896                 lock_canvas(" Canvas::output_refresh");
897                 get_canvas()->lock_window(" Canvas::output_refresh");
898         }
899         else
900                 refresh_frame->transfer_from(output_frame, -1);
901 }
902
903
904 void Canvas::clear(int flush)
905 {
906         if( refresh_frame )
907                 refresh_frame->clear_frame();
908         BC_WindowBase *wdw = get_canvas();
909         if( !wdw ) return;
910         wdw->set_bg_color(BLACK);
911         wdw->clear_box(0,0, wdw->get_w(), wdw->get_h());
912         wdw->flash(flush);
913 }
914
915
916
917 CanvasOutput::CanvasOutput(Canvas *canvas,
918     int x,
919     int y,
920     int w,
921     int h)
922  : BC_SubWindow(x, y, w, h, BLACK)
923 {
924         this->canvas = canvas;
925 }
926
927 CanvasOutput::~CanvasOutput()
928 {
929 }
930
931 int CanvasOutput::cursor_leave_event()
932 {
933         return canvas->cursor_leave_event_base(canvas->get_canvas());
934 }
935
936 int CanvasOutput::cursor_enter_event()
937 {
938         return canvas->cursor_enter_event_base(canvas->get_canvas());
939 }
940
941 int CanvasOutput::button_press_event()
942 {
943         return canvas->button_press_event_base(canvas->get_canvas());
944 }
945
946 int CanvasOutput::button_release_event()
947 {
948         return canvas->button_release_event();
949 }
950
951 int CanvasOutput::cursor_motion_event()
952 {
953         return canvas->cursor_motion_event();
954 }
955
956 int CanvasOutput::keypress_event()
957 {
958         return canvas->keypress_event(canvas->get_canvas());
959 }
960
961
962
963 CanvasFullScreen::CanvasFullScreen(Canvas *canvas, int w, int h)
964  : BC_FullScreen(canvas->subwindow, w, h, BLACK, 0, 0, 0)
965 {
966         this->canvas = canvas;
967 }
968
969 CanvasFullScreen::~CanvasFullScreen()
970 {
971 }
972
973
974 CanvasXScroll::CanvasXScroll(EDL *edl, Canvas *canvas, int x, int y,
975         int length, int position, int handle_length, int pixels)
976  : BC_ScrollBar(x, y, SCROLL_HORIZ, pixels, length, position, handle_length)
977 {
978         this->canvas = canvas;
979 }
980
981 CanvasXScroll::~CanvasXScroll()
982 {
983 }
984
985 int CanvasXScroll::handle_event()
986 {
987 //printf("CanvasXScroll::handle_event %d %d %d\n", get_length(), get_value(), get_handlelength());
988         canvas->update_zoom(get_value(), canvas->get_yscroll(), canvas->get_zoom());
989         canvas->draw_refresh();
990         return 1;
991 }
992
993
994 CanvasYScroll::CanvasYScroll(EDL *edl, Canvas *canvas, int x, int y,
995         int length, int position, int handle_length, int pixels)
996  : BC_ScrollBar(x, y, SCROLL_VERT, pixels, length, position, handle_length)
997 {
998         this->canvas = canvas;
999 }
1000
1001 CanvasYScroll::~CanvasYScroll()
1002 {
1003 }
1004
1005 int CanvasYScroll::handle_event()
1006 {
1007 //printf("CanvasYScroll::handle_event %d %d\n", get_value(), get_length());
1008         canvas->update_zoom(canvas->get_xscroll(), get_value(), canvas->get_zoom());
1009         canvas->draw_refresh();
1010         return 1;
1011 }
1012
1013
1014 CanvasFullScreenPopup::CanvasFullScreenPopup(Canvas *canvas)
1015  : BC_PopupMenu(0, 0, 0, "", 0)
1016 {
1017         this->canvas = canvas;
1018 }
1019
1020
1021 void CanvasFullScreenPopup::create_objects()
1022 {
1023         add_item(new CanvasSubWindowItem(canvas));
1024 }
1025
1026 void CanvasFullScreenPopup::use_cwindow()
1027 {
1028         add_item(new CanvasPopupAuto(canvas));
1029 }
1030
1031 CanvasSubWindowItem::CanvasSubWindowItem(Canvas *canvas)
1032  : BC_MenuItem(_("Windowed"), "f", 'f')
1033 {
1034         this->canvas = canvas;
1035 }
1036
1037 int CanvasSubWindowItem::handle_event()
1038 {
1039 // It isn't a problem to delete the canvas from in here because the event
1040 // dispatcher is the canvas subwindow.
1041         canvas->subwindow->unlock_window();
1042         canvas->stop_fullscreen();
1043         canvas->subwindow->lock_window("CanvasSubWindowItem::handle_event");
1044         return 1;
1045 }
1046
1047
1048 CanvasPopup::CanvasPopup(Canvas *canvas)
1049  : BC_PopupMenu(0, 0, 0, "", 0)
1050 {
1051         this->canvas = canvas;
1052 }
1053
1054 CanvasPopup::~CanvasPopup()
1055 {
1056 }
1057
1058 CanvasZoomSize::CanvasZoomSize(Canvas *canvas)
1059  : BC_MenuItem(_("Zoom..."))
1060 {
1061         this->canvas = canvas;
1062 }
1063
1064 CanvasSizeSubMenu::CanvasSizeSubMenu(CanvasZoomSize *zoom_size)
1065 {
1066         this->zoom_size = zoom_size;
1067 }
1068
1069 void CanvasPopup::create_objects()
1070 {
1071         add_item(new BC_MenuItem("-"));
1072         add_item(new CanvasFullScreenItem(canvas));
1073
1074         CanvasZoomSize *zoom_size = new CanvasZoomSize(canvas);
1075         add_item(zoom_size);
1076         CanvasSizeSubMenu *submenu = new CanvasSizeSubMenu(zoom_size);
1077         zoom_size->add_submenu(submenu);
1078
1079         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 25%"), 0.25));
1080         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 33%"), 0.33));
1081         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 50%"), 0.5));
1082         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 75%"), 0.75));
1083         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 100%"), 1.0));
1084         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 150%"), 1.5));
1085         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 200%"), 2.0));
1086         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 300%"), 3.0));
1087         submenu->add_submenuitem(new CanvasPopupSize(canvas, _("Zoom 400%"), 4.0));
1088 }
1089
1090 void CanvasPopup::use_cwindow()
1091 {
1092         add_item(new CanvasPopupAuto(canvas));
1093         add_item(new CanvasPopupResetCamera(canvas));
1094         add_item(new CanvasPopupResetProjector(canvas));
1095         add_item(new CanvasPopupCameraKeyframe(canvas));
1096         add_item(new CanvasPopupProjectorKeyframe(canvas));
1097         add_item(toggle_controls = new CanvasToggleControls(canvas));
1098 }
1099
1100 void CanvasPopup::use_rwindow()
1101 {
1102         add_item(new CanvasPopupResetTranslation(canvas));
1103 }
1104
1105 void CanvasPopup::use_vwindow()
1106 {
1107         add_item(new CanvasPopupRemoveSource(canvas));
1108 }
1109
1110
1111 CanvasPopupAuto::CanvasPopupAuto(Canvas *canvas)
1112  : BC_MenuItem(_("Zoom Auto"))
1113 {
1114         this->canvas = canvas;
1115 }
1116
1117 int CanvasPopupAuto::handle_event()
1118 {
1119         canvas->zoom_auto();
1120         return 1;
1121 }
1122
1123
1124 CanvasPopupSize::CanvasPopupSize(Canvas *canvas, char *text, float percentage)
1125  : BC_MenuItem(text)
1126 {
1127         this->canvas = canvas;
1128         this->percentage = percentage;
1129 }
1130 CanvasPopupSize::~CanvasPopupSize()
1131 {
1132 }
1133 int CanvasPopupSize::handle_event()
1134 {
1135         canvas->zoom_resize_window(percentage);
1136         return 1;
1137 }
1138
1139
1140
1141 CanvasPopupResetCamera::CanvasPopupResetCamera(Canvas *canvas)
1142  : BC_MenuItem(_("Reset camera"), _("F11"), KEY_F11)
1143 {
1144         this->canvas = canvas;
1145 }
1146 int CanvasPopupResetCamera::handle_event()
1147 {
1148         canvas->reset_camera();
1149         return 1;
1150 }
1151
1152 CanvasPopupResetProjector::CanvasPopupResetProjector(Canvas *canvas)
1153  : BC_MenuItem(_("Reset projector"), _("F12"), KEY_F12)
1154 {
1155         this->canvas = canvas;
1156 }
1157 int CanvasPopupResetProjector::handle_event()
1158 {
1159         canvas->reset_projector();
1160         return 1;
1161 }
1162
1163
1164 CanvasPopupCameraKeyframe::CanvasPopupCameraKeyframe(Canvas *canvas)
1165  : BC_MenuItem(_("Camera keyframe"), _("Shift-F11"), KEY_F11)
1166 {
1167         this->canvas = canvas;
1168         set_shift(1);
1169 }
1170 int CanvasPopupCameraKeyframe::handle_event()
1171 {
1172         canvas->camera_keyframe();
1173         return 1;
1174 }
1175
1176 CanvasPopupProjectorKeyframe::CanvasPopupProjectorKeyframe(Canvas *canvas)
1177  : BC_MenuItem(_("Projector keyframe"), _("Shift-F12"), KEY_F12)
1178 {
1179         this->canvas = canvas;
1180         set_shift(1);
1181 }
1182 int CanvasPopupProjectorKeyframe::handle_event()
1183 {
1184         canvas->projector_keyframe();
1185         return 1;
1186 }
1187
1188
1189
1190 CanvasPopupResetTranslation::CanvasPopupResetTranslation(Canvas *canvas)
1191  : BC_MenuItem(_("Reset translation"))
1192 {
1193         this->canvas = canvas;
1194 }
1195 int CanvasPopupResetTranslation::handle_event()
1196 {
1197         canvas->reset_translation();
1198         return 1;
1199 }
1200
1201
1202
1203 CanvasToggleControls::CanvasToggleControls(Canvas *canvas)
1204  : BC_MenuItem(calculate_text(canvas->get_cwindow_controls()))
1205 {
1206         this->canvas = canvas;
1207 }
1208 int CanvasToggleControls::handle_event()
1209 {
1210         canvas->toggle_controls();
1211         set_text(calculate_text(canvas->get_cwindow_controls()));
1212         return 1;
1213 }
1214
1215 char* CanvasToggleControls::calculate_text(int cwindow_controls)
1216 {
1217         if(!cwindow_controls)
1218                 return _("Show controls");
1219         else
1220                 return _("Hide controls");
1221 }
1222
1223
1224
1225
1226
1227
1228
1229 CanvasFullScreenItem::CanvasFullScreenItem(Canvas *canvas)
1230  : BC_MenuItem(_("Fullscreen"), "f", 'f')
1231 {
1232         this->canvas = canvas;
1233 }
1234 int CanvasFullScreenItem::handle_event()
1235 {
1236         canvas->subwindow->unlock_window();
1237         canvas->start_fullscreen();
1238         canvas->subwindow->lock_window("CanvasFullScreenItem::handle_event");
1239         return 1;
1240 }
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250 CanvasPopupRemoveSource::CanvasPopupRemoveSource(Canvas *canvas)
1251  : BC_MenuItem(_("Close source"))
1252 {
1253         this->canvas = canvas;
1254 }
1255 int CanvasPopupRemoveSource::handle_event()
1256 {
1257         canvas->close_source();
1258         return 1;
1259 }
1260
1261