diff --git a/SConstruct b/SConstruct index 8a2316996..dc0f76864 100644 --- a/SConstruct +++ b/SConstruct @@ -257,7 +257,7 @@ def configurePlatform(env): if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): problems.append('Unable to configure Lib glib--, exiting.') - if not conf.CheckPkgConfig('gthread-2.0', '2.16'): + if not conf.CheckPkgConfig('gthread-2.0', '2.12'): problems.append('Need gthread support lib for glib-- based thread handling.') if not conf.CheckPkgConfig('cairomm-1.0', 0.6): diff --git a/configure.ac b/configure.ac index 717fe6f4b..0cf1d61bb 100644 --- a/configure.ac +++ b/configure.ac @@ -141,9 +141,13 @@ PKG_CHECK_MODULES(LUMIERA_COMMON_LIBS, [sigc++-2.0 >= 2.0.17]) # gtk+-2.0 >= 2.12 gtkmm-2.4 >= 2.12 for Debian Lenny compatibility PKG_CHECK_MODULES(LUMIERA_GUI, [ - gtk+-2.0 >= 2.8 gtkmm-2.4 >= 2.8 - gdl-1.0 >= 0.6.1 cairomm-1.0 >= 0.6.0 - gavl >= 0.2.5 librsvg-2.0 >= 2.18.1]) + gtk+-2.0 >= 2.8 + gtkmm-2.4 >= 2.8 + cairomm-1.0 >= 0.6.0 + librsvg-2.0 >= 2.18.1 + gdl-1.0 >= 0.6.1 + gavl >= 0.2.5 + gthread-2.0 >= 2.12.4]) # END Gtk Dependancies diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index e46c1e78d..44a77c0c3 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -163,6 +163,10 @@ libgui_la_SOURCES = \ $(lumigui_srcdir)/model/sequence.hpp \ $(lumigui_srcdir)/model/clip.cpp \ $(lumigui_srcdir)/model/clip.hpp \ + $(lumigui_srcdir)/controller/controller.cpp \ + $(lumigui_srcdir)/controller/controller.hpp \ + $(lumigui_srcdir)/controller/playback-controller.cpp \ + $(lumigui_srcdir)/controller/playback-controller.hpp \ $(lumigui_srcdir)/output/displayer.cpp \ $(lumigui_srcdir)/output/displayer.hpp \ $(lumigui_srcdir)/output/gdkdisplayer.cpp \ diff --git a/src/gui/controller/controller.cpp b/src/gui/controller/controller.cpp new file mode 100644 index 000000000..2d7deefc9 --- /dev/null +++ b/src/gui/controller/controller.cpp @@ -0,0 +1,41 @@ +/* + controllerk.cpp - Implementation of the timeline track object + + Copyright (C) Lumiera.org + 2008, Joel Holdsworth + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +#include "controller.hpp" + +namespace gui { +namespace controller { + +Controller::Controller(model::Project &model_project) : + project(model_project) +{ + +} + +PlaybackController& Controller::get_playback_controller() +{ + return playback; +} + +} // namespace controller +} // namespace gui + diff --git a/src/gui/controller/controller.hpp b/src/gui/controller/controller.hpp new file mode 100644 index 000000000..27f185e97 --- /dev/null +++ b/src/gui/controller/controller.hpp @@ -0,0 +1,56 @@ +/* + controller.hpp - Declaration of the controller object + + Copyright (C) Lumiera.org + 2009, Joel Holdsworth + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +/** @file controller/controller.hpp + ** This file contains the definition of the controller object + */ + +#include "playback-controller.hpp" + +#ifndef CONTROLLER_HPP +#define CONTROLLER_HPP + +namespace gui { + +namespace model { +class Project; +} // namespace model + +namespace controller { + +class Controller +{ +public: + Controller(model::Project &model_project); + + PlaybackController& get_playback_controller(); + +private: + model::Project &project; + + PlaybackController playback; +}; + +} // namespace controller +} // namespace gui + +#endif // CONTROLLER_HPP + diff --git a/src/gui/controller/playback-controller.cpp b/src/gui/controller/playback-controller.cpp new file mode 100644 index 000000000..8b79f21d6 --- /dev/null +++ b/src/gui/controller/playback-controller.cpp @@ -0,0 +1,207 @@ +/* + playback-controller.cpp - Implementation of the playback controller object + + Copyright (C) Lumiera.org + 2008, Joel Holdsworth + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +#include "playback-controller.hpp" +#include "../gtk-lumiera.hpp" + +namespace gui { +namespace controller { + +PlaybackController::PlaybackController() : + finish_playback_thread(false), + playing(false) +{ + start_playback_thread(); +} + +PlaybackController::~PlaybackController() +{ + mutex.lock(); + finish_playback_thread = true; + mutex.unlock(); + thread->join(); +} + +void +PlaybackController::play() +{ + Glib::Mutex::Lock lock(mutex); + playing = true; +} + +void +PlaybackController::pause() +{ + Glib::Mutex::Lock lock(mutex); + playing = false; +} + +void +PlaybackController::stop() +{ + Glib::Mutex::Lock lock(mutex); + playing = false; +} + +bool +PlaybackController::is_playing() +{ + Glib::Mutex::Lock lock(mutex); + return playing; +} + +void +PlaybackController::start_playback_thread() +{ + dispatcher.connect(sigc::mem_fun(this, &PlaybackController::on_frame)); + thread = Glib::Thread::create (sigc::mem_fun( + this, &PlaybackController::playback_thread), true); +} + +void +PlaybackController::attach_viewer( + const sigc::slot& on_frame) +{ + frame_signal.connect(on_frame); +} + +void +PlaybackController::playback_thread() +{ + for(;;) + { + { + Glib::Mutex::Lock lock(mutex); + if(finish_playback_thread) + return; + } + + if(is_playing()) + pull_frame(); + + Glib::Thread::yield(); + } +} + +typedef unsigned char byte; + +inline int +clamp(const int &val, const int &maxval, const int &minval) +{ + if(val > maxval) return maxval; + if(val < minval) return minval; + return val; +} + +inline void +rgb_to_yuv(int r, int g, int b, byte &y, byte &u, byte &v) +{ + // This code isn't great, but it does the job + y = (byte)clamp((299 * r + 587 * g + 114 * b) / 1000, 235, 16); + v = (byte)clamp((500 * r - 419 * g - 81 * b) / 1000 + 127, 255, 0); + u = (byte)clamp((-169 * r - 331 * g + 500 * b) / 1000 + 127, 255, 0); +} + +void rgb_buffer_to_yuy2(unsigned char *in, unsigned char *out) +{ + for(int i = 0; i < 320*240*2; i+=4) + { + byte y0, u0, v0; + const byte r0 = *(in++); + const byte g0 = *(in++); + const byte b0 = *(in++); + rgb_to_yuv(r0, g0, b0, y0, u0, v0); + + byte y1, u1, v1; + const byte r1 = *(in++); + const byte g1 = *(in++); + const byte b1 = *(in++); + rgb_to_yuv(r1, g1, b1, y1, u1, v1); + + out[i] = y0; + out[i + 1] = (byte)(((int)u0 + (int)u1) / 2); + out[i + 2] = y1; + out[i + 3] = (byte)(((int)v0 + (int)v1) / 2); + } +} + +void +PlaybackController::pull_frame() +{ + static int frame = 0; + unsigned char in[320 * 240 * 3]; + + frame--; + + if(frame <= 0) + frame = 200; + + if(frame > 150) + { + for(int i = 0; i < 320*240*3; i+=3) + { + byte value = (byte)rand(); + in[i] = value; + in[i+1] = value; + in[i+2] = value; + } + } + else + { + unsigned char row[320 * 3]; + + + for(int x = 0; x < 320; x++) + { + byte &r = row[x*3]; + byte &g = row[x*3+1]; + byte &b = row[x*3+2]; + + if(x < 1*320/7) r = 0xC0, g = 0xC0, b = 0xC0; + else if(x < 2*320/7) r = 0xC0, g = 0xC0, b = 0x00; + else if(x < 3*320/7) r = 0x00, g = 0xC0, b = 0xC0; + else if(x < 4*320/7) r = 0x00, g = 0xC0, b = 0x00; + else if(x < 5*320/7) r = 0xC0, g = 0x00, b = 0xC0; + else if(x < 6*320/7) r = 0xC0, g = 0x00, b = 0x00; + else r = 0x00, g = 0x00, b = 0xC0; + } + + for(int y = 0; y < 240; y++) + { + memcpy(in + y*320*3, row, sizeof(row)); + } + } + + rgb_buffer_to_yuy2(in, buffer); + + dispatcher.emit(); +} + +void +PlaybackController::on_frame() +{ + frame_signal.emit(buffer); +} + +} // namespace controller +} // namespace gui + diff --git a/src/gui/controller/playback-controller.hpp b/src/gui/controller/playback-controller.hpp new file mode 100644 index 000000000..9333fcbc2 --- /dev/null +++ b/src/gui/controller/playback-controller.hpp @@ -0,0 +1,84 @@ +/* + playback-controller.hpp - Declaration of the playback controller object + + Copyright (C) Lumiera.org + 2009, Joel Holdsworth + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +/** @file controller/playback-controller.hpp + ** This file contains the definition of the playback controller object + */ + +#include +#include + +#ifndef PLAYBACK_CONTROLLER_HPP +#define PLAYBACK_CONTROLLER_HPP + +namespace gui { +namespace controller { + +class PlaybackController +{ +public: + + PlaybackController(); + + ~PlaybackController(); + + void play(); + + void pause(); + + void stop(); + + bool is_playing(); + + void attach_viewer(const sigc::slot& on_frame); + +private: + + void start_playback_thread(); + + void playback_thread(); + + void pull_frame(); + + void on_frame(); + +private: + + Glib::Thread *thread; + + Glib::StaticMutex mutex; + + Glib::Dispatcher dispatcher; + + volatile bool finish_playback_thread; + + volatile bool playing; + + unsigned char buffer[320 * 240 * 4]; + + sigc::signal frame_signal; +}; + +} // namespace controller +} // namespace gui + +#endif // PLAYBACK_CONTROLLER_HPP + diff --git a/src/gui/gtk-lumiera.cpp b/src/gui/gtk-lumiera.cpp index 9fd41b94e..eb8fbe023 100644 --- a/src/gui/gtk-lumiera.cpp +++ b/src/gui/gtk-lumiera.cpp @@ -31,6 +31,7 @@ #include "window-manager.hpp" #include "workspace/workspace-window.hpp" #include "model/project.hpp" +#include "controller/controller.hpp" extern "C" { #include "common/interface.h" @@ -43,6 +44,7 @@ using namespace Glib; using namespace gui; using namespace gui::workspace; using namespace gui::model; +using namespace gui::controller; GtkLumiera the_application; @@ -55,17 +57,19 @@ namespace gui { void GtkLumiera::main(int argc, char *argv[]) { + Glib::thread_init(); Main kit(argc, argv); Glib::set_application_name(AppTitle); Project project; + Controller controller(project); WindowManager window_manager; window_manager.set_theme("lumiera_ui.rc"); - WorkspaceWindow main_window(&project); + WorkspaceWindow main_window(project, controller); kit.run(main_window); diff --git a/src/gui/panels/assets-panel.cpp b/src/gui/panels/assets-panel.cpp index 316ebf256..1e189c0fc 100644 --- a/src/gui/panels/assets-panel.cpp +++ b/src/gui/panels/assets-panel.cpp @@ -26,8 +26,8 @@ namespace gui { namespace panels { -AssetsPanel::AssetsPanel(model::Project *const owner_project) : - Panel(owner_project, "assets", _("Assets"), "panel_assets"), +AssetsPanel::AssetsPanel(workspace::WorkspaceWindow &workspace_window) : + Panel(workspace_window, "assets", _("Assets"), "panel_assets"), placeholder("Assets/Media") { pack_start(placeholder); diff --git a/src/gui/panels/assets-panel.hpp b/src/gui/panels/assets-panel.hpp index 05c473dca..33513243a 100644 --- a/src/gui/panels/assets-panel.hpp +++ b/src/gui/panels/assets-panel.hpp @@ -34,11 +34,12 @@ namespace panels { class AssetsPanel : public Panel { public: + /** - * Constructor - * @param owner_project The project associated with this panel. + * Contructor. + * @param workspace_window The window that owns this panel. **/ - AssetsPanel(model::Project *const owner_project); + AssetsPanel(workspace::WorkspaceWindow &workspace_window); protected: Gtk::Label placeholder; diff --git a/src/gui/panels/panel.cpp b/src/gui/panels/panel.cpp index 05fb096b1..aa600cafd 100644 --- a/src/gui/panels/panel.cpp +++ b/src/gui/panels/panel.cpp @@ -26,12 +26,11 @@ namespace gui { namespace panels { -Panel::Panel(model::Project *const owner_project, +Panel::Panel(workspace::WorkspaceWindow &workspace_window, const gchar *name, const gchar *long_name, GdlDockItemBehavior behavior) : - project(owner_project) + workspace(workspace_window) { - REQUIRE(owner_project != NULL); REQUIRE(name != NULL); REQUIRE(long_name != NULL); @@ -42,12 +41,11 @@ Panel::Panel(model::Project *const owner_project, ENSURE(dock_item != NULL); } -Panel::Panel(model::Project *const owner_project, +Panel::Panel(workspace::WorkspaceWindow &workspace_window, const gchar *name, const gchar *long_name, const gchar *stock_id, GdlDockItemBehavior behavior) : - project(owner_project) + workspace(workspace_window) { - REQUIRE(owner_project != NULL); REQUIRE(name != NULL); REQUIRE(long_name != NULL); REQUIRE(stock_id != NULL); diff --git a/src/gui/panels/panel.hpp b/src/gui/panels/panel.hpp index 669a7a4f1..941afa0b8 100644 --- a/src/gui/panels/panel.hpp +++ b/src/gui/panels/panel.hpp @@ -32,8 +32,8 @@ namespace gui { -namespace model { -class Project; +namespace workspace { +class WorkspaceWindow; } namespace panels { @@ -46,24 +46,24 @@ class Panel : public Gtk::VBox protected: /** * Constructs a panel object - * @param owner_project The project associated with this panel. + * @param workspace_window The window that owns this panel. * @param name The internal name of this panel * @param long_name The name to display on the caption * @param behavior The GDL behaviour of this item */ - Panel(model::Project *const owner_project, + Panel(workspace::WorkspaceWindow &workspace_window, const gchar *name, const gchar *long_name, GdlDockItemBehavior behavior = GDL_DOCK_ITEM_BEH_NORMAL); /** * Constructs a panel object with a stock item for a caption - * @param owner_project The project associated with this panel. + * @param owner_window The window that owns this panel. * @param name The internal name of this panel * @param long_name The name to display on the caption * @param stock_id The id of the stock item to display on the caption * @param behavior The GDL behaviour of this item */ - Panel(model::Project *const owner_project, + Panel(workspace::WorkspaceWindow &owner_window, const gchar *name, const gchar *long_name, const gchar *stock_id, GdlDockItemBehavior behavior = GDL_DOCK_ITEM_BEH_NORMAL); @@ -99,7 +99,7 @@ private: protected: - model::Project *const project; + workspace::WorkspaceWindow &workspace; GdlDockItem* dock_item; }; diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index f1ca132d2..0c7a566cb 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -25,7 +25,9 @@ #include "../gtk-lumiera.hpp" #include "timeline-panel.hpp" +#include "../workspace/workspace-window.hpp" #include "../model/project.hpp" +#include "../controller/controller.hpp" extern "C" { #include "../../lib/time.h" @@ -43,8 +45,9 @@ namespace panels { const int TimelinePanel::ZoomToolSteps = 2; // 2 seems comfortable -TimelinePanel::TimelinePanel(model::Project *const owner_project) : - Panel(owner_project, "timeline", _("Timeline"), "panel_timeline"), +TimelinePanel::TimelinePanel(workspace::WorkspaceWindow + &workspace_window) : + Panel(workspace_window, "timeline", _("Timeline"), "panel_timeline"), timeIndicator(), previousButton(Stock::MEDIA_PREVIOUS), rewindButton(Stock::MEDIA_REWIND), @@ -65,7 +68,7 @@ TimelinePanel::TimelinePanel(model::Project *const owner_project) : // mem_fun(this, &TimelinePanel::on_playback_period_drag_released)); // Hook up notifications - project->get_sequences().signal_changed().connect( + workspace.get_project().get_sequences().signal_changed().connect( mem_fun(this, &TimelinePanel::on_sequence_list_changed)); // Setup the notebook @@ -119,15 +122,14 @@ TimelinePanel::~TimelinePanel() void TimelinePanel::on_play_pause() -{ - // TEST CODE! +{ if(!is_playing()) { play(); } else { - frameEvent.disconnect(); + pause(); } update_playback_buttons(); @@ -136,12 +138,8 @@ TimelinePanel::on_play_pause() void TimelinePanel::on_stop() { - // TEST CODE! - /*timelineWidget.set_playback_point(GAVL_TIME_UNDEFINED); - frameEvent.disconnect(); - show_time(timelineWidget.get_playback_period_start()); - - update_playback_buttons();*/ + workspace.get_controller().get_playback_controller().stop(); + update_playback_buttons(); } void @@ -223,7 +221,7 @@ TimelinePanel::update_notebook() old_pages.swap(notebook_pages); BOOST_FOREACH( shared_ptr< model::Sequence > sequence, - project->get_sequences() ) + workspace.get_project().get_sequences() ) { std::map >:: iterator iterator = old_pages.find(sequence.get()); @@ -283,20 +281,21 @@ TimelinePanel::update_zoom_buttons() void TimelinePanel::play() +{ + workspace.get_controller().get_playback_controller().play(); +} + +void +TimelinePanel::pause() { - /*if(timelineWidget.get_playback_point() == GAVL_TIME_UNDEFINED) - timelineWidget.set_playback_point( - timelineWidget.get_playback_period_start()); - frameEvent = Glib::signal_timeout().connect( - sigc::mem_fun(this, &TimelinePanel::on_frame), - 1000 / 25);*/ + workspace.get_controller().get_playback_controller().pause(); } bool TimelinePanel::is_playing() const { - // TEST CODE! - this should be hooked up to the real playback control - return frameEvent.connected(); + return workspace.get_controller().get_playback_controller(). + is_playing(); } void diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index 846d675fe..9c860b05a 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -48,10 +48,9 @@ class TimelinePanel : public Panel public: /** * Constructor - * @param owner_project The project associated with this panel. + * @param workspace_window The window that owns this panel. */ - TimelinePanel(model::Project *const owner_project); - + TimelinePanel(workspace::WorkspaceWindow &workspace_window); /** * Destructor @@ -88,6 +87,9 @@ private: void update_zoom_buttons(); void play(); + + void pause(); + bool is_playing() const; void set_tool(gui::widgets::timeline::ToolType tool); @@ -138,7 +140,6 @@ private: private: // TEST CODE bool on_frame(); - sigc::connection frameEvent; //----- Constants -----// private: diff --git a/src/gui/panels/viewer-panel.cpp b/src/gui/panels/viewer-panel.cpp index 070dd2d29..f012a406e 100644 --- a/src/gui/panels/viewer-panel.cpp +++ b/src/gui/panels/viewer-panel.cpp @@ -23,17 +23,36 @@ #include "../gtk-lumiera.hpp" #include "viewer-panel.hpp" -using namespace gui::widgets; +#include "../workspace/workspace-window.hpp" +#include "../controller/controller.hpp" +#include "../controller/playback-controller.hpp" + using namespace Gtk; +using namespace gui::widgets; +using namespace gui::controller; namespace gui { namespace panels { -ViewerPanel::ViewerPanel(model::Project *const owner_project) : - Panel(owner_project, "viewer", _("Viewer"), "panel_viewer") +ViewerPanel::ViewerPanel(workspace::WorkspaceWindow &workspace_window) : + Panel(workspace_window, "viewer", _("Viewer"), "panel_viewer") { //----- Pack in the Widgets -----// pack_start(display, PACK_EXPAND_WIDGET); + + PlaybackController &playback = + workspace_window.get_controller().get_playback_controller(); + + playback.attach_viewer(sigc::mem_fun(this, &ViewerPanel::on_frame)); +} + +void +ViewerPanel::on_frame(void *buffer) +{ + Displayer *displayer = display.get_displayer(); + REQUIRE(displayer); + + displayer->put(buffer); } } // namespace panels diff --git a/src/gui/panels/viewer-panel.hpp b/src/gui/panels/viewer-panel.hpp index 027ab1fc2..b56fb3701 100644 --- a/src/gui/panels/viewer-panel.hpp +++ b/src/gui/panels/viewer-panel.hpp @@ -42,9 +42,13 @@ class ViewerPanel : public Panel public: /** * Contructor. - @param owner_project The project associated with this panel. + * @param workspace_window The window that owns this panel. **/ - ViewerPanel(model::Project *const owner_project); + ViewerPanel(workspace::WorkspaceWindow &owner_window); + +protected: + + void on_frame(void *buffer); protected: diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 9859a3def..0e589bba0 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -484,6 +484,12 @@ TimelineWidget::get_y_scroll_offset() const return (int)verticalAdjustment.get_value(); } +void +TimelineWidget::set_y_scroll_offset(const int offset) +{ + verticalAdjustment.set_value(offset); +} + bool TimelineWidget::on_motion_in_body_notify_event(GdkEventMotion *event) { diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index b95073230..7113d100f 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -226,6 +226,8 @@ private: int get_y_scroll_offset() const; + void set_y_scroll_offset(const int offset); + // ----- Event Handlers -----// /** diff --git a/src/gui/widgets/timeline/timeline-header-container.cpp b/src/gui/widgets/timeline/timeline-header-container.cpp index 172c130d6..4041ab554 100644 --- a/src/gui/widgets/timeline/timeline-header-container.cpp +++ b/src/gui/widgets/timeline/timeline-header-container.cpp @@ -36,6 +36,13 @@ using namespace util; namespace gui { namespace widgets { namespace timeline { + +// ===== Constants ===== // + +const int TimelineHeaderContainer::ScrollSlideRateDivisor = 4; +const int TimelineHeaderContainer::ScrollSlideEventInterval = 40; + +// ===== Implementation ===== // TimelineHeaderContainer::TimelineHeaderContainer( gui::widgets::TimelineWidget &timeline_widget) : @@ -176,7 +183,7 @@ bool TimelineHeaderContainer::on_button_release_event ( // Has the user been dragging? if(layout.get_dragging_track()) - layout.end_dragging_track(); + end_drag(); return Container::on_button_release_event(event); } @@ -201,14 +208,26 @@ bool TimelineHeaderContainer::on_motion_notify_event ( if((event->state & GDK_BUTTON1_MASK) && hoveringTrack && !layout.get_dragging_track()) { - layout.begin_dragging_track(mousePoint); + begin_drag(); return result; } // Are we currently dragging? if(layout.get_dragging_track()) { + // Forward the message to the layout manager layout.drag_to_point(mousePoint); + + // Is the mouse out of bounds? if so we must begin scrolling + const int height = get_allocation().get_height(); + const int y = mousePoint.get_y(); + + if(y < 0) + begin_scroll_slide(y / ScrollSlideRateDivisor); + else if(y > height) + begin_scroll_slide((y - height) / ScrollSlideRateDivisor); + else end_scroll_slide(); + return result; } @@ -304,6 +323,22 @@ TimelineHeaderContainer::on_hovering_track_changed( } + +bool +TimelineHeaderContainer::on_scroll_slide_timer() +{ + // Shift the view + const int view_height = get_allocation().get_height(); + timelineWidget.set_y_scroll_offset( + timelineWidget.get_y_scroll_offset() + + scrollSlideRate * view_height / 256); + + // Keep the layout manager updated + timelineWidget.layoutHelper.drag_to_point(mousePoint); + + // Return true to keep the timer going + return true; +} void TimelineHeaderContainer::layout_headers() @@ -365,6 +400,84 @@ TimelineHeaderContainer::lookup_timeline_track( return timeline_track; } +void +TimelineHeaderContainer::begin_drag() +{ + TimelineLayoutHelper &layout = timelineWidget.layoutHelper; + + shared_ptr dragging_track = + layout.begin_dragging_track(mousePoint); + ENSURE(dragging_track); // Something strange has happened if we + // were somehow not hovering on a track + + const TimelineLayoutHelper::TrackTree::pre_order_iterator node = + layout.iterator_from_track(dragging_track->get_model_track()); + set_keep_above_recursive(node, true); +} + +void +TimelineHeaderContainer::end_drag() +{ + TimelineLayoutHelper &layout = timelineWidget.layoutHelper; + + shared_ptr dragging_track = + layout.get_dragging_track(); + ENSURE(dragging_track); // Something strange has happened if we + // were somehow not dragging on a track + + const TimelineLayoutHelper::TrackTree::pre_order_iterator node = + layout.iterator_from_track(dragging_track->get_model_track()); + set_keep_above_recursive(node, false); + + layout.end_dragging_track(); +} + +void +TimelineHeaderContainer::set_keep_above_recursive( + TimelineLayoutHelper::TrackTree::iterator_base node, + const bool keep_above) +{ + TimelineLayoutHelper::TrackTree::pre_order_iterator iter; + + const TimelineLayoutHelper::TrackTree &layout_tree = + timelineWidget.layoutHelper.get_layout_tree(); + + shared_ptr timeline_track = + lookup_timeline_track(*node); + REQUIRE(timeline_track); + + Glib::RefPtr window = + timeline_track->get_header_widget().get_window(); + ENSURE(window); // Something strange has happened if there was no + // window + window->set_keep_above(keep_above); + + for(iter = layout_tree.begin(node); + iter != layout_tree.end(node); + iter++) + { + set_keep_above_recursive(iter, keep_above); + } +} + +void +TimelineHeaderContainer::begin_scroll_slide(int scroll_slide_rate) +{ + scrollSlideRate = scroll_slide_rate; + if(!scrollSlideEvent.connected()) + scrollSlideEvent = Glib::signal_timeout().connect(sigc::mem_fun( + this, &TimelineHeaderContainer::on_scroll_slide_timer), + ScrollSlideEventInterval); +} + +void +TimelineHeaderContainer::end_scroll_slide() +{ + scrollSlideRate = 0; + if(scrollSlideEvent.connected()) + scrollSlideEvent.disconnect(); +} + } // namespace timeline } // namespace widgets } // namespace gui diff --git a/src/gui/widgets/timeline/timeline-header-container.hpp b/src/gui/widgets/timeline/timeline-header-container.hpp index 6526244c3..4b2883d47 100644 --- a/src/gui/widgets/timeline/timeline-header-container.hpp +++ b/src/gui/widgets/timeline/timeline-header-container.hpp @@ -29,6 +29,7 @@ #define HEADER_CONTAINER_HPP #include "../../gtk-lumiera.hpp" +#include "timeline-layout-helper.hpp" namespace gui { @@ -139,6 +140,15 @@ private: void on_hovering_track_changed( boost::shared_ptr hovering_track); +private: + /* ===== Internal Event Handlers ===== */ + + /** + * An internal event handler, which is called when the scroll slide + * timer calls it. + */ + bool on_scroll_slide_timer(); + /* ===== Internals ===== */ private: @@ -172,6 +182,27 @@ private: **/ boost::shared_ptr lookup_timeline_track( boost::shared_ptr model_track); + + void begin_drag(); + + + void end_drag(); + + void set_keep_above_recursive( + TimelineLayoutHelper::TrackTree::iterator_base node, + const bool keep_above); + + /** + * Begins, or continues a scroll slide at a given rate + * @param scroll_slide_rate The distance to slide every timer event + * in units of 1/256th of the view height. + */ + void begin_scroll_slide(int scroll_slide_rate); + + /** + * Ends a scroll slide, and disconnects the slide timer + */ + void end_scroll_slide(); private: @@ -195,11 +226,38 @@ private: * click is not processed by track headers. **/ Gtk::Menu contextMenu; + + /** + * This connection is used to represent the timer which causes scroll + * sliding to occur. + * @remarks Scroll sliding is an animated scroll which occurs when + * the user drags a header outside the area of the timeline body. + */ + sigc::connection scrollSlideEvent; + + /** + * Specifies the rate at which scroll sliding is currently taking + * place. + */ + int scrollSlideRate; //----- User Interaction State -----// boost::shared_ptr hoveringTrack; Gdk::Point mousePoint; + + /* ===== Constants ===== */ + /** + * The amount to divide the mouse overshoot by to produce the slide + * scroll rate. + * @remarks Smaller values cause faster scrolling. + */ + static const int ScrollSlideRateDivisor; + + /** + * The interval between scroll slide events in ms. + */ + static const int ScrollSlideEventInterval; friend class gui::widgets::TimelineWidget; friend class timeline::Track; diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index 4efb2cf2a..dccd9d143 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -128,32 +128,25 @@ TimelineLayoutHelper::track_from_y(int y) return shared_ptr(); } -bool +shared_ptr TimelineLayoutHelper::begin_dragging_track( const Gdk::Point &mouse_point) { draggingTrack = header_from_point(mouse_point); if(!draggingTrack) - return false; + return shared_ptr(); const Gdk::Rectangle &rect = headerBoxes[draggingTrack]; dragStartOffset = Gdk::Point( mouse_point.get_x() - rect.get_x(), - mouse_point.get_y() - rect.get_y()); + mouse_point.get_y() - rect.get_y() + + timelineWidget.get_y_scroll_offset()); - // Find the track in the tree const shared_ptr model_track = draggingTrack->get_model_track(); - REQUIRE(model_track); - for(draggingTrackIter = layoutTree.begin(); - draggingTrackIter != layoutTree.end(); - draggingTrackIter++) - { - if(*draggingTrackIter == model_track) - break; - } + draggingTrackIter = iterator_from_track(model_track); - return true; + return draggingTrack; } void @@ -187,7 +180,7 @@ TimelineLayoutHelper::drag_to_point(const Gdk::Point &point) const weak_ptr track = lookup_timeline_track(*iterator); - if(util::pt_in_rect(point, headerBoxes[track])) + if(util::pt_in_rect(dragPoint, headerBoxes[track])) { // Relocate the header draggingTrackIter = layoutTree.move_after( @@ -211,6 +204,22 @@ TimelineLayoutHelper::is_animating() const return animating; } +TimelineLayoutHelper::TrackTree::pre_order_iterator +TimelineLayoutHelper::iterator_from_track( + boost::shared_ptr model_track) +{ + REQUIRE(model_track); + + TrackTree::pre_order_iterator iter; + for(iter = layoutTree.begin(); iter != layoutTree.end(); iter++) + { + if(*iter == model_track) + break; + } + + return iter; +} + void TimelineLayoutHelper::update_layout() { @@ -243,24 +252,28 @@ TimelineLayoutHelper::layout_headers_recursive( REQUIRE(depth >= 0); int child_offset = 0; - + TrackTree::sibling_iterator iterator; for(iterator = layoutTree.begin(parent_iterator); iterator != layoutTree.end(parent_iterator); iterator++) { Gdk::Rectangle rect; + int track_height = 0; + const shared_ptr &model_track = *iterator; REQUIRE(model_track); - shared_ptr timeline_track = lookup_timeline_track(model_track); + + const bool being_dragged = (timeline_track == draggingTrack); // Is the track going to be shown? if(parent_expanded) { // Calculate and store the box of the header - const int track_height = timeline_track->get_height(); + track_height = timeline_track->get_height() + + TimelineWidget::TrackPadding; const int indent = depth * indent_width; rect = Gdk::Rectangle( @@ -270,19 +283,15 @@ TimelineLayoutHelper::layout_headers_recursive( track_height); // height // Offset for the next header - child_offset += track_height + TimelineWidget::TrackPadding; + child_offset += track_height; // Is this header being dragged? - if(timeline_track == draggingTrack) - { - rect.set_y(dragPoint.get_y() - dragStartOffset.get_y()); - } + if(being_dragged) + rect.set_y(dragPoint.get_y() - dragStartOffset.get_y()); headerBoxes[timeline_track] = rect; } - - // Is the track animating? const bool is_track_animating = timeline_track->is_expand_animating(); @@ -292,10 +301,11 @@ TimelineLayoutHelper::layout_headers_recursive( const bool expand_child = (animating || timeline_track->get_expanded()) && parent_expanded; - + int child_branch_height = layout_headers_recursive( - iterator, branch_offset + child_offset, - header_width, indent_width, depth + 1, expand_child); + iterator, rect.get_y() + track_height, + header_width, indent_width, depth + 1, + expand_child); // Do collapse animation as necessary if(is_track_animating) diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index a93f88fa4..68f44939a 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -127,7 +127,8 @@ public: **/ boost::shared_ptr track_from_y(int y); - bool begin_dragging_track(const Gdk::Point &mouse_point); + boost::shared_ptr + begin_dragging_track(const Gdk::Point &mouse_point); void end_dragging_track(); @@ -144,6 +145,16 @@ public: int get_total_height() const; bool is_animating() const; + + /** + * A utility function which finds the iterator of a track in the + * layout tree. + * @param model_track The model track to look for. + * @return Returns the model iterator of layoutTree.end() if no + * iterator was found. + **/ + TrackTree::pre_order_iterator iterator_from_track( + boost::shared_ptr model_track); protected: diff --git a/src/gui/widgets/video-display-widget.cpp b/src/gui/widgets/video-display-widget.cpp index 0c396ed4d..4a31af101 100644 --- a/src/gui/widgets/video-display-widget.cpp +++ b/src/gui/widgets/video-display-widget.cpp @@ -45,6 +45,12 @@ VideoDisplayWidget::~VideoDisplayWidget() delete displayer; } +Displayer* +VideoDisplayWidget::get_displayer() const +{ + return displayer; +} + void VideoDisplayWidget::on_realize() { @@ -61,21 +67,6 @@ VideoDisplayWidget::on_realize() add_events(Gdk::ALL_EVENTS_MASK); } -bool -VideoDisplayWidget::on_button_press_event (GdkEventButton* event) -{ - (void)event; - - unsigned char buffer[320 * 240 * 4]; - - for(int i = 0; i < 320*240*4; i++) - buffer[i] = rand(); - - displayer->put((void*)buffer); - - return true; -} - Displayer* VideoDisplayWidget::createDisplayer( Gtk::Widget *drawingArea, int width, int height ) { diff --git a/src/gui/widgets/video-display-widget.hpp b/src/gui/widgets/video-display-widget.hpp index 6bf829f7a..6587362cf 100644 --- a/src/gui/widgets/video-display-widget.hpp +++ b/src/gui/widgets/video-display-widget.hpp @@ -41,13 +41,12 @@ public: VideoDisplayWidget(); ~VideoDisplayWidget(); + + Displayer* get_displayer() const; /* ===== Overrides ===== */ private: virtual void on_realize(); - - // TEST CODE!!!! - virtual bool on_button_press_event (GdkEventButton* event); /* ===== Internals ===== */ private: diff --git a/src/gui/workspace/actions.cpp b/src/gui/workspace/actions.cpp index ed5a43002..2ac749528 100644 --- a/src/gui/workspace/actions.cpp +++ b/src/gui/workspace/actions.cpp @@ -188,7 +188,7 @@ Actions::on_menu_sequence_add() dialogs::NameChooser dialog(workspaceWindow, _("Add Sequence"), _("New Sequence")); if(dialog.run() == RESPONSE_OK) - workspaceWindow.get_project()->add_new_sequence(dialog.get_name()); + workspaceWindow.get_project().add_new_sequence(dialog.get_name()); } /* ===== Track Menu Event Handlers ===== */ diff --git a/src/gui/workspace/workspace-window.cpp b/src/gui/workspace/workspace-window.cpp index ee315e026..24557053d 100644 --- a/src/gui/workspace/workspace-window.cpp +++ b/src/gui/workspace/workspace-window.cpp @@ -37,16 +37,17 @@ using namespace Gtk; using namespace gui::model; +using namespace gui::controller; namespace gui { namespace workspace { -WorkspaceWindow::WorkspaceWindow(Project *source_project) : +WorkspaceWindow::WorkspaceWindow(Project &source_project, + gui::controller::Controller &source_controller) : project(source_project), + controller(source_controller), actions(*this) -{ - REQUIRE(source_project != NULL); - +{ layout = NULL; assetsPanel = NULL; viewerPanel = NULL; @@ -68,12 +69,18 @@ WorkspaceWindow::~WorkspaceWindow() timelinePanel->unreference(); } -Project* -WorkspaceWindow::get_project() const +Project& +WorkspaceWindow::get_project() { return project; } +Controller& +WorkspaceWindow::get_controller() +{ + return controller; +} + void WorkspaceWindow::create_ui() { @@ -152,11 +159,11 @@ WorkspaceWindow::create_ui() baseContainer.pack_start(*toolbar, Gtk::PACK_SHRINK); //----- Create the Panels -----// - assetsPanel = new AssetsPanel(project); + assetsPanel = new AssetsPanel(*this); ENSURE(assetsPanel != NULL); - viewerPanel = new ViewerPanel(project); + viewerPanel = new ViewerPanel(*this); ENSURE(viewerPanel != NULL); - timelinePanel = new TimelinePanel(project); + timelinePanel = new TimelinePanel(*this); ENSURE(timelinePanel != NULL); //----- Create the Dock -----// diff --git a/src/gui/workspace/workspace-window.hpp b/src/gui/workspace/workspace-window.hpp index 91c16d642..fb0254d2d 100644 --- a/src/gui/workspace/workspace-window.hpp +++ b/src/gui/workspace/workspace-window.hpp @@ -46,6 +46,10 @@ namespace model { class Project; } // model +namespace controller { + class Controller; +} // model + namespace workspace { /** @@ -54,18 +58,25 @@ namespace workspace { class WorkspaceWindow : public Gtk::Window { public: - WorkspaceWindow(gui::model::Project *source_project); + WorkspaceWindow(gui::model::Project &source_project, + gui::controller::Controller &source_controller); ~WorkspaceWindow(); - gui::model::Project* get_project() const; + gui::model::Project& get_project(); + + gui::controller::Controller& get_controller(); private: void create_ui(); - + /* ===== Model ===== */ private: - gui::model::Project *project; + gui::model::Project &project; + + /* ===== Controller ===== */ +private: + gui::controller::Controller &controller; /* ===== UI ===== */ private: diff --git a/wiki/compatibility.html b/wiki/compatibility.html index 56353ac46..a014734fe 100644 --- a/wiki/compatibility.html +++ b/wiki/compatibility.html @@ -747,7 +747,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS } //}}} -
+
! Programming Languages
 * C
 ** a C99 compatible compiler, some GCC extensions are used, most are optional.
@@ -787,7 +787,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
 ** libcairomm-1.0-dev (>=0.6.0)
 ** libgdl-1-dev (>=0.6.1)
 *** libbonoboui2-dev (>=2.14.0)
-** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
+** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16) and gthread-2.0 (>=2.12.4)
 ** libxv-dev ~~(1.0.2 is known to work)~~
 
 
diff --git a/wiki/index.html b/wiki/index.html
index af19d10e4..fc7632387 100644
--- a/wiki/index.html
+++ b/wiki/index.html
@@ -780,7 +780,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
 }
 //}}}
-
+
for __Building__
 * gcc (4.1), glibc6 (2.3), libstdc++6 (4.1)
 * [[build system|BuildSystem]] dependencies: SCons (0.96.90), Python (2.4), pkg-config
@@ -799,7 +799,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
 ** libcairomm-1.0-dev (>=0.6.0)
 ** libgdl-1-dev (>=0.6.1)
 *** libbonoboui2-dev (>=2.14.0)
-** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
+** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16) and gthread-2.0 (>=2.12.4)
 ** libxv-dev (>=1.0.2)
 //usually, newer versions are OK//