Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / cwindowtool.C
diff --git a/cinelerra-5.1/cinelerra/cwindowtool.C b/cinelerra-5.1/cinelerra/cwindowtool.C
new file mode 100644 (file)
index 0000000..4730d81
--- /dev/null
@@ -0,0 +1,2324 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2014 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "automation.h"
+#include "cicolors.h"
+#include "clip.h"
+#include "condition.h"
+#include "cpanel.h"
+#include "cplayback.h"
+#include "cwindow.h"
+#include "cwindowgui.h"
+#include "cwindowtool.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "floatauto.h"
+#include "floatautos.h"
+#include "keys.h"
+#include "language.h"
+#include "localsession.h"
+#include "mainsession.h"
+#include "mainundo.h"
+#include "maskauto.h"
+#include "maskautos.h"
+#include "mutex.h"
+#include "mwindow.h"
+#include "mwindowgui.h"
+#include "theme.h"
+#include "track.h"
+#include "trackcanvas.h"
+#include "transportque.h"
+
+
+CWindowTool::CWindowTool(MWindow *mwindow, CWindowGUI *gui)
+ : Thread(1, 0, 0)
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+       tool_gui = 0;
+       done = 0;
+       current_tool = CWINDOW_NONE;
+       set_synchronous(1);
+       input_lock = new Condition(0, "CWindowTool::input_lock");
+       output_lock = new Condition(1, "CWindowTool::output_lock");
+       tool_gui_lock = new Mutex("CWindowTool::tool_gui_lock");
+}
+
+CWindowTool::~CWindowTool()
+{
+       done = 1;
+       stop_tool();
+       input_lock->unlock();
+       Thread::join();
+       delete input_lock;
+       delete output_lock;
+       delete tool_gui_lock;
+}
+
+void CWindowTool::start_tool(int operation)
+{
+       CWindowToolGUI *new_gui = 0;
+       int result = 0;
+
+//printf("CWindowTool::start_tool 1\n");
+       if(current_tool != operation)
+       {
+               current_tool = operation;
+               switch(operation)
+               {
+                       case CWINDOW_EYEDROP:
+                               new_gui = new CWindowEyedropGUI(mwindow, this);
+                               break;
+                       case CWINDOW_CROP:
+                               new_gui = new CWindowCropGUI(mwindow, this);
+                               break;
+                       case CWINDOW_CAMERA:
+                               new_gui = new CWindowCameraGUI(mwindow, this);
+                               break;
+                       case CWINDOW_PROJECTOR:
+                               new_gui = new CWindowProjectorGUI(mwindow, this);
+                               break;
+                       case CWINDOW_MASK:
+                               new_gui = new CWindowMaskGUI(mwindow, this);
+                               break;
+                       case CWINDOW_RULER:
+                               new_gui = new CWindowRulerGUI(mwindow, this);
+                               break;
+                       default:
+                               result = 1;
+                               stop_tool();
+                               break;
+               }
+
+//printf("CWindowTool::start_tool 1\n");
+
+
+               if(!result)
+               {
+                       stop_tool();
+// Wait for previous tool GUI to finish
+                       output_lock->lock("CWindowTool::start_tool");
+                       this->tool_gui = new_gui;
+                       tool_gui->create_objects();
+
+                       if(mwindow->edl->session->tool_window &&
+                               mwindow->session->show_cwindow) tool_gui->show_window();
+                       tool_gui->lock_window("CWindowTool::start_tool 1");
+                       tool_gui->flush();
+                       tool_gui->unlock_window();
+
+
+// Signal thread to run next tool GUI
+                       input_lock->unlock();
+               }
+//printf("CWindowTool::start_tool 1\n");
+       }
+       else
+       if(tool_gui)
+       {
+               tool_gui->lock_window("CWindowTool::start_tool");
+               tool_gui->update();
+               tool_gui->unlock_window();
+       }
+
+//printf("CWindowTool::start_tool 2\n");
+
+}
+
+
+void CWindowTool::stop_tool()
+{
+       if(tool_gui)
+       {
+               tool_gui->lock_window("CWindowTool::stop_tool");
+               tool_gui->set_done(0);
+               tool_gui->unlock_window();
+       }
+}
+
+void CWindowTool::show_tool()
+{
+       if(tool_gui && mwindow->edl->session->tool_window)
+       {
+               tool_gui->lock_window("CWindowTool::show_tool");
+               tool_gui->show_window();
+               tool_gui->unlock_window();
+       }
+}
+
+void CWindowTool::hide_tool()
+{
+       if(tool_gui && mwindow->edl->session->tool_window)
+       {
+               tool_gui->lock_window("CWindowTool::show_tool");
+               tool_gui->hide_window();
+               tool_gui->unlock_window();
+       }
+}
+
+
+void CWindowTool::run()
+{
+       while(!done)
+       {
+               input_lock->lock("CWindowTool::run");
+               if(!done)
+               {
+                       tool_gui->run_window();
+                       tool_gui_lock->lock("CWindowTool::run");
+                       delete tool_gui;
+                       tool_gui = 0;
+                       tool_gui_lock->unlock();
+               }
+               output_lock->unlock();
+       }
+}
+
+void CWindowTool::update_show_window()
+{
+       if(tool_gui)
+       {
+               tool_gui->lock_window("CWindowTool::update_show_window");
+
+               if(mwindow->edl->session->tool_window)
+               {
+                       tool_gui->update();
+                       tool_gui->show_window();
+               }
+               else
+                       tool_gui->hide_window();
+               tool_gui->flush();
+
+               tool_gui->unlock_window();
+       }
+}
+
+void CWindowTool::raise_window()
+{
+       if(tool_gui)
+       {
+               gui->unlock_window();
+               tool_gui->lock_window("CWindowTool::raise_window");
+               tool_gui->raise_window();
+               tool_gui->unlock_window();
+               gui->lock_window("CWindowTool::raise_window");
+       }
+}
+
+void CWindowTool::update_values()
+{
+       tool_gui_lock->lock("CWindowTool::update_values");
+       if(tool_gui)
+       {
+               tool_gui->lock_window("CWindowTool::update_values");
+               tool_gui->update();
+               tool_gui->flush();
+               tool_gui->unlock_window();
+       }
+       tool_gui_lock->unlock();
+}
+
+
+
+
+
+
+
+CWindowToolGUI::CWindowToolGUI(MWindow *mwindow,
+       CWindowTool *thread,
+       const char *title,
+       int w,
+       int h)
+ : BC_Window(title,
+       mwindow->session->ctool_x,
+       mwindow->session->ctool_y,
+       w,
+       h,
+       w,
+       h,
+       0,
+       0,
+       1)
+{
+       this->mwindow = mwindow;
+       this->thread = thread;
+       current_operation = 0;
+}
+
+CWindowToolGUI::~CWindowToolGUI()
+{
+}
+
+int CWindowToolGUI::close_event()
+{
+       hide_window();
+       flush();
+       mwindow->edl->session->tool_window = 0;
+       unlock_window();
+
+
+
+       thread->gui->lock_window("CWindowToolGUI::close_event");
+       thread->gui->composite_panel->set_operation(mwindow->edl->session->cwindow_operation);
+       thread->gui->flush();
+       thread->gui->unlock_window();
+
+       lock_window("CWindowToolGUI::close_event");
+       return 1;
+;}
+
+int CWindowToolGUI::keypress_event()
+{
+       if(get_keypress() == 'w' || get_keypress() == 'W')
+               return close_event();
+       return 0;
+}
+
+int CWindowToolGUI::translation_event()
+{
+       mwindow->session->ctool_x = get_x();
+       mwindow->session->ctool_y = get_y();
+       return 0;
+}
+
+
+
+
+
+
+CWindowCoord::CWindowCoord(CWindowToolGUI *gui, int x, int y, float value, int log_increment = 0)
+ : BC_TumbleTextBox(gui, (float)value, (float)-65536, (float)65536, x, y, 100)
+{
+       this->gui = gui;
+       set_log_floatincrement(log_increment);
+}
+
+CWindowCoord::CWindowCoord(CWindowToolGUI *gui, int x, int y, int value)
+ : BC_TumbleTextBox(gui, (int64_t)value, (int64_t)-65536, (int64_t)65536, x, y, 100)
+{
+       this->gui = gui;
+}
+int CWindowCoord::handle_event()
+{
+       gui->event_caller = this;
+       gui->handle_event();
+       return 1;
+}
+
+
+CWindowCropOK::CWindowCropOK(MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Do it"))
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+}
+int CWindowCropOK::handle_event()
+{
+       mwindow->crop_video();
+       return 1;
+}
+
+
+int CWindowCropOK::keypress_event()
+{
+       if(get_keypress() == 0xd)
+       {
+               handle_event();
+               return 1;
+       }
+       return 0;
+}
+
+
+
+
+
+
+
+CWindowCropGUI::CWindowCropGUI(MWindow *mwindow, CWindowTool *thread)
+ : CWindowToolGUI(mwindow,
+       thread,
+       _(PROGRAM_NAME ": Crop"),
+       330,
+       100)
+{
+}
+
+
+CWindowCropGUI::~CWindowCropGUI()
+{
+}
+
+void CWindowCropGUI::create_objects()
+{
+       int x = 10, y = 10;
+       BC_Title *title;
+
+       lock_window("CWindowCropGUI::create_objects");
+       int column1 = 0;
+       int pad = MAX(BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1),
+               BC_Title::calculate_h(this, "X")) + 5;
+       add_subwindow(title = new BC_Title(x, y, "X1:"));
+       column1 = MAX(column1, title->get_w());
+       y += pad;
+       add_subwindow(title = new BC_Title(x, y, _("W:")));
+       column1 = MAX(column1, title->get_w());
+       y += pad;
+       add_subwindow(new CWindowCropOK(mwindow, thread->tool_gui, x, y));
+
+       x += column1 + 5;
+       y = 10;
+       x1 = new CWindowCoord(thread->tool_gui, x, y,
+               mwindow->edl->session->crop_x1);
+       x1->create_objects();
+       y += pad;
+       width = new CWindowCoord(thread->tool_gui, x, y,
+               mwindow->edl->session->crop_x2 - mwindow->edl->session->crop_x1);
+       width->create_objects();
+
+
+       x += x1->get_w() + 10;
+       y = 10;
+       int column2 = 0;
+       add_subwindow(title = new BC_Title(x, y, "Y1:"));
+       column2 = MAX(column2, title->get_w());
+       y += pad;
+       add_subwindow(title = new BC_Title(x, y, _("H:")));
+       column2 = MAX(column2, title->get_w());
+       y += pad;
+
+       y = 10;
+       x += column2 + 5;
+       y1 = new CWindowCoord(thread->tool_gui, x, y,
+               mwindow->edl->session->crop_y1);
+       y1->create_objects();
+       y += pad;
+       height = new CWindowCoord(thread->tool_gui, x, y,
+               mwindow->edl->session->crop_y2 - mwindow->edl->session->crop_y1);
+       height->create_objects();
+       unlock_window();
+}
+
+void CWindowCropGUI::handle_event()
+{
+       int new_x1, new_y1;
+       new_x1 = atol(x1->get_text());
+       new_y1 = atol(y1->get_text());
+       if(new_x1 != mwindow->edl->session->crop_x1)
+       {
+               mwindow->edl->session->crop_x2 = new_x1 +
+                       mwindow->edl->session->crop_x2 -
+                       mwindow->edl->session->crop_x1;
+               mwindow->edl->session->crop_x1 = new_x1;
+       }
+       if(new_y1 != mwindow->edl->session->crop_y1)
+       {
+               mwindow->edl->session->crop_y2 = new_y1 +
+                       mwindow->edl->session->crop_y2 -
+                       mwindow->edl->session->crop_y1;
+               mwindow->edl->session->crop_y1 = atol(y1->get_text());
+       }
+       mwindow->edl->session->crop_x2 = atol(width->get_text()) +
+               mwindow->edl->session->crop_x1;
+       mwindow->edl->session->crop_y2 = atol(height->get_text()) +
+               mwindow->edl->session->crop_y1;
+       update();
+       mwindow->cwindow->gui->lock_window("CWindowCropGUI::handle_event");
+       mwindow->cwindow->gui->canvas->draw_refresh();
+       mwindow->cwindow->gui->unlock_window();
+}
+
+void CWindowCropGUI::update()
+{
+       x1->update((int64_t)mwindow->edl->session->crop_x1);
+       y1->update((int64_t)mwindow->edl->session->crop_y1);
+       width->update((int64_t)mwindow->edl->session->crop_x2 -
+               mwindow->edl->session->crop_x1);
+       height->update((int64_t)mwindow->edl->session->crop_y2 -
+               mwindow->edl->session->crop_y1);
+}
+
+
+
+
+
+
+CWindowEyedropGUI::CWindowEyedropGUI(MWindow *mwindow, CWindowTool *thread)
+ : CWindowToolGUI(mwindow,
+       thread,
+       _(PROGRAM_NAME ": Color"),
+       200,
+       250)
+{
+}
+
+CWindowEyedropGUI::~CWindowEyedropGUI()
+{
+}
+
+void CWindowEyedropGUI::create_objects()
+{
+       int margin = mwindow->theme->widget_border;
+       int x = margin;
+       int y = margin;
+       int x2 = 70;
+       lock_window("CWindowEyedropGUI::create_objects");
+       BC_Title *title1, *title2, *title3, *title4, *title5, *title6, *title7;
+       add_subwindow(title7 = new BC_Title(x, y, _("Radius:")));
+       y += BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) + margin;
+
+       add_subwindow(title1 = new BC_Title(x, y, _("Red:")));
+       y += title1->get_h() + margin;
+       add_subwindow(title2 = new BC_Title(x, y, _("Green:")));
+       y += title2->get_h() + margin;
+       add_subwindow(title3 = new BC_Title(x, y, _("Blue:")));
+       y += title3->get_h() + margin;
+
+       add_subwindow(title4 = new BC_Title(x, y, "Y:"));
+       y += title4->get_h() + margin;
+       add_subwindow(title5 = new BC_Title(x, y, "U:"));
+       y += title5->get_h() + margin;
+       add_subwindow(title6 = new BC_Title(x, y, "V:"));
+
+
+       radius = new CWindowCoord(this, x2, title7->get_y(),
+               mwindow->edl->session->eyedrop_radius);
+       radius->create_objects();
+       radius->set_boundaries((int64_t)0, (int64_t)255);
+
+
+       add_subwindow(red = new BC_Title(x2, title1->get_y(), "0"));
+       add_subwindow(green = new BC_Title(x2, title2->get_y(), "0"));
+       add_subwindow(blue = new BC_Title(x2, title3->get_y(), "0"));
+
+       add_subwindow(this->y = new BC_Title(x2, title4->get_y(), "0"));
+       add_subwindow(this->u = new BC_Title(x2, title5->get_y(), "0"));
+       add_subwindow(this->v = new BC_Title(x2, title6->get_y(), "0"));
+
+       y = title6->get_y() + this->v->get_h() + margin;
+       add_subwindow(sample = new BC_SubWindow(x, y, 50, 50));
+       update();
+       unlock_window();
+}
+
+void CWindowEyedropGUI::update()
+{
+       radius->update((int64_t)mwindow->edl->session->eyedrop_radius);
+
+       red->update(mwindow->edl->local_session->red);
+       green->update(mwindow->edl->local_session->green);
+       blue->update(mwindow->edl->local_session->blue);
+
+       float y, u, v;
+       YUV::rgb_to_yuv_f(mwindow->edl->local_session->red,
+               mwindow->edl->local_session->green,
+               mwindow->edl->local_session->blue,
+               y,
+               u,
+               v);
+       this->y->update(y);
+       this->u->update(u);
+       this->v->update(v);
+
+       int red = (int)(CLIP(mwindow->edl->local_session->red, 0, 1) * 0xff);
+       int green = (int)(CLIP(mwindow->edl->local_session->green, 0, 1) * 0xff);
+       int blue = (int)(CLIP(mwindow->edl->local_session->blue, 0, 1) * 0xff);
+       sample->set_color((red << 16) | (green << 8) | blue);
+       sample->draw_box(0, 0, sample->get_w(), sample->get_h());
+       sample->set_color(BLACK);
+       sample->draw_rectangle(0, 0, sample->get_w(), sample->get_h());
+       sample->flash();
+}
+
+void CWindowEyedropGUI::handle_event()
+{
+       int new_radius = atoi(radius->get_text());
+       if(new_radius != mwindow->edl->session->eyedrop_radius)
+       {
+               CWindowGUI *gui = mwindow->cwindow->gui;
+               if(gui->eyedrop_visible)
+               {
+                       gui->lock_window("CWindowEyedropGUI::handle_event");
+// hide it
+                       int rerender;
+                       gui->canvas->do_eyedrop(rerender, 0, 1);
+               }
+
+               mwindow->edl->session->eyedrop_radius = new_radius;
+
+               if(gui->eyedrop_visible)
+               {
+// draw it
+                       int rerender;
+                       gui->canvas->do_eyedrop(rerender, 0, 1);
+                       gui->unlock_window();
+               }
+       }
+}
+
+
+
+/* Buttons to control Keyframe-Curve-Mode for Projector or Camera */
+
+// Configuration for all possible Keyframe Curve Mode toggles
+struct _CVD {
+       FloatAuto::t_mode mode;
+       bool use_camera;
+       const char* icon_id;
+       const char* tooltip;
+};
+
+const _CVD Camera_Crv_Smooth =
+       {       FloatAuto::SMOOTH,
+               true,
+               "tan_smooth",
+               _("\"smooth\" Curve on current Camera Keyframes")
+       };
+const _CVD Camera_Crv_Linear =
+       {       FloatAuto::LINEAR,
+               true,
+               "tan_linear",
+               _("\"linear\" Curve on current Camera Keyframes")
+       };
+const _CVD Projector_Crv_Smooth =
+       {       FloatAuto::SMOOTH,
+               false,
+               "tan_smooth",
+               _("\"smooth\" Curve on current Projector Keyframes")
+       };
+const _CVD Projector_Crv_Linear =
+       {       FloatAuto::LINEAR,
+               false,
+               "tan_linear",
+               _("\"linear\" Curve on current Projector Keyframes")
+       };
+
+
+// Implementation Class für Keyframe Curve Mode buttons
+//
+// This button reflects the state of the "current" keyframe
+// (the nearest keyframe on the left) for all three automation
+// lines together. Clicking on this button (re)sets the curve
+// mode for the three "current" keyframes simultanously, but
+// never creates a new keyframe.
+//
+class CWindowCurveToggle : public BC_Toggle
+{
+public:
+       CWindowCurveToggle(_CVD mode, MWindow *mwindow, CWindowToolGUI *gui, int x, int y);
+       void check_toggle_state(FloatAuto *x, FloatAuto *y, FloatAuto *z);
+       int handle_event();
+private:
+       _CVD cfg;
+       MWindow *mwindow;
+       CWindowToolGUI *gui;
+};
+
+
+CWindowCurveToggle::CWindowCurveToggle(_CVD mode, MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
+ : BC_Toggle(x, y, mwindow->theme->get_image_set(mode.icon_id), false),
+   cfg(mode)
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(cfg.tooltip);
+}
+
+void CWindowCurveToggle::check_toggle_state(FloatAuto *x, FloatAuto *y, FloatAuto *z)
+{
+// the toggle state is only set to ON if all
+// three automation lines have the same curve mode.
+// For mixed states the toggle stays off.
+       set_value( x->curve_mode == this->cfg.mode &&
+                  y->curve_mode == this->cfg.mode &&
+                  z->curve_mode == this->cfg.mode
+                ,true // redraw to show new state
+                );
+}
+
+int CWindowCurveToggle::handle_event()
+{
+       FloatAuto *x=0, *y=0, *z=0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+
+       if(track)
+       {       mwindow->cwindow->calculate_affected_autos(&x, &y, &z,
+                       track, cfg.use_camera, 0,0,0); // don't create new keyframe
+
+               if(x)   x->change_curve_mode(cfg.mode);
+               if(y)   y->change_curve_mode(cfg.mode);
+               if(z)   z->change_curve_mode(cfg.mode);
+
+               gui->update();
+               gui->update_preview();
+       }
+
+       return 1;
+}
+
+
+
+
+
+
+CWindowCameraGUI::CWindowCameraGUI(MWindow *mwindow, CWindowTool *thread)
+ : CWindowToolGUI(mwindow,
+       thread,
+       _(PROGRAM_NAME ": Camera"),
+       170,
+       170)
+{
+}
+CWindowCameraGUI::~CWindowCameraGUI()
+{
+}
+
+void CWindowCameraGUI::create_objects()
+{
+       int x = 10, y = 10, x1;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       FloatAuto *x_auto = 0;
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       BC_Title *title;
+       BC_Button *button;
+
+       lock_window("CWindowCameraGUI::create_objects");
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(&x_auto,
+                       &y_auto, &z_auto, track, 1, 0, 0, 0);
+       }
+
+       add_subwindow(title = new BC_Title(x, y, "X:"));
+       x += title->get_w();
+       this->x = new CWindowCoord(this, x, y,
+               x_auto ? x_auto->get_value() : (float)0);
+       this->x->create_objects();
+
+
+       y += 30;
+       x = 10;
+       add_subwindow(title = new BC_Title(x, y, "Y:"));
+       x += title->get_w();
+       this->y = new CWindowCoord(this, x, y,
+               y_auto ? y_auto->get_value() : (float)0);
+       this->y->create_objects();
+       y += 30;
+       x = 10;
+       add_subwindow(title = new BC_Title(x, y, "Z:"));
+       x += title->get_w();
+       this->z = new CWindowCoord(this, x, y,
+               z_auto ? z_auto->get_value() : (float)1);
+       this->z->create_objects();
+       this->z->set_increment(0.01);
+
+       y += 30;
+       x1 = 10;
+       add_subwindow(button = new CWindowCameraLeft(mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(button = new CWindowCameraCenter(mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(button = new CWindowCameraRight(mwindow, this, x1, y));
+
+       y += button->get_h();
+       x1 = 10;
+       add_subwindow(button = new CWindowCameraTop(mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(button = new CWindowCameraMiddle(mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(button = new CWindowCameraBottom(mwindow, this, x1, y));
+// additional Buttons to control the curve mode of the "current" keyframe
+       x1 += button->get_w() + 15;
+       add_subwindow(this->t_smooth = new CWindowCurveToggle(Camera_Crv_Smooth, mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(this->t_linear = new CWindowCurveToggle(Camera_Crv_Linear, mwindow, this, x1, y));
+
+// fill in current auto keyframe values, set toggle states.
+       this->update();
+       unlock_window();
+}
+
+void CWindowCameraGUI::update_preview()
+{
+       mwindow->restart_brender();
+       mwindow->sync_parameters(CHANGE_PARAMS);
+
+       mwindow->cwindow->playback_engine->que->send_command(CURRENT_FRAME,
+                       CHANGE_NONE,
+                       mwindow->edl,
+                       1);
+       mwindow->gui->lock_window("CWindowCameraGUI::update_preview");
+       mwindow->gui->draw_overlays(1);
+       mwindow->gui->unlock_window();
+       mwindow->cwindow->gui->lock_window("CWindowCameraGUI::update_preview");
+       mwindow->cwindow->gui->canvas->draw_refresh();
+       mwindow->cwindow->gui->unlock_window();
+}
+
+
+void CWindowCameraGUI::handle_event()
+{
+       FloatAuto *x_auto = 0;
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->undo->update_undo_before(_("camera"), this);
+               if(event_caller == x)
+               {
+                       x_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                               track->automation->autos[AUTOMATION_CAMERA_X],
+                               1);
+                       if(x_auto)
+                       {
+                               x_auto->set_value(atof(x->get_text()));
+                               update();
+                               update_preview();
+                       }
+               }
+               else
+               if(event_caller == y)
+               {
+                       y_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                               track->automation->autos[AUTOMATION_CAMERA_Y],
+                               1);
+                       if(y_auto)
+                       {
+                               y_auto->set_value(atof(y->get_text()));
+                               update();
+                               update_preview();
+                       }
+               }
+               else
+               if(event_caller == z)
+               {
+                       z_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                               track->automation->autos[AUTOMATION_CAMERA_Z],
+                               1);
+                       if(z_auto)
+                       {
+                               float zoom = atof(z->get_text());
+                               if(zoom > 10) zoom = 10;
+                               else
+                               if(zoom < 0) zoom = 0;
+       // Doesn't allow user to enter from scratch
+       //              if(zoom != atof(z->get_text()))
+       //                      z->update(zoom);
+
+                               z_auto->set_value(zoom);
+                               mwindow->gui->lock_window("CWindowCameraGUI::handle_event");
+                               mwindow->gui->draw_overlays(1);
+                               mwindow->gui->unlock_window();
+                               update();
+                               update_preview();
+                       }
+               }
+
+               mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
+       }
+}
+
+void CWindowCameraGUI::update()
+{
+       FloatAuto *x_auto = 0;
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(&x_auto,
+                       &y_auto,
+                       &z_auto,
+                       track,
+                       1,
+                       0,
+                       0,
+                       0);
+       }
+
+       if(x_auto)
+               x->update(x_auto->get_value());
+       if(y_auto)
+               y->update(y_auto->get_value());
+       if(z_auto)
+               z->update(z_auto->get_value());
+
+       if( x_auto && y_auto && z_auto )
+       {
+               t_smooth->check_toggle_state(x_auto, y_auto, z_auto);
+               t_linear->check_toggle_state(x_auto, y_auto, z_auto);
+       }
+}
+
+
+
+
+CWindowCameraLeft::CWindowCameraLeft(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("left_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Left justify"));
+}
+int CWindowCameraLeft::handle_event()
+{
+       FloatAuto *x_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(&x_auto,
+                       0,
+                       &z_auto,
+                       track,
+                       1,
+                       1,
+                       0,
+                       0);
+       }
+
+       if(x_auto && z_auto)
+       {
+               int w = 0, h = 0;
+               track->get_source_dimensions(
+                       mwindow->edl->local_session->get_selectionstart(1),
+                       w,
+                       h);
+
+               if(w && h)
+               {
+                       mwindow->undo->update_undo_before(_("camera"), 0);
+                       x_auto->set_value(
+                               (double)track->track_w / z_auto->get_value() / 2 -
+                               (double)w / 2);
+                       mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
+                       gui->update();
+                       gui->update_preview();
+               }
+       }
+
+       return 1;
+}
+
+
+CWindowCameraCenter::CWindowCameraCenter(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("center_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Center horizontal"));
+}
+int CWindowCameraCenter::handle_event()
+{
+       FloatAuto *x_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+               x_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                       track->automation->autos[AUTOMATION_CAMERA_X],
+                       1);
+
+       if(x_auto)
+       {
+               mwindow->undo->update_undo_before(_("camera"), 0);
+               x_auto->set_value(0);
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
+       }
+
+       return 1;
+}
+
+
+CWindowCameraRight::CWindowCameraRight(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("right_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Right justify"));
+}
+int CWindowCameraRight::handle_event()
+{
+       FloatAuto *x_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(&x_auto,
+                       0,
+                       &z_auto,
+                       track,
+                       1,
+                       1,
+                       0,
+                       0);
+       }
+
+       if(x_auto && z_auto)
+       {
+               int w = 0, h = 0;
+               track->get_source_dimensions(
+                       mwindow->edl->local_session->get_selectionstart(1),
+                       w,
+                       h);
+
+               if(w && h)
+               {
+                       mwindow->undo->update_undo_before(_("camera"), 0);
+                       x_auto->set_value( -((double)track->track_w / z_auto->get_value() / 2 -
+                               (double)w / 2));
+                       gui->update();
+                       gui->update_preview();
+                       mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
+               }
+       }
+
+       return 1;
+}
+
+
+CWindowCameraTop::CWindowCameraTop(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("top_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Top justify"));
+}
+int CWindowCameraTop::handle_event()
+{
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(0,
+                       &y_auto,
+                       &z_auto,
+                       track,
+                       1,
+                       0,
+                       1,
+                       0);
+       }
+
+       if(y_auto && z_auto)
+       {
+               int w = 0, h = 0;
+               track->get_source_dimensions(
+                       mwindow->edl->local_session->get_selectionstart(1),
+                       w,
+                       h);
+
+               if(w && h)
+               {
+                       mwindow->undo->update_undo_before(_("camera"), 0);
+                       y_auto->set_value((double)track->track_h / z_auto->get_value() / 2 -
+                               (double)h / 2);
+                       gui->update();
+                       gui->update_preview();
+                       mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
+               }
+       }
+
+       return 1;
+}
+
+
+CWindowCameraMiddle::CWindowCameraMiddle(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("middle_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Center vertical"));
+}
+int CWindowCameraMiddle::handle_event()
+{
+       FloatAuto *y_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+               y_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                       track->automation->autos[AUTOMATION_CAMERA_Y], 1);
+
+       if(y_auto)
+       {
+               mwindow->undo->update_undo_before(_("camera"), 0);
+               y_auto->set_value(0);
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
+       }
+
+       return 1;
+}
+
+
+CWindowCameraBottom::CWindowCameraBottom(MWindow *mwindow, CWindowCameraGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("bottom_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Bottom justify"));
+}
+int CWindowCameraBottom::handle_event()
+{
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(0,
+                       &y_auto,
+                       &z_auto,
+                       track,
+                       1,
+                       0,
+                       1,
+                       0);
+       }
+
+       if(y_auto && z_auto)
+       {
+               int w = 0, h = 0;
+               track->get_source_dimensions(
+                       mwindow->edl->local_session->get_selectionstart(1),
+                       w,
+                       h);
+
+               if(w && h)
+               {
+                       mwindow->undo->update_undo_before(_("camera"), 0);
+                       y_auto->set_value(-((double)track->track_h / z_auto->get_value() / 2 -
+                               (double)h / 2));
+                       gui->update();
+                       gui->update_preview();
+                       mwindow->undo->update_undo_after(_("camera"), LOAD_ALL);
+               }
+       }
+
+       return 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CWindowProjectorGUI::CWindowProjectorGUI(MWindow *mwindow, CWindowTool *thread)
+ : CWindowToolGUI(mwindow,
+       thread,
+       _(PROGRAM_NAME ": Projector"),
+       170,
+       170)
+{
+}
+CWindowProjectorGUI::~CWindowProjectorGUI()
+{
+}
+void CWindowProjectorGUI::create_objects()
+{
+       int x = 10, y = 10, x1;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       FloatAuto *x_auto = 0;
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       BC_Title *title;
+       BC_Button *button;
+
+       lock_window("CWindowProjectorGUI::create_objects");
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(&x_auto,
+                       &y_auto,
+                       &z_auto,
+                       track,
+                       0,
+                       0,
+                       0,
+                       0);
+       }
+
+       add_subwindow(title = new BC_Title(x, y, "X:"));
+       x += title->get_w();
+       this->x = new CWindowCoord(this, x, y,
+               x_auto ? x_auto->get_value() : (float)0);
+       this->x->create_objects();
+       y += 30;
+       x = 10;
+       add_subwindow(title = new BC_Title(x, y, "Y:"));
+       x += title->get_w();
+       this->y = new CWindowCoord(this, x, y,
+               y_auto ? y_auto->get_value() : (float)0);
+       this->y->create_objects();
+       y += 30;
+       x = 10;
+       add_subwindow(title = new BC_Title(x, y, "Z:"));
+       x += title->get_w();
+       this->z = new CWindowCoord(this, x, y,
+               z_auto ? z_auto->get_value() : (float)1);
+       this->z->create_objects();
+       this->z->set_increment(0.01);
+
+       y += 30;
+       x1 = 10;
+       add_subwindow(button = new CWindowProjectorLeft(mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(button = new CWindowProjectorCenter(mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(button = new CWindowProjectorRight(mwindow, this, x1, y));
+
+       y += button->get_h();
+       x1 = 10;
+       add_subwindow(button = new CWindowProjectorTop(mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(button = new CWindowProjectorMiddle(mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(button = new CWindowProjectorBottom(mwindow, this, x1, y));
+
+// additional Buttons to control the curve mode of the "current" keyframe
+       x1 += button->get_w() + 15;
+       add_subwindow(this->t_smooth = new CWindowCurveToggle(Projector_Crv_Smooth, mwindow, this, x1, y));
+       x1 += button->get_w();
+       add_subwindow(this->t_linear = new CWindowCurveToggle(Projector_Crv_Linear, mwindow, this, x1, y));
+
+// fill in current auto keyframe values, set toggle states.
+       this->update();
+       unlock_window();
+}
+
+void CWindowProjectorGUI::update_preview()
+{
+       mwindow->restart_brender();
+       mwindow->sync_parameters(CHANGE_PARAMS);
+       mwindow->cwindow->playback_engine->que->send_command(CURRENT_FRAME,
+                       CHANGE_NONE,
+                       mwindow->edl,
+                       1);
+       // TODO: really need to lock the main window??
+       mwindow->gui->lock_window("CWindowProjectorGUI::update_preview");
+       mwindow->gui->draw_overlays(1);
+       mwindow->gui->unlock_window();
+       mwindow->cwindow->gui->lock_window("CWindowProjectorGUI::update_preview");
+       mwindow->cwindow->gui->canvas->draw_refresh();
+       mwindow->cwindow->gui->unlock_window();
+}
+
+void CWindowProjectorGUI::handle_event()
+{
+       FloatAuto *x_auto = 0;
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+
+       if(track)
+       {
+               mwindow->undo->update_undo_before(_("projector"), this);
+               if(event_caller == x)
+               {
+                       x_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                               track->automation->autos[AUTOMATION_PROJECTOR_X],
+                               1);
+                       if(x_auto)
+                       {
+                               x_auto->set_value(atof(x->get_text()));
+                               update();
+                               update_preview();
+                       }
+               }
+               else
+               if(event_caller == y)
+               {
+                       y_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                               track->automation->autos[AUTOMATION_PROJECTOR_Y],
+                               1);
+                       if(y_auto)
+                       {
+                               y_auto->set_value(atof(y->get_text()));
+                               update();
+                               update_preview();
+                       }
+               }
+               else
+               if(event_caller == z)
+               {
+                       z_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                               track->automation->autos[AUTOMATION_PROJECTOR_Z],
+                               1);
+                       if(z_auto)
+                       {
+                               float zoom = atof(z->get_text());
+                               if(zoom > 10000) zoom = 10000;
+                               else
+                               if(zoom < 0) zoom = 0;
+//                     if (zoom != atof(z->get_text()))
+//                             z->update(zoom);
+                               z_auto->set_value(zoom);
+
+                               mwindow->gui->lock_window("CWindowProjectorGUI::handle_event");
+                               mwindow->gui->draw_overlays(1);
+                               mwindow->gui->unlock_window();
+
+                               update();
+                               update_preview();
+                       }
+               }
+               mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
+       }
+}
+
+void CWindowProjectorGUI::update()
+{
+       FloatAuto *x_auto = 0;
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(&x_auto,
+                       &y_auto,
+                       &z_auto,
+                       track,
+                       0,
+                       0,
+                       0,
+                       0);
+       }
+
+       if(x_auto)
+               x->update(x_auto->get_value());
+       if(y_auto)
+               y->update(y_auto->get_value());
+       if(z_auto)
+               z->update(z_auto->get_value());
+
+       if( x_auto && y_auto && z_auto )
+       {
+               t_smooth->check_toggle_state(x_auto, y_auto, z_auto);
+               t_linear->check_toggle_state(x_auto, y_auto, z_auto);
+       }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CWindowProjectorLeft::CWindowProjectorLeft(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("left_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Left justify"));
+}
+int CWindowProjectorLeft::handle_event()
+{
+       FloatAuto *x_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(&x_auto,
+                       0,
+                       &z_auto,
+                       track,
+                       0,
+                       1,
+                       0,
+                       0);
+       }
+       if(x_auto && z_auto)
+       {
+               mwindow->undo->update_undo_before(_("projector"), 0);
+               x_auto->set_value( (double)track->track_w * z_auto->get_value() / 2 -
+                       (double)mwindow->edl->session->output_w / 2 );
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
+       }
+
+       return 1;
+}
+
+
+CWindowProjectorCenter::CWindowProjectorCenter(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("center_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Center horizontal"));
+}
+int CWindowProjectorCenter::handle_event()
+{
+       FloatAuto *x_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+               x_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                       track->automation->autos[AUTOMATION_PROJECTOR_X],
+                       1);
+
+       if(x_auto)
+       {
+               mwindow->undo->update_undo_before(_("projector"), 0);
+               x_auto->set_value(0);
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
+       }
+
+       return 1;
+}
+
+
+CWindowProjectorRight::CWindowProjectorRight(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("right_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Right justify"));
+}
+int CWindowProjectorRight::handle_event()
+{
+       FloatAuto *x_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(&x_auto,
+                       0,
+                       &z_auto,
+                       track,
+                       0,
+                       1,
+                       0,
+                       0);
+       }
+
+       if(x_auto && z_auto)
+       {
+               mwindow->undo->update_undo_before(_("projector"), 0);
+               x_auto->set_value( -((double)track->track_w * z_auto->get_value() / 2 -
+                       (double)mwindow->edl->session->output_w / 2));
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
+       }
+
+       return 1;
+}
+
+
+CWindowProjectorTop::CWindowProjectorTop(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("top_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Top justify"));
+}
+int CWindowProjectorTop::handle_event()
+{
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(0,
+                       &y_auto,
+                       &z_auto,
+                       track,
+                       0,
+                       0,
+                       1,
+                       0);
+       }
+
+       if(y_auto && z_auto)
+       {
+               mwindow->undo->update_undo_before(_("projector"), 0);
+               y_auto->set_value( (double)track->track_h * z_auto->get_value() / 2 -
+                       (double)mwindow->edl->session->output_h / 2 );
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
+       }
+
+       return 1;
+}
+
+
+CWindowProjectorMiddle::CWindowProjectorMiddle(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("middle_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Center vertical"));
+}
+int CWindowProjectorMiddle::handle_event()
+{
+       FloatAuto *y_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+               y_auto = (FloatAuto*)mwindow->cwindow->calculate_affected_auto(
+                       track->automation->autos[AUTOMATION_PROJECTOR_Y], 1);
+
+       if(y_auto)
+       {
+               mwindow->undo->update_undo_before(_("projector"), 0);
+               y_auto->set_value(0);
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
+       }
+
+       return 1;
+}
+
+
+CWindowProjectorBottom::CWindowProjectorBottom(MWindow *mwindow, CWindowProjectorGUI *gui, int x, int y)
+ : BC_Button(x, y, mwindow->theme->get_image_set("bottom_justify"))
+{
+       this->gui = gui;
+       this->mwindow = mwindow;
+       set_tooltip(_("Bottom justify"));
+}
+int CWindowProjectorBottom::handle_event()
+{
+       FloatAuto *y_auto = 0;
+       FloatAuto *z_auto = 0;
+       Track *track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               mwindow->cwindow->calculate_affected_autos(0,
+                       &y_auto,
+                       &z_auto,
+                       track,
+                       0,
+                       0,
+                       1,
+                       0);
+       }
+
+       if(y_auto && z_auto)
+       {
+               mwindow->undo->update_undo_before(_("projector"), 0);
+               y_auto->set_value( -((double)track->track_h * z_auto->get_value() / 2 -
+                       (double)mwindow->edl->session->output_h / 2));
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("projector"), LOAD_ALL);
+       }
+
+       return 1;
+}
+
+
+
+
+
+
+
+
+CWindowMaskMode::CWindowMaskMode(MWindow *mwindow,
+       CWindowToolGUI *gui, int x, int y, const char *text)
+ : BC_PopupMenu(x, y, 220, text, 1)
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+}
+
+void CWindowMaskMode::create_objects()
+{
+       add_item(new BC_MenuItem(mode_to_text(MASK_MULTIPLY_ALPHA)));
+       add_item(new BC_MenuItem(mode_to_text(MASK_SUBTRACT_ALPHA)));
+}
+
+char* CWindowMaskMode::mode_to_text(int mode)
+{
+       switch(mode)
+       {
+               case MASK_MULTIPLY_ALPHA:
+                       return _("Multiply alpha");
+                       break;
+
+               case MASK_SUBTRACT_ALPHA:
+                       return _("Subtract alpha");
+                       break;
+       }
+
+       return _("Subtract alpha");
+}
+
+int CWindowMaskMode::text_to_mode(char *text)
+{
+       if(!strcasecmp(text, _("Multiply alpha")))
+               return MASK_MULTIPLY_ALPHA;
+       else
+       if(!strcasecmp(text, _("Subtract alpha")))
+               return MASK_SUBTRACT_ALPHA;
+
+       return MASK_SUBTRACT_ALPHA;
+}
+
+int CWindowMaskMode::handle_event()
+{
+       MaskAutos *autos;
+       MaskAuto *keyframe;
+       Track *track;
+       MaskPoint *point;
+       SubMask *mask;
+// Get existing keyframe
+       ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe, mask, point, 0);
+       if(track)
+       {
+               mwindow->undo->update_undo_before(_("mask mode"), 0);
+#ifdef USE_KEYFRAME_SPANNING
+// Create temp keyframe
+               MaskAuto temp_keyframe(mwindow->edl, autos);
+               temp_keyframe.copy_data(keyframe);
+// Update parameter
+               temp_keyframe.mode = text_to_mode(get_text());
+// Commit change to span of keyframes
+               autos->update_parameter(&temp_keyframe);
+#else
+               ((MaskAuto*)autos->default_auto)->mode =
+                       text_to_mode(get_text());
+#endif
+               mwindow->undo->update_undo_after(_("mask mode"), LOAD_AUTOMATION);
+       }
+
+//printf("CWindowMaskMode::handle_event 1\n");
+       gui->update_preview();
+       return 1;
+}
+
+
+
+
+
+
+
+
+CWindowMaskDelete::CWindowMaskDelete(MWindow *mwindow,
+       CWindowToolGUI *gui,
+       int x,
+       int y)
+ : BC_GenericButton(x, y, _("Delete"))
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+}
+
+int CWindowMaskDelete::handle_event()
+{
+       MaskAutos *autos;
+       MaskAuto *keyframe;
+       Track *track;
+       MaskPoint *point;
+       SubMask *mask;
+
+// Get existing keyframe
+       ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe, mask, point, 0);
+
+       if(track)
+       {
+               mwindow->undo->update_undo_before(_("mask delete"), 0);
+
+#ifdef USE_KEYFRAME_SPANNING
+// Create temp keyframe
+               MaskAuto temp_keyframe(mwindow->edl, autos);
+               temp_keyframe.copy_data(keyframe);
+// Update parameter
+               SubMask *submask = temp_keyframe.get_submask(mwindow->edl->session->cwindow_mask);
+               for(int i = mwindow->cwindow->gui->affected_point;
+                       i < submask->points.total - 1;
+                       i++)
+               {
+                       *submask->points.values[i] = *submask->points.values[i + 1];
+               }
+
+               if(submask->points.total)
+               {
+                       submask->points.remove_object(
+                               submask->points.values[submask->points.total - 1]);
+               }
+// Commit change to span of keyframes
+               ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->update_parameter(&temp_keyframe);
+#else
+               for(MaskAuto *current = (MaskAuto*)autos->default_auto;
+                       current; )
+               {
+                       SubMask *submask = current->get_submask(mwindow->edl->session->cwindow_mask);
+
+
+
+                       for(int i = mwindow->cwindow->gui->affected_point;
+                               i < submask->points.total - 1;
+                               i++)
+                       {
+                               *submask->points.values[i] = *submask->points.values[i + 1];
+                       }
+
+                       if(submask->points.total)
+                       {
+                               submask->points.remove_object(
+                                       submask->points.values[submask->points.total - 1]);
+                       }
+
+
+                       if(current == (MaskAuto*)autos->default_auto)
+                               current = (MaskAuto*)autos->first;
+                       else
+                               current = (MaskAuto*)NEXT;
+               }
+#endif
+
+               gui->update();
+               gui->update_preview();
+               mwindow->undo->update_undo_after(_("mask delete"), LOAD_AUTOMATION);
+       }
+
+
+       return 1;
+}
+
+int CWindowMaskDelete::keypress_event()
+{
+       if(get_keypress() == BACKSPACE ||
+               get_keypress() == DELETE)
+               return handle_event();
+       return 0;
+}
+
+
+// CWindowMaskCycleNext::CWindowMaskCycleNext(MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
+//  : BC_GenericButton(x, y, _("Cycle next"))
+// {
+//     this->mwindow = mwindow;
+//     this->gui = gui;
+// }
+// int CWindowMaskCycleNext::handle_event()
+// {
+//     MaskAuto *keyframe;
+//     MaskAutos *autos;
+//     Track *track;
+//     MaskPoint *point;
+//     SubMask *mask;
+//     ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe, mask, point, 0);
+//
+//     MaskPoint *temp;
+//
+// // Should apply to all keyframes
+//     if(keyframe && mask->points.total)
+//     {
+//             temp = mask->points.values[0];
+//
+//             for(int i = 0; i < mask->points.total - 1; i++)
+//             {
+//                     mask->points.values[i] = mask->points.values[i + 1];
+//             }
+//             mask->points.values[mask->points.total - 1] = temp;
+//
+//             mwindow->cwindow->gui->affected_point--;
+//             if(mwindow->cwindow->gui->affected_point < 0)
+//                     mwindow->cwindow->gui->affected_point = mask->points.total - 1;
+//
+//             gui->update();
+//             gui->update_preview();
+//     }
+//
+//     return 1;
+// }
+//
+// CWindowMaskCyclePrev::CWindowMaskCyclePrev(MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
+//  : BC_GenericButton(x, y, _("Cycle prev"))
+// {
+//     this->mwindow = mwindow;
+//     this->gui = gui;
+// }
+// int CWindowMaskCyclePrev::handle_event()
+// {
+//     MaskAuto *keyframe;
+//     MaskAutos *autos;
+//     Track *track;
+//     MaskPoint *point;
+//     SubMask *mask;
+//     ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe, mask, point, 0);
+//
+// // Should apply to all keyframes
+//     MaskPoint *temp;
+//     if(keyframe && mask->points.total)
+//     {
+//             temp = mask->points.values[mask->points.total - 1];
+//
+//             for(int i = mask->points.total - 1; i > 0; i--)
+//             {
+//                     mask->points.values[i] = mask->points.values[i - 1];
+//             }
+//             mask->points.values[0] = temp;
+//
+//             mwindow->cwindow->gui->affected_point++;
+//             if(mwindow->cwindow->gui->affected_point >= mask->points.total)
+//                     mwindow->cwindow->gui->affected_point = 0;
+//
+//             gui->update();
+//             gui->update_preview();
+//     }
+//     return 1;
+// }
+
+
+CWindowMaskNumber::CWindowMaskNumber(MWindow *mwindow,
+       CWindowToolGUI *gui,
+       int x,
+       int y)
+ : BC_TumbleTextBox(gui,
+               (int64_t)mwindow->edl->session->cwindow_mask,
+               (int64_t)0,
+               (int64_t)SUBMASKS - 1,
+               x,
+               y,
+               100)
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+}
+
+CWindowMaskNumber::~CWindowMaskNumber()
+{
+}
+
+int CWindowMaskNumber::handle_event()
+{
+       mwindow->edl->session->cwindow_mask = atol(get_text());
+       gui->update();
+       gui->update_preview();
+       return 1;
+}
+
+
+
+
+
+CWindowMaskFeather::CWindowMaskFeather(MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
+ : BC_TumbleTextBox(gui,
+               (int64_t)0,
+               (int64_t)0,
+               (int64_t)0xff,
+               x,
+               y,
+               100)
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+}
+CWindowMaskFeather::~CWindowMaskFeather()
+{
+}
+int CWindowMaskFeather::handle_event()
+{
+       MaskAutos *autos;
+       MaskAuto *keyframe;
+       Track *track;
+       MaskPoint *point;
+       SubMask *mask;
+#ifdef USE_KEYFRAME_SPANNING
+       int create_it = 0;
+#else
+       int create_it = 1;
+#endif
+
+       mwindow->undo->update_undo_before(_("mask feather"), this);
+
+// Get existing keyframe
+       ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe,
+                       mask, point, create_it);
+
+       if(track)
+       {       
+#ifdef USE_KEYFRAME_SPANNING
+// Create temp keyframe
+               MaskAuto temp_keyframe(mwindow->edl, autos);
+               temp_keyframe.copy_data(keyframe);
+// Update parameter
+               temp_keyframe.feather = atof(get_text());
+// Commit change to span of keyframes
+               autos->update_parameter(&temp_keyframe);
+#else
+               keyframe->feather = atof(get_text());
+#endif
+
+               gui->update_preview();
+       }
+
+       mwindow->undo->update_undo_after(_("mask feather"), LOAD_AUTOMATION);
+       return 1;
+}
+
+
+CWindowMaskValue::CWindowMaskValue(MWindow *mwindow, CWindowToolGUI *gui, int x, int y)
+ : BC_ISlider(x,
+                       y,
+                       0,
+                       200,
+                       200,
+                       0,
+                       100,
+                       0)
+{
+       this->mwindow = mwindow;
+       this->gui = gui;
+}
+
+CWindowMaskValue::~CWindowMaskValue()
+{
+}
+
+int CWindowMaskValue::handle_event()
+{
+       MaskAutos *autos;
+       MaskAuto *keyframe;
+       Track *track;
+       MaskPoint *point;
+       SubMask *mask;
+#ifdef USE_KEYFRAME_SPANNING
+       int create_it = 0;
+#else
+       int create_it = 1;
+#endif
+       
+       mwindow->undo->update_undo_before(_("mask value"), this);
+       ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe,
+               mask, point, create_it);
+
+       if(track)
+       {
+#ifdef USE_KEYFRAME_SPANNING
+// Create temp keyframe
+               MaskAuto temp_keyframe(mwindow->edl, autos);
+               temp_keyframe.copy_data(keyframe);
+// Update parameter
+               temp_keyframe.value = get_value();
+// Commit change to span of keyframes
+               autos->update_parameter(&temp_keyframe);
+#else
+               keyframe->value = get_value();
+#endif
+       }
+
+       gui->update_preview();
+       mwindow->undo->update_undo_after(_("mask value"), LOAD_AUTOMATION);
+       return 1;
+}
+
+
+CWindowMaskBeforePlugins::CWindowMaskBeforePlugins(CWindowToolGUI *gui, int x, int y)
+ : BC_CheckBox(x,
+       y,
+       1,
+       _("Apply mask before plugins"))
+{
+       this->gui = gui;
+}
+
+int CWindowMaskBeforePlugins::handle_event()
+{
+       Track *track;
+       MaskAutos *autos;
+       MaskAuto *keyframe;
+       SubMask *mask;
+       MaskPoint *point;
+       ((CWindowMaskGUI*)gui)->get_keyframe(track, autos, keyframe, mask, point, 1);
+
+       if (keyframe) {
+               keyframe->apply_before_plugins = get_value();
+               gui->update_preview();
+       }
+       return 1;
+}
+
+
+
+
+
+
+
+
+CWindowMaskGUI::CWindowMaskGUI(MWindow *mwindow, CWindowTool *thread)
+ : CWindowToolGUI(mwindow,
+       thread,
+       _(PROGRAM_NAME ": Mask"),
+       330,
+       280)
+{
+       this->mwindow = mwindow;
+       this->thread = thread;
+}
+CWindowMaskGUI::~CWindowMaskGUI()
+{
+       lock_window("CWindowMaskGUI::~CWindowMaskGUI");
+       delete number;
+       delete feather;
+       unlock_window();
+}
+
+void CWindowMaskGUI::create_objects()
+{
+       int x = 10, y = 10, margin = mwindow->theme->widget_border;
+       //MaskAuto *keyframe = 0;
+       //Track *track = mwindow->cwindow->calculate_affected_track();
+       //if(track)
+       //      keyframe = (MaskAuto*)mwindow->cwindow->calculate_affected_auto(track->automation->autos[AUTOMATION_MASK], 0);
+
+       lock_window("CWindowMaskGUI::create_objects");
+       BC_Title *title;
+       add_subwindow(title = new BC_Title(x, y, _("Mode:")));
+       add_subwindow(mode = new CWindowMaskMode(mwindow,
+               this, x + title->get_w() + margin, y, ""));
+       mode->create_objects();
+       y += mode->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, _("Value:")));
+       add_subwindow(value = new CWindowMaskValue(mwindow, this, x + title->get_w() + margin, y));
+       y += value->get_h() + margin;
+       add_subwindow(delete_point = new CWindowMaskDelete(mwindow, this, x, y));
+       y += delete_point->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, _("Mask number:")));
+       number = new CWindowMaskNumber(mwindow,
+               this, x + title->get_w() + margin, y);
+       number->create_objects();
+       y += number->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, _("Feather:")));
+       feather = new CWindowMaskFeather(mwindow,
+               this, x + title->get_w() + margin, y);
+       feather->create_objects();
+       y += feather->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, "X:"));
+       x += title->get_w() + margin;
+       this->x = new CWindowCoord(this, x, y, (float)0.0);
+       this->x->create_objects();
+       x += this->x->get_w() + margin;
+       add_subwindow(title = new BC_Title(x, y, "Y:"));
+       x += title->get_w() + margin;
+       this->y = new CWindowCoord(this, x, y, (float)0.0);
+       this->y->create_objects();
+
+       x = 10;
+       y += this->y->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, _("Press Ctrl to move a point")));
+       y += title->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, _("Press Alt to translate the mask")));
+       y += title->get_h() + margin;
+       add_subwindow(title = new BC_Title(x, y, _("Press Shift to edit bezier curve")));
+
+       y += 30;
+//     add_subwindow(title = new BC_Title(x, y, _("Apply mask before plugins:")));
+
+       add_subwindow(this->apply_before_plugins = new CWindowMaskBeforePlugins(this,
+               10,
+               y));
+//     this->apply_before_plugins->create_objects();
+
+       update();
+       unlock_window();
+}
+
+void CWindowMaskGUI::get_keyframe(Track* &track,
+       MaskAutos* &autos,
+       MaskAuto* &keyframe,
+       SubMask* &mask,
+       MaskPoint* &point,
+       int create_it)
+{
+       autos = 0;
+       keyframe = 0;
+
+       track = mwindow->cwindow->calculate_affected_track();
+       if(track)
+       {
+               autos = (MaskAutos*)track->automation->autos[AUTOMATION_MASK];
+               keyframe = (MaskAuto*)mwindow->cwindow->calculate_affected_auto(
+                       autos,
+                       create_it);
+       }
+
+       if(keyframe)
+               mask = keyframe->get_submask(mwindow->edl->session->cwindow_mask);
+       else
+               mask = 0;
+
+       point = 0;
+       if(keyframe)
+       {
+               if(mwindow->cwindow->gui->affected_point < mask->points.total &&
+                       mwindow->cwindow->gui->affected_point >= 0)
+               {
+                       point = mask->points.values[mwindow->cwindow->gui->affected_point];
+               }
+       }
+}
+
+void CWindowMaskGUI::update()
+{
+       Track *track;
+       MaskAutos *autos;
+       MaskAuto *keyframe;
+       SubMask *mask;
+       MaskPoint *point;
+//printf("CWindowMaskGUI::update 1\n");
+       get_keyframe(track, autos, keyframe, mask, point, 0);
+
+       double position = mwindow->edl->local_session->get_selectionstart(1);
+       position = mwindow->edl->align_to_frame(position, 0);
+       if(track)
+       {
+               int64_t position_i = track->to_units(position, 0);
+
+               if(point)
+               {
+                       x->update(point->x);
+                       y->update(point->y);
+               }
+
+               if(mask)
+               {
+                       feather->update((int64_t)autos->get_feather(position_i, PLAY_FORWARD));
+                       value->update((int64_t)autos->get_value(position_i, PLAY_FORWARD));
+                       apply_before_plugins->update((int64_t)keyframe->apply_before_plugins);
+               }
+       }
+//printf("CWindowMaskGUI::update 1\n");
+
+       number->update((int64_t)mwindow->edl->session->cwindow_mask);
+
+//printf("CWindowMaskGUI::update 1\n");
+       if(track)
+       {
+#ifdef USE_KEYFRAME_SPANNING
+               mode->set_text(
+                       CWindowMaskMode::mode_to_text(keyframe->mode));
+#else
+               mode->set_text(
+                       CWindowMaskMode::mode_to_text(((MaskAuto*)autos->default_auto)->mode));
+#endif
+       }
+//printf("CWindowMaskGUI::update 2\n");
+}
+
+void CWindowMaskGUI::handle_event()
+{
+       Track *track;
+       MaskAuto *keyframe;
+       MaskAutos *autos;
+       SubMask *mask;
+       MaskPoint *point;
+       get_keyframe(track, autos, keyframe, mask, point, 0);
+
+       mwindow->undo->update_undo_before(_("mask point"), this);
+
+       if(point)
+       {
+#ifdef USE_KEYFRAME_SPANNING
+// Create temp keyframe
+               MaskAuto temp_keyframe(mwindow->edl, autos);
+               temp_keyframe.copy_data(keyframe);
+// Get affected point in temp keyframe
+               mask = temp_keyframe.get_submask(mwindow->edl->session->cwindow_mask);
+               if(mwindow->cwindow->gui->affected_point < mask->points.total &&
+                       mwindow->cwindow->gui->affected_point >= 0)
+               {
+                       point = mask->points.values[mwindow->cwindow->gui->affected_point];
+               }
+
+               if(point)
+               {
+                       point->x = atof(x->get_text());
+                       point->y = atof(y->get_text());
+// Commit to spanned keyframes
+                       autos->update_parameter(&temp_keyframe);
+               }
+#else
+               point->x = atof(x->get_text());
+               point->y = atof(y->get_text());
+#endif
+       }
+
+       update_preview();
+       mwindow->undo->update_undo_after(_("mask point"), LOAD_AUTOMATION);
+}
+
+void CWindowMaskGUI::update_preview()
+{
+       mwindow->restart_brender();
+       mwindow->sync_parameters(CHANGE_PARAMS);
+       mwindow->cwindow->playback_engine->que->send_command(CURRENT_FRAME,
+                       CHANGE_NONE,
+                       mwindow->edl,
+                       1);
+       mwindow->cwindow->gui->lock_window("CWindowMaskGUI::update_preview");
+       mwindow->cwindow->gui->canvas->draw_refresh();
+       mwindow->cwindow->gui->unlock_window();
+}
+
+
+CWindowRulerGUI::CWindowRulerGUI(MWindow *mwindow, CWindowTool *thread)
+ : CWindowToolGUI(mwindow,
+       thread,
+       _(PROGRAM_NAME ": Ruler"),
+       320,
+       240)
+{
+}
+
+CWindowRulerGUI::~CWindowRulerGUI()
+{
+}
+
+void CWindowRulerGUI::create_objects()
+{
+       int x = 10, y = 10;
+       BC_Title *title;
+
+       lock_window("CWindowRulerGUI::create_objects");
+       add_subwindow(title = new BC_Title(x, y, _("Current:")));
+       add_subwindow(current = new BC_Title(x + title->get_w() + 10, y, ""));
+       y += title->get_h() + 5;
+
+       add_subwindow(title = new BC_Title(x, y, _("Point 1:")));
+       add_subwindow(point1 = new BC_Title(x + title->get_w() + 10, y, ""));
+       y += title->get_h() + 5;
+
+       add_subwindow(title = new BC_Title(x, y, _("Point 2:")));
+       add_subwindow(point2 = new BC_Title(x + title->get_w() + 10, y, ""));
+       y += title->get_h() + 5;
+
+       add_subwindow(title = new BC_Title(x, y, _("Distance:")));
+       add_subwindow(distance = new BC_Title(x + title->get_w() + 10, y, ""));
+       y += title->get_h() + 5;
+       add_subwindow(title = new BC_Title(x, y, _("Angle:")));
+       add_subwindow(angle = new BC_Title(x + title->get_w() + 10, y, ""));
+       y += title->get_h() + 10;
+       char string[BCTEXTLEN];
+       sprintf(string, _("Press Ctrl to lock ruler to the\nnearest 45%c angle."), 0xb0);
+       add_subwindow(title = new BC_Title(x,
+               y,
+               string));
+       y += title->get_h() + 10;
+       sprintf(string, _("Press Alt to translate the ruler."));
+       add_subwindow(title = new BC_Title(x,
+               y,
+               string));
+       update();
+       unlock_window();
+}
+
+void CWindowRulerGUI::update()
+{
+       double distance_value =
+               sqrt(SQR(mwindow->edl->session->ruler_x2 - mwindow->edl->session->ruler_x1) +
+               SQR(mwindow->edl->session->ruler_y2 - mwindow->edl->session->ruler_y1));
+       double angle_value = atan((mwindow->edl->session->ruler_y2 - mwindow->edl->session->ruler_y1) /
+               (mwindow->edl->session->ruler_x2 - mwindow->edl->session->ruler_x1)) *
+               360 /
+               2 /
+               M_PI;
+
+       if(EQUIV(distance_value, 0.0))
+       {
+               angle_value = 0.0;
+       }
+       else
+       if(angle_value < 0)
+       {
+               angle_value *= -1;
+       }
+
+       char string[BCTEXTLEN];
+       sprintf(string, "%d, %d",
+               mwindow->session->cwindow_output_x,
+               mwindow->session->cwindow_output_y);
+       current->update(string);
+       sprintf(string, "%.0f, %.0f",
+               mwindow->edl->session->ruler_x1,
+               mwindow->edl->session->ruler_y1);
+       point1->update(string);
+       sprintf(string, "%.0f, %.0f",
+               mwindow->edl->session->ruler_x2,
+               mwindow->edl->session->ruler_y2);
+       point2->update(string);
+
+       sprintf(string, _("%0.01f pixels"), distance_value);
+       distance->update(string);
+       sprintf(string, "%0.02f %c", angle_value, 0xb0);
+       angle->update(string);
+}
+
+void CWindowRulerGUI::handle_event()
+{
+}
+
+
+
+