mask focus/pivot pt tweaks, set mask_track_id on update_project, ffmpeg cuda open...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / cwindowgui.C
index 0d8b755a6cc18c9703ffd1c5423db97e92fdb835..99c395bee41f314ebe9ba94f527f3f0d15d6bcc0 100644 (file)
@@ -78,14 +78,10 @@ static int total_zooms = sizeof(my_zoom_table) / sizeof(double);
 CWindowGUI::CWindowGUI(MWindow *mwindow, CWindow *cwindow)
  : BC_Window(_(PROGRAM_NAME ": Compositor"),
        mwindow->session->cwindow_x,
-    mwindow->session->cwindow_y,
-    mwindow->session->cwindow_w,
-    mwindow->session->cwindow_h,
-    100,
-    100,
-    1,
-    1,
-    1,
+       mwindow->session->cwindow_y,
+       mwindow->session->cwindow_w,
+       mwindow->session->cwindow_h,
+       100, 100, 1, 1, 1,
        BC_WindowBase::get_resources()->bg_color,
        mwindow->get_cwindow_display())
 {
@@ -119,6 +115,7 @@ CWindowGUI::CWindowGUI(MWindow *mwindow, CWindow *cwindow)
 
 CWindowGUI::~CWindowGUI()
 {
+       cwindow->stop_playback(1);
        if(tool_panel) delete tool_panel;
        delete meters;
        delete composite_panel;
@@ -128,6 +125,7 @@ CWindowGUI::~CWindowGUI()
        delete zoom_panel;
        delete active;
        delete inactive;
+       delete focus_frame;
        delete orig_mask_keyframe;
 }
 
@@ -138,6 +136,7 @@ void CWindowGUI::create_objects()
 
        active = new BC_Pixmap(this, mwindow->theme->get_image("cwindow_active"));
        inactive = new BC_Pixmap(this, mwindow->theme->get_image("cwindow_inactive"));
+       focus_frame = new VFramePng(mwindow->theme->get_image_data("cwindow_focus.png"));
 
        mwindow->theme->get_cwindow_sizes(this, mwindow->session->cwindow_controls);
        mwindow->theme->draw_cwindow_bg(this);
@@ -209,12 +208,6 @@ void CWindowGUI::create_objects()
        if( !mwindow->edl->session->cwindow_scrollbars )
                zoom_panel->set_text(auto_zoom);
 
-//     destination = new CWindowDestination(mwindow,
-//             this,
-//             mwindow->theme->cdest_x,
-//             mwindow->theme->cdest_y);
-//     destination->create_objects();
-
 // Must create after meter panel
        tool_panel = new CWindowTool(mwindow, this);
        tool_panel->Thread::start();
@@ -419,7 +412,7 @@ void CWindowGUI::zoom_canvas(double value, int update_menu)
        canvas->reposition_window(mwindow->edl,
                mwindow->theme->ccanvas_x, mwindow->theme->ccanvas_y,
                mwindow->theme->ccanvas_w, mwindow->theme->ccanvas_h);
-       canvas->draw_refresh();
+       canvas->refresh(0);
 }
 
 void CWindowGUI::set_operation(int value)
@@ -443,7 +436,7 @@ void CWindowGUI::set_operation(int value)
 
        edit_panel->update();
        tool_panel->start_tool(value);
-       canvas->draw_refresh();
+       canvas->refresh(0);
 }
 
 void CWindowGUI::update_tool()
@@ -480,7 +473,7 @@ int CWindowGUI::keypress_event()
                break;
        case 'f':
                unlock_window();
-               if(mwindow->session->cwindow_fullscreen)
+               if( canvas->get_fullscreen() )
                        canvas->stop_fullscreen();
                else
                        canvas->start_fullscreen();
@@ -506,7 +499,7 @@ int CWindowGUI::keypress_event()
                break;
        case ESC:
                unlock_window();
-               if(mwindow->session->cwindow_fullscreen)
+               if( canvas->get_fullscreen() )
                        canvas->stop_fullscreen();
                lock_window("CWindowGUI::keypress_event 4");
                result = 1;
@@ -672,7 +665,7 @@ void CWindowGUI::sync_parameters(int change_type, int redraw, int overlay)
 {
        if( redraw ) {
                update_tool();
-               canvas->draw_refresh();
+               canvas->refresh(1);
        }
        if( change_type < 0 && !overlay ) return;
        unlock_window();
@@ -681,7 +674,7 @@ void CWindowGUI::sync_parameters(int change_type, int redraw, int overlay)
                mwindow->sync_parameters(change_type);
        }
        if( overlay ) {
-               mwindow->gui->lock_window("CWindow::camera_keyframe");
+               mwindow->gui->lock_window("CWindowGUI::sync_parameters");
                mwindow->gui->draw_overlays(1);
                mwindow->gui->unlock_window();
        }
@@ -698,7 +691,7 @@ void CWindowGUI::drag_motion()
        int need_highlight = cursor_above() && get_cursor_over_window();
        if( highlighted == need_highlight ) return;
        highlighted = need_highlight;
-       canvas->draw_refresh();
+       canvas->refresh(1);
 }
 
 int CWindowGUI::drag_stop()
@@ -710,7 +703,7 @@ int CWindowGUI::drag_stop()
            mwindow->session->current_operation != DRAG_VTRANSITION &&
            mwindow->session->current_operation != DRAG_VEFFECT) return 0;
        highlighted = 0;
-       canvas->draw_refresh();
+       canvas->refresh(1);
        result = 1;
 
        if(mwindow->session->current_operation == DRAG_ASSET)
@@ -1000,44 +993,12 @@ int CWindowSlider::decrease_value()
        lock_window("CWindowSlider::decrease_value");
        return 1;
 }
-
-
-// CWindowDestination::CWindowDestination(MWindow *mwindow, CWindowGUI *cwindow, int x, int y)
-//  : BC_PopupTextBox(cwindow,
-//     &cwindow->destinations,
-//     cwindow->destinations.values[cwindow->cwindow->destination]->get_text(),
-//     x,
-//     y,
-//     70,
-//     200)
-// {
-//     this->mwindow = mwindow;
-//     this->cwindow = cwindow;
-// }
-//
-// CWindowDestination::~CWindowDestination()
-// {
-// }
-//
-// int CWindowDestination::handle_event()
-// {
-//     return 1;
-// }
 #endif // USE_SLIDER
 
 
-
-
-
-
 CWindowTransport::CWindowTransport(MWindow *mwindow,
-       CWindowGUI *gui,
-       int x,
-       int y)
- : PlayTransport(mwindow,
-       gui,
-       x,
-       y)
+       CWindowGUI *gui, int x, int y)
+ : PlayTransport(mwindow, gui, x, y)
 {
        this->gui = gui;
 }
@@ -1093,17 +1054,6 @@ void CWindowCanvas::status_event()
        gui->draw_status(1);
 }
 
-int CWindowCanvas::get_fullscreen()
-{
-       return mwindow->session->cwindow_fullscreen;
-}
-
-void CWindowCanvas::set_fullscreen(int value)
-{
-       mwindow->session->cwindow_fullscreen = value;
-}
-
-
 void CWindowCanvas::update_zoom(int x, int y, float zoom)
 {
        use_scrollbars = mwindow->edl->session->cwindow_scrollbars;
@@ -1136,43 +1086,23 @@ float CWindowCanvas::get_zoom()
 
 void CWindowCanvas::draw_refresh(int flush)
 {
-       if(get_canvas() && !get_canvas()->get_video_on())
-       {
-
-               if(refresh_frame && refresh_frame->get_w()>0 && refresh_frame->get_h()>0)
-               {
+       BC_WindowBase *window = get_canvas();
+       if( window && !window->get_video_on() ) {
+               clear(0);
+               if( refresh_frame && refresh_frame->get_w()>0 && refresh_frame->get_h()>0 ) {
                        float in_x1, in_y1, in_x2, in_y2;
                        float out_x1, out_y1, out_x2, out_y2;
                        get_transfers(mwindow->edl,
-                               in_x1,
-                               in_y1,
-                               in_x2,
-                               in_y2,
-                               out_x1,
-                               out_y1,
-                               out_x2,
-                               out_y2);
-
-                       if(!EQUIV(out_x1, 0) ||
-                               !EQUIV(out_y1, 0) ||
-                               !EQUIV(out_x2, get_canvas()->get_w()) ||
-                               !EQUIV(out_y2, get_canvas()->get_h()))
-                       {
-                               get_canvas()->clear_box(0,
-                                       0,
-                                       get_canvas()->get_w(),
-                                       get_canvas()->get_h());
-                       }
+                               in_x1, in_y1, in_x2, in_y2,
+                               out_x1, out_y1, out_x2, out_y2);
+
 
 //printf("CWindowCanvas::draw_refresh %.2f %.2f %.2f %.2f -> %.2f %.2f %.2f %.2f\n",
 //in_x1, in_y1, in_x2, in_y2, out_x1, out_y1, out_x2, out_y2);
 
 
-                       if(out_x2 > out_x1 &&
-                               out_y2 > out_y1 &&
-                               in_x2 > in_x1 &&
-                               in_y2 > in_y1)
-                       {
+                       if( out_x2 > out_x1 && out_y2 > out_y1 &&
+                           in_x2 > in_x1 && in_y2 > in_y1 ) {
 // input scaled from session to refresh frame coordinates
                                int ow = get_output_w(mwindow->edl);
                                int oh = get_output_h(mwindow->edl);
@@ -1184,31 +1114,19 @@ void CWindowCanvas::draw_refresh(int flush)
                                in_y1 *= ys;  in_y2 *= ys;
 // Can't use OpenGL here because it is called asynchronously of the
 // playback operation.
-                               get_canvas()->draw_vframe(refresh_frame,
-                                               (int)out_x1,
-                                               (int)out_y1,
+                               window->draw_vframe(refresh_frame,
+                                               (int)out_x1, (int)out_y1,
                                                (int)(out_x2 - out_x1),
                                                (int)(out_y2 - out_y1),
-                                               (int)in_x1,
-                                               (int)in_y1,
+                                               (int)in_x1, (int)in_y1,
                                                (int)(in_x2 - in_x1),
                                                (int)(in_y2 - in_y1),
                                                0);
                        }
                }
-               else
-               {
-                       get_canvas()->clear_box(0,
-                               0,
-                               get_canvas()->get_w(),
-                               get_canvas()->get_h());
-               }
-
+//usleep(10000);
                draw_overlays();
-// allow last opengl write to complete before redraw
-// tried sync_display, glFlush, glxMake*Current(0..)
-usleep(20000);
-               get_canvas()->flash(flush);
+               window->flash(flush);
        }
 //printf("CWindowCanvas::draw_refresh 10\n");
 }
@@ -1222,10 +1140,6 @@ void CWindowCanvas::draw_crophandle(int x, int y)
 }
 
 
-
-
-
-
 #define CONTROL_W 10
 #define CONTROL_H 10
 #define FIRST_CONTROL_W 20
@@ -1236,8 +1150,6 @@ void CWindowCanvas::draw_crophandle(int x, int y)
 #define RULERHANDLE_W 16
 #define RULERHANDLE_H 16
 
-
-
 int CWindowCanvas::do_ruler(int draw,
        int motion,
        int button_press,
@@ -1526,16 +1438,22 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
 {
 // Retrieve points from top recordable track
 //printf("CWindowCanvas::do_mask 1\n");
-       Track *track = gui->cwindow->calculate_affected_track();
+       Track *track = gui->cwindow->calculate_mask_track();
 //printf("CWindowCanvas::do_mask 2\n");
 
        if(!track) return 0;
 //printf("CWindowCanvas::do_mask 3\n");
-
+       CWindowMaskGUI *mask_gui = (CWindowMaskGUI *)
+               (gui->tool_panel ? gui->tool_panel->tool_gui : 0);
+       int draw_markers = mask_gui ? mask_gui->markers : 0;
+       int draw_boundary = mask_gui ? mask_gui->boundary : 0;
        MaskAutos *mask_autos = (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
        int64_t position = track->to_units(
                mwindow->edl->local_session->get_selectionstart(1),
                0);
+       Auto *prev_auto = 0;
+       mask_autos->get_prev_auto(position, PLAY_FORWARD, (Auto *&)prev_auto, 1);
+       MaskAuto *prev_mask = (MaskAuto *)prev_auto;
        ArrayList<MaskPoint*> points;
 
 // Determine the points based on whether
@@ -1543,19 +1461,16 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
 // If keyframe generation occurs, use the interpolated mask.
 // If no keyframe generation occurs, use the previous mask.
        int use_interpolated = 0;
-       if(button_press || cursor_motion) {
+       if( button_press || cursor_motion ) {
 #ifdef USE_KEYFRAME_SPANNING
                double selection_start = mwindow->edl->local_session->get_selectionstart(0);
                double selection_end = mwindow->edl->local_session->get_selectionend(0);
-
-               Auto *first = 0;
-               mask_autos->get_prev_auto(track->to_units(selection_start, 0),
-                       PLAY_FORWARD, first, 1);
-               Auto *last = 0;
-               mask_autos->get_prev_auto(track->to_units(selection_end, 0),
-                       PLAY_FORWARD, last, 1);
-
-               if(last == first && (!mwindow->edl->session->auto_keyframes))
+               int64_t start_pos = track->to_units(selection_start, 0);
+               int64_t end_pos = track->to_units(selection_end, 0);
+               Auto *first = 0, *last = 0;
+               mask_autos->get_prev_auto(start_pos, PLAY_FORWARD, first, 1);
+               mask_autos->get_prev_auto(end_pos, PLAY_FORWARD, last, 1);
+               if( last == first && (!mwindow->edl->session->auto_keyframes) )
                        use_interpolated = 0;
                else
 // If keyframe spanning occurs, use the interpolated points.
@@ -1563,28 +1478,22 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                        use_interpolated = 1;
 
 #else
-               if(mwindow->edl->session->auto_keyframes)
+               if( mwindow->edl->session->auto_keyframes )
                        use_interpolated = 1;
 #endif
        }
        else
                use_interpolated = 1;
 
-       if(use_interpolated) {
+       if( use_interpolated ) {
 // Interpolate the points to get exactly what is being rendered at this position.
                mask_autos->get_points(&points,
                        mwindow->edl->session->cwindow_mask,
-                       position,
-                       PLAY_FORWARD);
+                       position, PLAY_FORWARD);
        }
        else {
 // Use the prev mask
-               Auto *prev = 0;
-               mask_autos->get_prev_auto(position,
-                       PLAY_FORWARD,
-                       prev,
-                       1);
-               ((MaskAuto*)prev)->get_points(&points,
+               prev_mask->get_points(&points,
                        mwindow->edl->session->cwindow_mask);
        }
 
@@ -1758,15 +1667,15 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
 
                                output_to_canvas(mwindow->edl, 0, canvas_x, canvas_y);
 
-                               if(j > 0) {
+                               if( j > 0 ) {
 
-                                       if(draw) { // Draw joining line
+                                       if( draw ) { // Draw joining line
                                                x_points.append((int)canvas_x);
                                                y_points.append((int)canvas_y);
                                        }
 
-                                       if(j == segments) {
-                                               if(draw) { // Draw second anchor
+                                       if( j == segments ) {
+                                               if( draw && draw_markers ) { // Draw second anchor
                                                        if(i < points.size() - 1) {
                                                                if(i == gui->affected_point - 1)
                                                                        get_canvas()->draw_disc(
@@ -1796,23 +1705,29 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                                }
                                else {
 // Draw first anchor
-                                       if(i == 0 && draw) {
-                                               char mask_label[BCSTRLEN];
-                                               sprintf(mask_label, "%d",
-                                                       mwindow->edl->session->cwindow_mask);
-                                               get_canvas()->draw_text(
-                                                       (int)canvas_x - FIRST_CONTROL_W,
-                                                       (int)canvas_y - FIRST_CONTROL_H,
-                                                       mask_label);
-
-                                               get_canvas()->draw_disc(
-                                                       (int)canvas_x - FIRST_CONTROL_W / 2,
-                                                       (int)canvas_y - FIRST_CONTROL_H / 2,
-                                                       FIRST_CONTROL_W, FIRST_CONTROL_H);
+                                       if( i == 0 && draw ) {
+                                               if( draw_boundary ) {
+                                                       char mask_label[BCSTRLEN];
+                                                       int k = mwindow->edl->session->cwindow_mask;
+                                                       if( !prev_mask || k < 0 || k >= prev_mask->masks.size() )
+                                                               sprintf(mask_label, "%d", k);
+                                                       else
+                                                               sprintf(mask_label, "%s", prev_mask->masks[k]->name);
+                                                       get_canvas()->draw_text(
+                                                               (int)canvas_x - FIRST_CONTROL_W,
+                                                               (int)canvas_y - FIRST_CONTROL_H,
+                                                               mask_label);
+                                               }
+                                               if( draw_markers ) {
+                                                       get_canvas()->draw_disc(
+                                                               (int)canvas_x - FIRST_CONTROL_W / 2,
+                                                               (int)canvas_y - FIRST_CONTROL_H / 2,
+                                                               FIRST_CONTROL_W, FIRST_CONTROL_H);
+                                               }
                                        }
 
 // Draw first control point.
-                                       if(draw) {
+                                       if( draw && draw_markers ) {
                                                output_to_canvas(mwindow->edl, 0, canvas_x1, canvas_y1);
                                                get_canvas()->draw_line(
                                                        (int)canvas_x, (int)canvas_y,
@@ -1832,20 +1747,61 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                }
 //printf("CWindowCanvas::do_mask 1\n");
 
-               if(draw) {
-                       get_canvas()->draw_polygon(&x_points, &y_points);
-                       get_canvas()->set_opaque();
+               BC_WindowBase *cvs_win = get_canvas();
+               if( draw && draw_boundary ) {
+                       cvs_win->draw_polygon(&x_points, &y_points);
+                       cvs_win->set_opaque();
+               }
+               if( draw && mask_gui && mask_gui->focused ) {
+                       float fx = atof(mask_gui->focus_x->get_text());
+                       float fy = atof(mask_gui->focus_y->get_text());
+                       output_to_canvas(mwindow->edl, 0, fx, fy);
+                       float r = bmax(cvs_win->get_w(), cvs_win->get_h());
+                       float d = 0.005*r;
+#if 1
+                       int fw = 2*d+3, fh = fw;
+                       VFrame focus(fw,fh, BC_RGBA8888);
+                       focus.transfer_from(gui->focus_frame);
+                       fx -= fw/2.f;  fy -= fh/2.f;
+                       BC_Pixmap focus_pixmap(cvs_win, &focus, PIXMAP_ALPHA);
+                       cvs_win->draw_pixmap(&focus_pixmap,fx,fy);
+#else
+                       cvs_win->set_line_width((int)(0.0025*r) + 1);
+                       cvs_win->set_color(BLUE);
+                       cvs_win->draw_line(fx-d,fy-d, fx+d, fy+d);
+                       cvs_win->draw_line(fx-d,fy+d, fx+d, fy-d);
+                       cvs_win->set_line_width(0);
+                       cvs_win->set_color(WHITE);
+#endif
+               }
+               if( draw && mask_gui && draw_markers && points.size() ) {
+                       float cx = 0, cy = 0;
+                       int n = points.size();
+                       for( int i=0; i<n; ++i ) {
+                               MaskPoint *point = points.get(i);
+                               cx += point->x;  cy += point->y;
+                       }
+                       cx /= n;  cy /= n;
+                       output_to_canvas(mwindow->edl, 0, cx, cy);
+                       float r = bmax(cvs_win->get_w(), cvs_win->get_h());
+                       float d = 0.007*r;
+                       cvs_win->set_line_width((int)(0.002*r) + 1);
+                       cvs_win->set_color(ORANGE);
+                       cvs_win->draw_line(cx-d,cy, cx+d, cy);
+                       cvs_win->draw_line(cx,cy-d, cx, cy+d);
+                       cvs_win->set_line_width(0);
+                       cvs_win->set_color(WHITE);
                }
 //printf("CWindowCanvas::do_mask 1\n");
        }
 
        if(button_press && !result) {
-               gui->affected_track = gui->cwindow->calculate_affected_track();
+               gui->affected_track = gui->cwindow->calculate_mask_track();
 
 // Get keyframe outside the EDL to edit.  This must be rendered
 // instead of the EDL keyframes when it exists.  Then it must be
 // applied to the EDL keyframes on buttonrelease.
-               if(gui->affected_track) {
+               if( gui->affected_track ) {
 #ifdef USE_KEYFRAME_SPANNING
 // Make copy of current parameters in local keyframe
                        gui->mask_keyframe =
@@ -1863,7 +1819,18 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                }
                SubMask *mask = gui->mask_keyframe->get_submask(mwindow->edl->session->cwindow_mask);
 
-
+               if( get_buttonpress() == WHEEL_UP || get_buttonpress() == WHEEL_DOWN ) {
+                       if( !gui->shift_down() ) {
+                               mwindow->undo->update_undo_before(_("mask rotate"), this);
+                               gui->current_operation = CWINDOW_MASK_ROTATE;
+                       }
+                       else {
+                               mwindow->undo->update_undo_before(_("mask scale"), this);
+                               gui->current_operation = CWINDOW_MASK_SCALE;
+                       }
+                       gui->affected_point = 0;
+               }
+               else
 // Translate entire keyframe
                if(gui->alt_down() && mask->points.size()) {
                        mwindow->undo->update_undo_before(_("mask translate"), 0);
@@ -2033,36 +2000,36 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
 
        if(button_press && result) {
 #ifdef USE_KEYFRAME_SPANNING
-               MaskPoint *point = points.values[gui->affected_point];
-               gui->center_x = point->x;
-               gui->center_y = point->y;
-               gui->control_in_x = point->control_x1;
-               gui->control_in_y = point->control_y1;
-               gui->control_out_x = point->control_x2;
-               gui->control_out_y = point->control_y2;
-               gui->tool_panel->raise_window();
+               ArrayList<MaskPoint*> &mask_points = points;
 #else
                SubMask *mask = gui->mask_keyframe->get_submask(mwindow->edl->session->cwindow_mask);
-               MaskPoint *point = mask->points.values[gui->affected_point];
-               gui->center_x = point->x;
-               gui->center_y = point->y;
-               gui->control_in_x = point->control_x1;
-               gui->control_in_y = point->control_y1;
-               gui->control_out_x = point->control_x2;
-               gui->control_out_y = point->control_y2;
-               gui->tool_panel->raise_window();
+               ArrayList<MaskPoint*> &mask_points = mask->points;
 #endif
+               int k = gui->affected_point;
+               if( k >= 0 && k < mask_points.size() ) {
+                       MaskPoint *point = mask_points.values[k];
+                       gui->center_x = point->x;
+                       gui->center_y = point->y;
+                       gui->control_in_x = point->control_x1;
+                       gui->control_in_y = point->control_y1;
+                       gui->control_out_x = point->control_x2;
+                       gui->control_out_y = point->control_y2;
+               }
+               else {
+                       gui->center_x = gui->center_y = 0;
+                       gui->control_in_x = gui->control_in_y = 0;
+                       gui->control_out_x = gui->control_out_y = 0;
+               }
+               gui->tool_panel->raise_window();
        }
 
 //printf("CWindowCanvas::do_mask 8\n");
-       if(cursor_motion) {
+       if( cursor_motion ) {
 
 #ifdef USE_KEYFRAME_SPANNING
 // Must update the reference keyframes for every cursor motion
-               gui->mask_keyframe =
-                       (MaskAuto*)gui->cwindow->calculate_affected_auto(
-                               mask_autos,
-                               0);
+               gui->mask_keyframe = (MaskAuto*)gui->cwindow->
+                       calculate_affected_auto(mask_autos, 0);
                gui->orig_mask_keyframe->copy_data(gui->mask_keyframe);
 #endif
 
@@ -2073,12 +2040,13 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                        gui->current_operation != CWINDOW_NONE) {
 //                     mwindow->undo->update_undo_before(_("mask point"), this);
 #ifdef USE_KEYFRAME_SPANNING
-                       MaskPoint *point = points.get(gui->affected_point);
+                       ArrayList<MaskPoint*> &mask_points = points;
 #else
-                       MaskPoint *point = mask->points.get(gui->affected_point);
+                       ArrayList<MaskPoint*> &mask_points = mask->points;
 #endif
+                       MaskPoint *point = mask_points.get(gui->affected_point);
 //                     canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
-//printf("CWindowCanvas::do_mask 9 %d %d\n", mask->points.size(), gui->affected_point);
+//printf("CWindowCanvas::do_mask 9 %d %d\n", mask_points.size(), gui->affected_point);
 
                        float last_x = point->x;
                        float last_y = point->y;
@@ -2086,39 +2054,73 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
                        float last_control_y1 = point->control_y1;
                        float last_control_x2 = point->control_x2;
                        float last_control_y2 = point->control_y2;
+                       int rotate = 0;
 
                        switch(gui->current_operation) {
-                               case CWINDOW_MASK:
+                       case CWINDOW_MASK:
 //printf("CWindowCanvas::do_mask %d %d\n", __LINE__, gui->affected_point);
-                                       point->x = mask_cursor_x - gui->x_origin + gui->center_x;
-                                       point->y = mask_cursor_y - gui->y_origin + gui->center_y;
-                                       break;
+                               point->x = mask_cursor_x - gui->x_origin + gui->center_x;
+                               point->y = mask_cursor_y - gui->y_origin + gui->center_y;
+                               break;
 
-                               case CWINDOW_MASK_CONTROL_IN:
-                                       point->control_x1 = mask_cursor_x - gui->x_origin + gui->control_in_x;
-                                       point->control_y1 = mask_cursor_y - gui->y_origin + gui->control_in_y;
-                                       break;
+                       case CWINDOW_MASK_CONTROL_IN:
+                               point->control_x1 = mask_cursor_x - gui->x_origin + gui->control_in_x;
+                               point->control_y1 = mask_cursor_y - gui->y_origin + gui->control_in_y;
+                               break;
 
-                               case CWINDOW_MASK_CONTROL_OUT:
-                                       point->control_x2 = mask_cursor_x - gui->x_origin + gui->control_out_x;
-                                       point->control_y2 = mask_cursor_y - gui->y_origin + gui->control_out_y;
-                                       break;
+                       case CWINDOW_MASK_CONTROL_OUT:
+                               point->control_x2 = mask_cursor_x - gui->x_origin + gui->control_out_x;
+                               point->control_y2 = mask_cursor_y - gui->y_origin + gui->control_out_y;
+                               break;
 
-                               case CWINDOW_MASK_TRANSLATE:
-#ifdef USE_KEYFRAME_SPANNING
-                                       for(int i = 0; i < points.size(); i++) {
-                                               points.values[i]->x += mask_cursor_x - gui->x_origin;
-                                               points.values[i]->y += mask_cursor_y - gui->y_origin;
-                                       }
-#else
-                                       for(int i = 0; i < mask->points.size(); i++) {
-                                               mask->points.values[i]->x += mask_cursor_x - gui->x_origin;
-                                               mask->points.values[i]->y += mask_cursor_y - gui->y_origin;
+                       case CWINDOW_MASK_TRANSLATE:
+                               for(int i = 0; i < mask_points.size(); i++) {
+                                       mask_points.values[i]->x += mask_cursor_x - gui->x_origin;
+                                       mask_points.values[i]->y += mask_cursor_y - gui->y_origin;
+                               }
+                               gui->x_origin = mask_cursor_x;
+                               gui->y_origin = mask_cursor_y;
+                               break;
+                       case CWINDOW_MASK_ROTATE:
+                               rotate = 1;
+                       case CWINDOW_MASK_SCALE: {
+                               int button_no = get_buttonpress();
+                               double scale = button_no == WHEEL_UP ? 1.02 : 0.98;
+                               double theta = button_no == WHEEL_UP ? M_PI/360. : -M_PI/360.;
+                               float st = sin(theta), ct = cos(theta);
+                               float cx = 0, cy = 0;
+                               int n = mask_points.size();
+                               if( mask_gui && mask_gui->focused ) {
+                                       cx = atof(mask_gui->focus_x->get_text());
+                                       cy = atof(mask_gui->focus_y->get_text());
+                               }
+                               else if( n > 0 ) {
+                                       for( int i=0; i<n; ++i ) {
+                                               MaskPoint *point = mask_points.values[i];
+                                               cx += point->x;  cy += point->y;
                                        }
-#endif
-                                       gui->x_origin = mask_cursor_x;
-                                       gui->y_origin = mask_cursor_y;
-                                       break;
+                                       cx /= n;  cy /= n;
+                               }
+                               gui->x_origin = cx;
+                               gui->y_origin = cy;
+                               for( int i=0; i<mask_points.size(); ++i ) {
+                                       MaskPoint *point = mask_points.values[i];
+                                       float px = point->x - gui->x_origin;
+                                       float py = point->y - gui->y_origin;
+                                       float nx = !rotate ? px*scale : px*ct + py*st;
+                                       float ny = !rotate ? py*scale : py*ct - px*st;
+                                       point->x = nx + gui->x_origin;
+                                       point->y = ny + gui->y_origin;
+                                       px = point->control_x1;  py = point->control_y1;
+                                       point->control_x1 = !rotate ? px*scale : px*ct + py*st;
+                                       point->control_y1 = !rotate ? py*scale : py*ct - px*st;
+                                       px = point->control_x2;  py = point->control_y2;
+                                       point->control_x2 = !rotate ? px*scale : px*ct + py*st;
+                                       point->control_y2 = !rotate ? py*scale : py*ct - px*st;
+                               }
+                               rerender = 1;
+                               redraw = 1;
+                               break; }
                        }
 
                        if( !EQUIV(last_x, point->x) ||
@@ -2196,6 +2198,19 @@ int CWindowCanvas::do_mask(int &redraw, int &rerender,
        return result;
 }
 
+int CWindowCanvas::do_mask_focus()
+{
+       CWindowMaskGUI *mask_gui = (CWindowMaskGUI*) gui->tool_panel->tool_gui;
+       float cx = get_cursor_x(), cy = get_cursor_y();
+       canvas_to_output(mwindow->edl, 0, cx, cy);
+       get_canvas()->unlock_window();
+       mask_gui->lock_window("CWindowCanvas::do_mask_focus");
+       mask_gui->set_focused(1, cx, cy);
+       mask_gui->unlock_window();
+       get_canvas()->lock_window("CWindowCanvas::do_mask_focus");
+       return 1;
+}
+
 int CWindowCanvas::do_eyedrop(int &rerender, int button_press, int draw)
 {
        int result = 0;
@@ -2469,11 +2484,11 @@ void CWindowCanvas::draw_overlays()
        switch(mwindow->edl->session->cwindow_operation)
        {
                case CWINDOW_CAMERA:
-                       draw_bezier(1);
+                       draw_outlines(1);
                        break;
 
                case CWINDOW_PROJECTOR:
-                       draw_bezier(0);
+                       draw_outlines(0);
                        break;
 
                case CWINDOW_CROP:
@@ -2884,74 +2899,58 @@ void CWindowCanvas::draw_crop()
 }
 
 
-
-
-
-
-
-
-void CWindowCanvas::draw_bezier(int do_camera)
+void CWindowCanvas::draw_outlines(int do_camera)
 {
        Track *track = gui->cwindow->calculate_affected_track();
 
        if(!track) return;
 
-       float center_x;
-       float center_y;
-       float center_z;
+       float proj_x, proj_y, proj_z;
        int64_t position = track->to_units(
                mwindow->edl->local_session->get_selectionstart(1),
                0);
-       if( do_camera ) {
-               track->automation->get_camera(&center_x,
-                       &center_y, &center_z, position, PLAY_FORWARD);
-// follow image, not camera
-               center_x = -center_x * center_z;  center_y = -center_y * center_z;
-       }
-       else
-               track->automation->get_projector(&center_x,
-                       &center_y, &center_z, position, PLAY_FORWARD);
-
-//     center_x += track->track_w / 2;
-//     center_y += track->track_h / 2;
-       center_x += mwindow->edl->session->output_w / 2;
-       center_y += mwindow->edl->session->output_h / 2;
-       float track_x1 = center_x - track->track_w / 2 * center_z;
-       float track_y1 = center_y - track->track_h / 2 * center_z;
-       float track_x2 = track_x1 + track->track_w * center_z;
-       float track_y2 = track_y1 + track->track_h * center_z;
-
-       output_to_canvas(mwindow->edl, 0, track_x1, track_y1);
-       output_to_canvas(mwindow->edl, 0, track_x2, track_y2);
+       track->automation->get_projector(&proj_x, &proj_y, &proj_z,
+                               position, PLAY_FORWARD);
+
+       proj_x += mwindow->edl->session->output_w/2.;
+       proj_y += mwindow->edl->session->output_h/2.;
+       float proj_x1 = proj_x - track->track_w/2. * proj_z;
+       float proj_y1 = proj_y - track->track_h/2. * proj_z;
+       float proj_x2 = proj_x + track->track_w/2. * proj_z;
+       float proj_y2 = proj_y + track->track_h/2. * proj_z;
+       float x1 = proj_x1, x2 = proj_x2;
+       float y1 = proj_y1, y2 = proj_y2;
+       output_to_canvas(mwindow->edl, 0, x1, y1);
+       output_to_canvas(mwindow->edl, 0, x2, y2);
 
 #define DRAW_PROJECTION(offset) \
-       get_canvas()->draw_rectangle((int)track_x1 + offset, \
-               (int)track_y1 + offset, \
-               (int)(track_x2 - track_x1), \
-               (int)(track_y2 - track_y1)); \
-       get_canvas()->draw_line((int)track_x1 + offset,  \
-               (int)track_y1 + offset, \
-               (int)track_x2 + offset, \
-               (int)track_y2 + offset); \
-       get_canvas()->draw_line((int)track_x2 + offset,  \
-               (int)track_y1 + offset, \
-               (int)track_x1 + offset, \
-               (int)track_y2 + offset); \
-
+       get_canvas()->draw_rectangle(x1+offset, y1+offset, (x2-x1), (y2-y1)); \
+       get_canvas()->draw_line(x1+offset, y1+offset, x2+offset, y2+offset); \
+       get_canvas()->draw_line(x2+offset, y1+offset, x1+offset, y2+offset); \
 
 // Drop shadow
        get_canvas()->set_color(BLACK);
        DRAW_PROJECTION(1);
-
-//     canvas->set_inverse();
-       if(do_camera)
-               get_canvas()->set_color(GREEN);
-       else
-               get_canvas()->set_color(RED);
-
+       get_canvas()->set_color(do_camera ? GREEN : RED);
        DRAW_PROJECTION(0);
-//     canvas->set_opaque();
 
+       if( do_camera ) {
+               float cam_x, cam_y, cam_z;
+               track->automation->get_camera(&cam_x, &cam_y, &cam_z,
+                                       position, PLAY_FORWARD);
+               cam_x += track->track_w / 2.;
+               cam_y += track->track_h / 2.;
+// follow image, not camera
+               cam_x = -cam_x;  cam_y = -cam_y;  cam_z *= proj_z;
+               float cam_x1 = cam_x * cam_z + proj_x;
+               float cam_y1 = cam_y * cam_z + proj_y;
+               float cam_x2 = (cam_x + track->track_w) * cam_z + proj_x;
+               float cam_y2 = (cam_y + track->track_h) * cam_z + proj_y;
+               output_to_canvas(mwindow->edl, 0, cam_x1, cam_y1);
+               output_to_canvas(mwindow->edl, 0, cam_x2, cam_y2);
+               get_canvas()->set_color(YELLOW);
+               get_canvas()->draw_rectangle(cam_x1, cam_y1, cam_x2-cam_x1, cam_y2-cam_y1);
+       }
 }
 
 int CWindowCanvas::test_bezier(int button_press,
@@ -2983,7 +2982,7 @@ int CWindowCanvas::test_bezier(int button_press,
 
 // Get target keyframe
                        if( !gui->affected_x && !gui->affected_y && !gui->affected_z ) {
-                               if(!gui->affected_track) return 0;
+                               if( !gui->affected_track ) return 0;
                                FloatAutos *affected_x_autos, *affected_y_autos, *affected_z_autos;
                                FloatAutos** autos = (FloatAutos**) gui->affected_track->automation->autos;
                                if( mwindow->edl->session->cwindow_operation == CWINDOW_CAMERA ) {
@@ -3081,8 +3080,7 @@ int CWindowCanvas::test_bezier(int button_press,
                gui->affected_track = gui->cwindow->calculate_affected_track();
                gui->reset_affected();
 
-               if(gui->affected_track)
-               {
+               if( gui->affected_track ) {
                        if( do_camera )
                                mwindow->undo->update_undo_before(_("camera"), this);
                        else
@@ -3253,12 +3251,7 @@ int CWindowCanvas::cursor_motion_event()
                case CWINDOW_MASK_CONTROL_IN:
                case CWINDOW_MASK_CONTROL_OUT:
                case CWINDOW_MASK_TRANSLATE:
-
-                       result = do_mask(redraw,
-                               rerender,
-                               0,
-                               1,
-                               0);
+                       result = do_mask(redraw, rerender, 0, 1, 0);
                        break;
 
                case CWINDOW_EYEDROP:
@@ -3286,12 +3279,8 @@ int CWindowCanvas::cursor_motion_event()
                                result = do_ruler(0, 1, 0, 0);
                                break;
                        case CWINDOW_MASK:
-                               result = do_mask(redraw,
-                                       rerender,
-                                       0,
-                                       1,
-                                       0);
-                                       break;
+                               result = do_mask(redraw, rerender, 0, 1, 0);
+                               break;
                }
        }
 
@@ -3322,7 +3311,7 @@ int CWindowCanvas::button_press_event()
 
 // Scroll view
        if( mwindow->edl->session->cwindow_operation != CWINDOW_PROTECT &&
-           get_buttonpress() == 2 )
+           get_buttonpress() == MIDDLE_BUTTON && !get_canvas()->shift_down() )
        {
                gui->current_operation = CWINDOW_SCROLL;
                result = 1;
@@ -3353,8 +3342,20 @@ int CWindowCanvas::button_press_event()
                                break;
 
                        case CWINDOW_MASK:
-                               if(get_buttonpress() == 1)
+                               switch( get_buttonpress() ) {
+                               case LEFT_BUTTON:
                                        result = do_mask(redraw, rerender, 1, 0, 0);
+                                       break;
+                               case MIDDLE_BUTTON: {  // && shift_down()
+                                       result = do_mask_focus();
+                                       redraw = redraw_canvas = 1;
+                                       break; }
+                               case WHEEL_UP:
+                               case WHEEL_DOWN:
+                                       result = do_mask(redraw, rerender, 1, 1, 0);
+                                       break;
+                               }
+                               if( result ) redraw_canvas = 1;
                                break;
 
                        case CWINDOW_EYEDROP:
@@ -3372,6 +3373,7 @@ int CWindowCanvas::button_press_event()
 int CWindowCanvas::button_release_event()
 {
        int result = 0;
+       const char *undo_label = 0;
 
        switch(gui->current_operation)
        {
@@ -3384,11 +3386,11 @@ int CWindowCanvas::button_release_event()
                        break;
 
                case CWINDOW_CAMERA:
-                       mwindow->undo->update_undo_after(_("camera"), LOAD_AUTOMATION);
+                       undo_label = _("camera");
                        break;
 
                case CWINDOW_PROJECTOR:
-                       mwindow->undo->update_undo_after(_("projector"), LOAD_AUTOMATION);
+                       undo_label = _("projector");
                        break;
 
                case CWINDOW_MASK:
@@ -3397,13 +3399,23 @@ int CWindowCanvas::button_release_event()
                case CWINDOW_MASK_TRANSLATE:
 // Finish mask operation
                        gui->mask_keyframe = 0;
-                       mwindow->undo->update_undo_after(_("mask"), LOAD_AUTOMATION);
+                       undo_label = _("mask");
+                       break;
+               case CWINDOW_MASK_ROTATE:
+                       gui->mask_keyframe = 0;
+                       undo_label = _("mask rotate");
+                       break;
+               case CWINDOW_MASK_SCALE:
+                       gui->mask_keyframe = 0;
+                       undo_label = _("mask scale");
                        break;
                case CWINDOW_NONE:
                        result = Canvas::button_release_event();
                        break;
        }
 
+       if( undo_label )
+               mwindow->undo->update_undo_after(undo_label, LOAD_AUTOMATION);
        gui->current_operation = CWINDOW_NONE;
        return result;
 }