From 071e332f042e58fae1b9d4b00c4facefc346a40c Mon Sep 17 00:00:00 2001 From: Joel Holdsworth Date: Sat, 16 Aug 2008 20:02:46 +0100 Subject: [PATCH 01/11] Added a --- src/gui/workspace/actions.cpp | 21 ++++--- src/gui/workspace/workspace-window.cpp | 84 +++++++++++++++----------- src/gui/workspace/workspace-window.hpp | 17 ++++-- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/src/gui/workspace/actions.cpp b/src/gui/workspace/actions.cpp index db8528b46..7dffaad28 100644 --- a/src/gui/workspace/actions.cpp +++ b/src/gui/workspace/actions.cpp @@ -95,14 +95,17 @@ Actions::Actions(WorkspaceWindow &workspace_window) : void Actions::update_action_state() { - REQUIRE(workspaceWindow.assets_panel != NULL); - REQUIRE(workspaceWindow.timeline_panel != NULL); - REQUIRE(workspaceWindow.viewer_panel != NULL); + REQUIRE(workspaceWindow.assetsPanel != NULL); + REQUIRE(workspaceWindow.timelinePanel != NULL); + REQUIRE(workspaceWindow.viewerPanel != NULL); is_updating_action_state = true; - assetsPanelAction->set_active(workspaceWindow.assets_panel->is_shown()); - timelinePanelAction->set_active(workspaceWindow.timeline_panel->is_shown()); - viewerPanelAction->set_active(workspaceWindow.viewer_panel->is_shown()); + assetsPanelAction->set_active( + workspaceWindow.assetsPanel->is_shown()); + timelinePanelAction->set_active( + workspaceWindow.timelinePanel->is_shown()); + viewerPanelAction->set_active( + workspaceWindow.viewerPanel->is_shown()); is_updating_action_state = false; } @@ -148,21 +151,21 @@ void Actions::on_menu_view_assets() { if(!is_updating_action_state) - workspaceWindow.assets_panel->show(assetsPanelAction->get_active()); + workspaceWindow.assetsPanel->show(assetsPanelAction->get_active()); } void Actions::on_menu_view_timeline() { if(!is_updating_action_state) - workspaceWindow.timeline_panel->show(timelinePanelAction->get_active()); + workspaceWindow.timelinePanel->show(timelinePanelAction->get_active()); } void Actions::on_menu_view_viewer() { if(!is_updating_action_state) - workspaceWindow.viewer_panel->show(viewerPanelAction->get_active()); + workspaceWindow.viewerPanel->show(viewerPanelAction->get_active()); } void diff --git a/src/gui/workspace/workspace-window.cpp b/src/gui/workspace/workspace-window.cpp index 027677bbb..4b82c8d5c 100644 --- a/src/gui/workspace/workspace-window.cpp +++ b/src/gui/workspace/workspace-window.cpp @@ -49,9 +49,9 @@ WorkspaceWindow::WorkspaceWindow(Project *source_project) : REQUIRE(source_project != NULL); layout = NULL; - assets_panel = NULL; - viewer_panel = NULL; - timeline_panel = NULL; + assetsPanel = NULL; + viewerPanel = NULL; + timelinePanel = NULL; create_ui(); } @@ -61,12 +61,12 @@ WorkspaceWindow::~WorkspaceWindow() REQUIRE(layout != NULL); g_object_unref(layout); - REQUIRE(assets_panel != NULL); - assets_panel->unreference(); - REQUIRE(viewer_panel != NULL); - viewer_panel->unreference(); - REQUIRE(timeline_panel != NULL); - timeline_panel->unreference(); + REQUIRE(assetsPanel != NULL); + assetsPanel->unreference(); + REQUIRE(viewerPanel != NULL); + viewerPanel->unreference(); + REQUIRE(timelinePanel != NULL); + timelinePanel->unreference(); } void @@ -78,7 +78,7 @@ WorkspaceWindow::create_ui() //----- Set up the UI Manager -----// // The UI will be nested within a VBox - add(base_container); + add(baseContainer); uiManager = Gtk::UIManager::create(); uiManager->insert_action_group(actions.actionGroup); @@ -131,21 +131,22 @@ WorkspaceWindow::create_ui() //----- Set up the Menu Bar -----// Gtk::Widget* menu_bar = uiManager->get_widget("/MenuBar"); ASSERT(menu_bar != NULL); - base_container.pack_start(*menu_bar, Gtk::PACK_SHRINK); + baseContainer.pack_start(*menu_bar, Gtk::PACK_SHRINK); //----- Set up the Tool Bar -----// - Gtk::Toolbar* toolbar = dynamic_cast(uiManager->get_widget("/ToolBar")); + Gtk::Toolbar* toolbar = dynamic_cast( + uiManager->get_widget("/ToolBar")); ASSERT(toolbar != NULL); toolbar->set_toolbar_style(TOOLBAR_ICONS); - base_container.pack_start(*toolbar, Gtk::PACK_SHRINK); + baseContainer.pack_start(*toolbar, Gtk::PACK_SHRINK); //----- Create the Panels -----// - assets_panel = new AssetsPanel(); - ENSURE(assets_panel != NULL); - viewer_panel = new ViewerPanel(); - ENSURE(viewer_panel != NULL); - timeline_panel = new TimelinePanel(); - ENSURE(timeline_panel != NULL); + assetsPanel = new AssetsPanel(); + ENSURE(assetsPanel != NULL); + viewerPanel = new ViewerPanel(); + ENSURE(viewerPanel != NULL); + timelinePanel = new TimelinePanel(); + ENSURE(timelinePanel != NULL); //----- Create the Dock -----// dock = Glib::wrap(gdl_dock_new()); @@ -154,30 +155,41 @@ WorkspaceWindow::create_ui() dockbar = Glib::wrap(gdl_dock_bar_new ((GdlDock*)dock->gobj())); - dock_container.pack_start(*dockbar, PACK_SHRINK); - dock_container.pack_end(*dock, PACK_EXPAND_WIDGET); - base_container.pack_start(dock_container, PACK_EXPAND_WIDGET); + dockContainer.pack_start(*dockbar, PACK_SHRINK); + dockContainer.pack_end(*dock, PACK_EXPAND_WIDGET); + baseContainer.pack_start(dockContainer, PACK_EXPAND_WIDGET); - gdl_dock_add_item ((GdlDock*)dock->gobj(), assets_panel->get_dock_item(), GDL_DOCK_LEFT); - gdl_dock_add_item ((GdlDock*)dock->gobj(), viewer_panel->get_dock_item(), GDL_DOCK_RIGHT); - gdl_dock_add_item ((GdlDock*)dock->gobj(), timeline_panel->get_dock_item(), GDL_DOCK_BOTTOM); + gdl_dock_add_item ((GdlDock*)dock->gobj(), + assetsPanel->get_dock_item(), GDL_DOCK_LEFT); + gdl_dock_add_item ((GdlDock*)dock->gobj(), + viewerPanel->get_dock_item(), GDL_DOCK_RIGHT); + gdl_dock_add_item ((GdlDock*)dock->gobj(), + timelinePanel->get_dock_item(), GDL_DOCK_BOTTOM); // Manually dock and move around some of the items - gdl_dock_item_dock_to (timeline_panel->get_dock_item(), - assets_panel->get_dock_item(), GDL_DOCK_BOTTOM, -1); - gdl_dock_item_dock_to (viewer_panel->get_dock_item(), - assets_panel->get_dock_item(), GDL_DOCK_RIGHT, -1); - - show_all_children(); - + gdl_dock_item_dock_to (timelinePanel->get_dock_item(), + assetsPanel->get_dock_item(), GDL_DOCK_BOTTOM, -1); + gdl_dock_item_dock_to (viewerPanel->get_dock_item(), + assetsPanel->get_dock_item(), GDL_DOCK_RIGHT, -1); + gchar ph1[] = "ph1"; - gdl_dock_placeholder_new (ph1, (GdlDockObject*)dock->gobj(), GDL_DOCK_TOP, FALSE); + gdl_dock_placeholder_new (ph1, (GdlDockObject*)dock->gobj(), + GDL_DOCK_TOP, FALSE); gchar ph2[] = "ph2"; - gdl_dock_placeholder_new (ph2, (GdlDockObject*)dock->gobj(), GDL_DOCK_BOTTOM, FALSE); + gdl_dock_placeholder_new (ph2, (GdlDockObject*)dock->gobj(), + GDL_DOCK_BOTTOM, FALSE); gchar ph3[] = "ph3"; - gdl_dock_placeholder_new (ph3, (GdlDockObject*)dock->gobj(), GDL_DOCK_LEFT, FALSE); + gdl_dock_placeholder_new (ph3, (GdlDockObject*)dock->gobj(), + GDL_DOCK_LEFT, FALSE); gchar ph4[] = "ph4"; - gdl_dock_placeholder_new (ph4, (GdlDockObject*)dock->gobj(), GDL_DOCK_RIGHT, FALSE); + gdl_dock_placeholder_new (ph4, (GdlDockObject*)dock->gobj(), + GDL_DOCK_RIGHT, FALSE); + + //----- Create the status bar -----// + statusBar.set_has_resize_grip(); + baseContainer.pack_start(statusBar, PACK_SHRINK); + + show_all_children(); } } // namespace workspace diff --git a/src/gui/workspace/workspace-window.hpp b/src/gui/workspace/workspace-window.hpp index 86f56a2bf..3f3b60de1 100644 --- a/src/gui/workspace/workspace-window.hpp +++ b/src/gui/workspace/workspace-window.hpp @@ -56,7 +56,8 @@ class WorkspaceWindow : public Gtk::Window { public: WorkspaceWindow(lumiera::gui::model::Project *source_project); - virtual ~WorkspaceWindow(); + + ~WorkspaceWindow(); private: void create_ui(); @@ -68,18 +69,22 @@ private: /* ===== UI ===== */ private: Glib::RefPtr uiManager; - Gtk::VBox base_container; - Gtk::HBox dock_container; + Gtk::VBox baseContainer; + Gtk::HBox dockContainer; + //----- Dock Frame -----// Gtk::Widget *dock; Gtk::Widget *dockbar; GdlDockLayout *layout; + + //----- Status Bar -----// + Gtk::Statusbar statusBar; /* ===== Panels ===== */ private: - AssetsPanel *assets_panel; - ViewerPanel *viewer_panel; - TimelinePanel *timeline_panel; + AssetsPanel *assetsPanel; + ViewerPanel *viewerPanel; + TimelinePanel *timelinePanel; /* ===== Helpers ===== */ private: From 574bb5a2fd0cd54bb2456deaeb2bd06f2241e8b3 Mon Sep 17 00:00:00 2001 From: Joel Holdsworth Date: Sat, 16 Aug 2008 20:04:05 +0100 Subject: [PATCH 02/11] Added some documentation --- src/gui/gtk-lumiera.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index 41b355d80..d3c59d7ba 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -114,6 +114,11 @@ GtkLumiera& application(); */ namespace dialogs {} +/** + * The namespace of data model classes. + */ +namespace model {} + /** * The namespace of all video output implementations. */ From 4e5e298bbb69dc41473c2e39e10cc6bf88574a02 Mon Sep 17 00:00:00 2001 From: Joel Holdsworth Date: Sat, 16 Aug 2008 22:06:46 +0100 Subject: [PATCH 03/11] Added a time indicator widget --- src/gui/panels/timeline-panel.cpp | 21 +++++++++++++++++++++ src/gui/panels/timeline-panel.hpp | 10 ++++++++++ src/gui/widgets/timeline-widget.cpp | 7 +++++++ src/gui/widgets/timeline-widget.hpp | 6 ++++++ 4 files changed, 44 insertions(+) diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 00a3c3f0f..fb0bace5b 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -23,6 +23,10 @@ #include "../gtk-lumiera.hpp" #include "timeline-panel.hpp" +extern "C" { +#include "../../lib/time.h" +} + using namespace Gtk; using namespace sigc; using namespace lumiera::gui::widgets; @@ -39,8 +43,13 @@ TimelinePanel::TimelinePanel() : iBeamTool(Gtk::StockID("tool_i_beam")), zoomIn(Stock::ZOOM_IN), zoomOut(Stock::ZOOM_OUT), + timeIndicator(), updatingToolbar(false) { + // Setup the widget + timelineWidget.mouse_hover_signal().connect( + mem_fun(this, &TimelinePanel::on_mouse_hover)); + // Setup the toolbar toolbar.append(arrowTool, mem_fun(this, &TimelinePanel::on_arrow_tool)); @@ -50,6 +59,12 @@ TimelinePanel::TimelinePanel() : toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in)); toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out)); + seperator2.set_expand(true); + toolbar.append(seperator2); + + timeIndicatorButton.set_label_widget(timeIndicator); + toolbar.append(timeIndicatorButton); + toolbar.set_icon_size(IconSize(ICON_SIZE_LARGE_TOOLBAR)); toolbar.set_toolbar_style(TOOLBAR_ICONS); @@ -92,6 +107,12 @@ TimelinePanel::on_zoom_out() update_zoom_buttons(); } +void +TimelinePanel::on_mouse_hover(gavl_time_t time) +{ + timeIndicator.set_text(lumiera_tmpbuf_print_time(time)); +} + void TimelinePanel::update_tool_buttons() { diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index 37491c0d9..e2609bf20 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -55,6 +55,10 @@ private: void on_zoom_in(); void on_zoom_out(); + void on_time_pressed(); + + void on_mouse_hover(gavl_time_t time); + private: void update_tool_buttons(); void update_zoom_buttons(); @@ -65,6 +69,7 @@ private: // Widgets Gtk::Toolbar toolbar; + Gtk::HBox toolStrip; TimelineWidget timelineWidget; // Toolbar Widgets @@ -76,6 +81,11 @@ private: Gtk::ToolButton zoomIn; Gtk::ToolButton zoomOut; + Gtk::SeparatorToolItem seperator2; + + Gtk::Label timeIndicator; + Gtk::ToolButton timeIndicatorButton; + // Internals bool updatingToolbar; diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index fd22ab3f9..e1931ab5b 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -215,6 +215,12 @@ TimelineWidget::set_tool(ToolType tool_type) body->set_tool(tool_type); } +sigc::signal +TimelineWidget::mouse_hover_signal() const +{ + return mouseHoverSignal; +} + void TimelineWidget::on_scroll() { @@ -309,6 +315,7 @@ TimelineWidget::on_motion_in_body_notify_event(GdkEventMotion *event) { REQUIRE(event != NULL); ruler->set_mouse_chevron_offset(event->x); + mouseHoverSignal.emit(x_to_time(event->x)); return true; } diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index 76e4a16d7..aba15cc04 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -132,6 +132,10 @@ public: * Sets the type of the tool currently active. */ void set_tool(timeline::ToolType tool_type); + +public: + /* ===== Signals ===== */ + sigc::signal mouse_hover_signal() const; /* ===== Events ===== */ protected: @@ -179,6 +183,8 @@ protected: Gtk::Adjustment horizontalAdjustment, verticalAdjustment; Gtk::HScrollbar horizontalScroll; Gtk::VScrollbar verticalScroll; + + sigc::signal mouseHoverSignal; /* ===== Constants ===== */ public: From 6d5cf0e6424b2e9e428f211b15cafc491500abca Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 28 Aug 2008 02:57:12 +0200 Subject: [PATCH 04/11] scons: use Doxygen builder (lib implementation from http://www.scons.org/wiki/DoxygenBuilder) --- SConstruct | 15 +-- admin/scons/BuilderDoxygen.py | 230 ++++++++++++++++++++++++++++++++++ admin/scons/BuilderGCH.py | 3 +- doc/devel/.gitignore | 1 + doc/devel/Doxyfile | 10 +- 5 files changed, 241 insertions(+), 18 deletions(-) create mode 100644 admin/scons/BuilderDoxygen.py diff --git a/SConstruct b/SConstruct index 93c3e39fd..d0a143db5 100644 --- a/SConstruct +++ b/SConstruct @@ -61,7 +61,7 @@ def setupBasicEnvironment(): opts = defineCmdlineOptions() env = LumieraEnvironment(options=opts ,toolpath = [TOOLDIR] - ,tools = ["default", "BuilderGCH"] + ,tools = ["default", "BuilderGCH", "BuilderDoxygen"] ) env.Append ( CCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honor CFLAGS, only CCFLAGS @@ -340,16 +340,9 @@ def definePostBuildTargets(env, artifacts): env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) env.Clean ('build', [ '$SRCDIR/pre.gch' ]) - # Doxygen documentation - # Note: at the moment we only depend on Doxyfile - # obviousely, we should depend on all sourcefiles - # real Doxygen builder for scons is under developement for 0.97 - # so for the moment I prefere not to bother - doxyfile = File('doc/devel/Doxyfile') - env.NoClean(doxyfile) - doxydoc = artifacts['doxydoc'] = [ Dir('doc/devel/html'), Dir('doc/devel/latex') ] - env.Command(doxydoc, doxyfile, "doxygen Doxyfile 2>&1 |tee ,doxylog", chdir='doc/devel') - env.Clean ('doc/devel', doxydoc + ['doc/devel/,doxylog']) + doxydoc = artifacts['doxydoc'] = env.Doxygen('doc/devel/Doxyfile') + env.Alias ('doxydoc', doxydoc) + env.Clean ('doxydoc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) def defineInstallTargets(env, artifacts): diff --git a/admin/scons/BuilderDoxygen.py b/admin/scons/BuilderDoxygen.py new file mode 100644 index 000000000..943fce946 --- /dev/null +++ b/admin/scons/BuilderDoxygen.py @@ -0,0 +1,230 @@ +# -*- python -*- +## +## BuilderDoxygen.py - SCons builder for generating Doxygen documentation +## + +# +# Astxx, the Asterisk C++ API and Utility Library. +# Copyright (C) 2005, 2006 Matthew A. Nicholson +# Copyright (C) 2006 Tim Blechmann +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License version 2.1 as published by the Free Software Foundation. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, see http://www.gnu.org/licenses/ +##################################################################### + +# history: 8/2008 adapted for Lumiera build system +# added patch for Doxyfile in subdirectory +# see http://www.scons.org/wiki/DoxygenBuilder + + +import os +import os.path +import glob +from fnmatch import fnmatch + + +def DoxyfileParse(file_contents): + """ + Parse a Doxygen source file and return a dictionary of all the values. + Values will be strings and lists of strings. + """ + data = {} + + import shlex + lex = shlex.shlex(instream = file_contents, posix = True) + lex.wordchars += "*+./-:" + lex.whitespace = lex.whitespace.replace("\n", "") + lex.escape = "" + + lineno = lex.lineno + token = lex.get_token() + key = token # the first token should be a key + last_token = "" + key_token = False + next_key = False + new_data = True + + def append_data(data, key, new_data, token): + if new_data or len(data[key]) == 0: + data[key].append(token) + else: + data[key][-1] += token + + while token: + if token in ['\n']: + if last_token not in ['\\']: + key_token = True + elif token in ['\\']: + pass + elif key_token: + key = token + key_token = False + else: + if token == "+=": + if not data.has_key(key): + data[key] = list() + elif token == "=": + data[key] = list() + else: + append_data( data, key, new_data, token ) + new_data = True + + last_token = token + token = lex.get_token() + + if last_token == '\\' and token != '\n': + new_data = False + append_data( data, key, new_data, '\\' ) + + # compress lists of len 1 into single strings + for (k, v) in data.items(): + if len(v) == 0: + data.pop(k) + + # items in the following list will be kept as lists and not converted to strings + if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS"]: + continue + + if len(v) == 1: + data[k] = v[0] + + return data + +def DoxySourceScan(node, env, path): + """ + Doxygen Doxyfile source scanner. This should scan the Doxygen file and add + any files used to generate docs to the list of source files. + """ + default_file_patterns = [ + '*.c', '*.cc', '*.cxx', '*.cpp', '*.c++', '*.java', '*.ii', '*.ixx', + '*.ipp', '*.i++', '*.inl', '*.h', '*.hh ', '*.hxx', '*.hpp', '*.h++', + '*.idl', '*.odl', '*.cs', '*.php', '*.php3', '*.inc', '*.m', '*.mm', + '*.py', + ] + + default_exclude_patterns = [ + '*~', + ] + + sources = [] + + data = DoxyfileParse(node.get_contents()) + + if data.get("RECURSIVE", "NO") == "YES": + recursive = True + else: + recursive = False + + file_patterns = data.get("FILE_PATTERNS", default_file_patterns) + exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns) + + # + # We're running in the top-level directory, but the doxygen + # configuration file is in the same directory as node; this means + # that relative pathnames in node must be adjusted before they can + # go onto the sources list + conf_dir = os.path.dirname(str(node)) + + for node in data.get("INPUT", []): + if not os.path.isabs(node): + node = os.path.join(conf_dir, node) + if os.path.isfile(node): + sources.append(node) + elif os.path.isdir(node): + if recursive: + for root, dirs, files in os.walk(node): + for f in files: + filename = os.path.join(root, f) + + pattern_check = reduce(lambda x, y: x or bool(fnmatch(filename, y)), file_patterns, False) + exclude_check = reduce(lambda x, y: x and fnmatch(filename, y), exclude_patterns, True) + + if pattern_check and not exclude_check: + sources.append(filename) + else: + for pattern in file_patterns: + sources.extend(glob.glob("/".join([node, pattern]))) + + sources = map( lambda path: env.File(path), sources ) + return sources + + +def DoxySourceScanCheck(node, env): + """Check if we should scan this file""" + return os.path.isfile(node.path) + +def DoxyEmitter(source, target, env): + """Doxygen Doxyfile emitter""" + # possible output formats and their default values and output locations + output_formats = { + "HTML": ("YES", "html"), + "LATEX": ("YES", "latex"), + "RTF": ("NO", "rtf"), + "MAN": ("NO", "man"), + "XML": ("NO", "xml"), + } + + data = DoxyfileParse(source[0].get_contents()) + + targets = [] + out_dir = data.get("OUTPUT_DIRECTORY", ".") + + # add our output locations + for (k, v) in output_formats.items(): + if data.get("GENERATE_" + k, v[0]) == "YES": + targets.append(env.Dir( os.path.join(out_dir, data.get(k + "_OUTPUT", v[1]))) ) + + # don't clobber targets + for node in targets: + env.Precious(node) + + # set up cleaning stuff + for node in targets: + env.Clean(node, node) + + return (targets, source) + + +def generate(env): + """ + Add builders and construction variables for the + Doxygen tool. This is currently for Doxygen 1.4.6. + """ + doxyfile_scanner = env.Scanner( + DoxySourceScan, + "DoxySourceScan", + scan_check = DoxySourceScanCheck, + ) + + import SCons.Builder + doxyfile_builder = SCons.Builder.Builder( + action = "cd ${SOURCE.dir} && (${DOXYGEN} ${SOURCE.file} 2>&1 |tee ,doxylog)", + emitter = DoxyEmitter, + target_factory = env.fs.Entry, + single_source = True, + source_scanner = doxyfile_scanner, + ) + + env.Append(BUILDERS = { + 'Doxygen': doxyfile_builder, + }) + + env.AppendUnique( + DOXYGEN = 'doxygen', + ) + +def exists(env): + """ + Make sure doxygen exists. + """ + return env.Detect("doxygen") + diff --git a/admin/scons/BuilderGCH.py b/admin/scons/BuilderGCH.py index a8e0eb95c..c522cb9cd 100644 --- a/admin/scons/BuilderGCH.py +++ b/admin/scons/BuilderGCH.py @@ -18,8 +18,7 @@ # 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. +# along with this program; if not, see http://www.gnu.org/licenses/ ##################################################################### # history: 8/2008 adapted for Lumiera build system diff --git a/doc/devel/.gitignore b/doc/devel/.gitignore index 96886436c..d6d649abd 100644 --- a/doc/devel/.gitignore +++ b/doc/devel/.gitignore @@ -1,2 +1,3 @@ ,doxylog +warnings.txt html/* diff --git a/doc/devel/Doxyfile b/doc/devel/Doxyfile index 28bab1f05..b7c59fa54 100644 --- a/doc/devel/Doxyfile +++ b/doc/devel/Doxyfile @@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Lumiera -PROJECT_NUMBER = 3.0+alpha +PROJECT_NUMBER = 0.1+pre OUTPUT_DIRECTORY = CREATE_SUBDIRS = YES OUTPUT_LANGUAGE = English @@ -32,7 +32,7 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO -DETAILS_AT_TOP = NO +DETAILS_AT_TOP = YES INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 @@ -65,8 +65,8 @@ CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES -SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = YES +SORT_MEMBER_DOCS = NO +SORT_BRIEF_DOCS = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES @@ -179,7 +179,7 @@ HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO GENERATE_DOCSET = NO -DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_FEEDNAME = "Lumiera Doxygen docs" DOCSET_BUNDLE_ID = org.doxygen.Project HTML_DYNAMIC_SECTIONS = NO CHM_FILE = From 699966c8207b8d43776a8d7ac948d2c7061fdb44 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 28 Aug 2008 03:52:43 +0200 Subject: [PATCH 05/11] add my favorite lumiera logo to the TiddlyWiki page --- wiki/draw/LumiLogo.png | Bin 0 -> 2345 bytes wiki/index.html | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 wiki/draw/LumiLogo.png diff --git a/wiki/draw/LumiLogo.png b/wiki/draw/LumiLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..1fd263aed1c9fc761b34bd79b337eaffcee5b004 GIT binary patch literal 2345 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 -
-
This is the entry point to several [[TiddlyWiki]]-Pages containing the developer and design documentation for Lumiera.
+
+
[<img[draw/LumiLogo.png]]
 
-* Cehteh started GitNotes where we will collect some information about git, howto and special setups
-* since we prefer gpg signed tags in git and not each developer knows gpg well here is a Micro-GPG-HowTo 
+
+  ''Lumiera'' is the emerging professional non linear video editor for Linux
+
+This is the entry point to several [[TiddlyWiki]]-Pages containing the developer and design documentation.
 * we maintain (semi-) final design docs in DesignDocumentation
 * Things get often worked out on IRC, see IRC-Transcripts for decisions made there and not yet put into the proper documentation places
 
-Please __end your tiddlers in a newline__, this makes merging in git easier since the /pre tag used in tiddlywiki will become on a single line.
-
 ----
 !Architecture and Subsystems
 to get started, we create design drafts emphasizing different aspects and regions of Lumiera

From fc6cbcf8e4e04189a3fc52296392f41667bc12bd Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 28 Aug 2008 03:53:46 +0200
Subject: [PATCH 06/11] improved the scons build to report all missing
 dependencies instead of stopping at the first one

---
 SConstruct | 60 +++++++++++++++++++++++++-----------------------------
 1 file changed, 28 insertions(+), 32 deletions(-)

diff --git a/SConstruct b/SConstruct
index d0a143db5..2b3e2cfd0 100644
--- a/SConstruct
+++ b/SConstruct
@@ -173,24 +173,21 @@ def configurePlatform(env):
     # run all configuration checks in the given env
     
     # Perform checks for prerequisites --------------------------------------------
+    problems = []
     if not conf.TryAction('pkg-config --version > $TARGET')[0]:
-        print 'We need pkg-config for including library configurations, exiting.'
-        Exit(1)
+        problems.append('We need pkg-config for including library configurations, exiting.')
     
     if not conf.CheckLibWithHeader('m', 'math.h','C'):
-        print 'Did not find math.h / libm, exiting.'
-        Exit(1)
+        problems.append('Did not find math.h / libm.')
     
     if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'):
-        print 'Functions for runtime dynamic loading not available, exiting.'
-        Exit(1)
+        problems.append('Functions for runtime dynamic loading not available.')
     
     if not conf.CheckLibWithHeader('nobugmt', 'nobug.h', 'C'):
-        print 'Did not find NoBug [http://www.pipapo.org/pipawiki/NoBug], exiting.'
-        Exit(1)
+        problems.append('Did not find NoBug [http://www.pipapo.org/pipawiki/NoBug].')
     
     if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'):
-        print 'Did not find the pthread lib or pthread.h, exiting.'
+        problems.append('Did not find the pthread lib or pthread.h.')
     else:
        conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD')
        conf.env.Append(CCFLAGS = ' -pthread')
@@ -202,57 +199,56 @@ def configurePlatform(env):
         conf.env.Append(CPPFLAGS = ' -DHAS_VALGRIND_VALGIND_H')
     
     if not conf.CheckCXXHeader('tr1/memory'):
-        print 'We rely on the std::tr1 proposed standard extension for shared_ptr.'
-        Exit(1) 
+        problems.append('We rely on the std::tr1 proposed standard extension for shared_ptr.')
     
     if not conf.CheckCXXHeader('boost/config.hpp'):
-        print 'We need the C++ boost-lib.'
-        Exit(1)
+        problems.append('We need the C++ boost-lib.')
     else:
         if not conf.CheckCXXHeader('boost/shared_ptr.hpp'):
-            print 'We need boost::shared_ptr (shared_ptr.hpp).'
-            Exit(1)
+            problems.append('We need boost::shared_ptr (shared_ptr.hpp).')
         if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'):
-            print 'We need boost::program_options (including binary lib for linking).'
-            Exit(1)
+            problems.append('We need boost::program_options (including binary lib for linking).')
         if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'):
-            print 'We need the boost regular expression lib (incl. binary lib for linking).'
-            Exit(1)
+            problems.append('We need the boost regular expression lib (incl. binary lib for linking).')
     
 #    if not conf.CheckLibWithHeader('gavl', ['gavlconfig.h', 'gavl/gavl.h'], 'C'):
     
     if not conf.CheckPkgConfig('gavl', 1.0):
-        print 'Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html], exiting.'
-        Exit(1)
+        problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].')
     else:
         conf.env.mergeConf('gavl')
     
     if not conf.CheckPkgConfig('gtkmm-2.4', 2.8):
-        print 'Unable to configure GTK--, exiting.'
-        Exit(1)
+        problems.append('Unable to configure GTK--, exiting.')
         
     if not conf.CheckPkgConfig('glibmm-2.4', '2.16'):
-        print 'Unable to configure Lib glib--, exiting.'
-        Exit(1)
+        problems.append('Unable to configure Lib glib--, exiting.')
     
     if not conf.CheckPkgConfig('cairomm-1.0', 0.6):
-        print 'Unable to configure Cairo--, exiting.'
-        Exit(1)
+        problems.append('Unable to configure Cairo--, exiting.')
     
     if not conf.CheckPkgConfig('gdl-1.0', '0.6.1'):
-        print 'Unable to configure the GNOME DevTool Library, exiting.'
-        Exit(1)
+        problems.append('Unable to configure the GNOME DevTool Library, exiting.')
     
     if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'):
-        print 'Need rsvg Library for rendering icons.'
-        Exit(1)
+        problems.append('Need rsvg Library for rendering icons.')
         
-    if not conf.CheckPkgConfig('xv'): Exit(1)
+    if not conf.CheckPkgConfig('xv'): problems.append('Need lib xv')
 #   if not conf.CheckPkgConfig('xext'): Exit(1)
 #   if not conf.CheckPkgConfig('sm'): Exit(1)
 #    
 # obviously not needed?
     
+    
+    # report missing dependencies
+    if problems:
+        print "*** unable to build due to the following problems:"
+        for isue in problems:
+            print " *  %s" % isue
+        print
+        print "build aborted."
+        Exit(1)
+    
     print "** Gathered Library Info: %s" % conf.env.libInfo.keys()
     
     

From 885704f0f8db293be73420bc4193a8ac82783695 Mon Sep 17 00:00:00 2001
From: Joel Holdsworth 
Date: Sat, 30 Aug 2008 14:00:47 +0100
Subject: [PATCH 07/11] Added time indication

---
 src/gui/panels/timeline-panel.cpp | 23 +++++++++++++++--------
 src/gui/panels/timeline-panel.hpp |  2 ++
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp
index fb0bace5b..340579ed5 100644
--- a/src/gui/panels/timeline-panel.cpp
+++ b/src/gui/panels/timeline-panel.cpp
@@ -51,20 +51,19 @@ TimelinePanel::TimelinePanel() :
     mem_fun(this, &TimelinePanel::on_mouse_hover));
   
   // Setup the toolbar
+  timeIndicatorButton.set_label_widget(timeIndicator);
+  toolbar.append(timeIndicatorButton);
+  
+  toolbar.append(seperator1);
+  
   toolbar.append(arrowTool, mem_fun(this,
     &TimelinePanel::on_arrow_tool));
   toolbar.append(iBeamTool, mem_fun(this,
     &TimelinePanel::on_ibeam_tool));
-  toolbar.append(seperator1);
+  toolbar.append(seperator2);
   toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in));
   toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out));
-  
-  seperator2.set_expand(true);
-  toolbar.append(seperator2);
 
-  timeIndicatorButton.set_label_widget(timeIndicator);
-  toolbar.append(timeIndicatorButton);
-  
   toolbar.set_icon_size(IconSize(ICON_SIZE_LARGE_TOOLBAR));
   toolbar.set_toolbar_style(TOOLBAR_ICONS);
   
@@ -72,8 +71,10 @@ TimelinePanel::TimelinePanel() :
   pack_start(toolbar, PACK_SHRINK);
   pack_start(timelineWidget, PACK_EXPAND_WIDGET);
   
+  // Set the initial UI state
   update_tool_buttons();
   update_zoom_buttons();
+  show_time(0);
 }
 
 void
@@ -110,7 +111,7 @@ TimelinePanel::on_zoom_out()
 void
 TimelinePanel::on_mouse_hover(gavl_time_t time)
 {
-  timeIndicator.set_text(lumiera_tmpbuf_print_time(time));
+  show_time(time);
 }
 
 void
@@ -134,6 +135,12 @@ TimelinePanel::update_zoom_buttons()
     TimelineWidget::MaxScale);
 }
 
+void
+TimelinePanel::show_time(gavl_time_t time)
+{
+  timeIndicator.set_text(lumiera_tmpbuf_print_time(time));
+}
+
 }   // namespace panels
 }   // namespace gui
 }   // namespace lumiera
diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp
index e2609bf20..b46218e87 100644
--- a/src/gui/panels/timeline-panel.hpp
+++ b/src/gui/panels/timeline-panel.hpp
@@ -62,6 +62,8 @@ private:
 private:
   void update_tool_buttons();
   void update_zoom_buttons();
+  
+  void show_time(gavl_time_t time);
 
 private:
 

From 49f87b28bbb0ae144a31e2eb644af5bd16f8517c Mon Sep 17 00:00:00 2001
From: Joel Holdsworth 
Date: Sat, 30 Aug 2008 22:34:26 +0100
Subject: [PATCH 08/11] Added support for audacity style playback period, and
 added some documentation

---
 src/gui/lumiera_ui.rc                         |  11 +-
 src/gui/widgets/timeline-widget.cpp           |  48 +++++-
 src/gui/widgets/timeline-widget.hpp           |  26 ++-
 src/gui/widgets/timeline/timeline-body.cpp    |  15 +-
 .../widgets/timeline/timeline-ibeam-tool.cpp  |   9 +-
 src/gui/widgets/timeline/timeline-ruler.cpp   | 151 +++++++++++++++++-
 src/gui/widgets/timeline/timeline-ruler.hpp   |  41 ++++-
 7 files changed, 278 insertions(+), 23 deletions(-)

diff --git a/src/gui/lumiera_ui.rc b/src/gui/lumiera_ui.rc
index b68407039..02f6d90fc 100644
--- a/src/gui/lumiera_ui.rc
+++ b/src/gui/lumiera_ui.rc
@@ -140,16 +140,15 @@ style "timeline_ruler" = "default_base"
   gtkmm__CustomObject_TimelineRuler::min_division_width = 100
   gtkmm__CustomObject_TimelineRuler::mouse_chevron_size = 5
   gtkmm__CustomObject_TimelineRuler::selection_chevron_size = 5
+  gtkmm__CustomObject_TimelineRuler::playback_arrow_colour = "#2D2D90"
+  gtkmm__CustomObject_TimelineRuler::playback_arrow_alpha = 0.5
+  gtkmm__CustomObject_TimelineRuler::playback_arrow_size = 10
+  gtkmm__CustomObject_TimelineRuler::playback_arrow_stem_size = 3
 }
 
 style "timeline_header_base" = "default_base"
 {
-#  fg[NORMAL] = { 0.77, 0.77, 0.72 }	
-#  bg[NORMAL] = { 0.18, 0.19, 0.22 }
-#  bg[ACTIVE] = { 0.20, 0.20, 0.20 }	
-#  bg[PRELIGHT] = { 0.20, 0.20, 0.20 }
-#  bg[INSENSITIVE] = { 0.20, 0.20, 0.20 }
-#  bg[SELECTED] = { 0.20, 0.20, 0.20 }
+
 }
 
 class "gtkmm__CustomObject_TimelineBody" style:highest "timeline_body"
diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp
index e1931ab5b..9345efe59 100644
--- a/src/gui/widgets/timeline-widget.cpp
+++ b/src/gui/widgets/timeline-widget.cpp
@@ -46,6 +46,8 @@ TimelineWidget::TimelineWidget() :
   verticalAdjustment(0, 0, 0),
   selectionStart(0),
   selectionEnd(0),
+  playbackPeriodStart(0),
+  playbackPeriodEnd(0),
   horizontalScroll(horizontalAdjustment),
   verticalScroll(verticalAdjustment)
 {
@@ -183,8 +185,12 @@ TimelineWidget::get_selection_end() const
 }
 
 void
-TimelineWidget::set_selection(gavl_time_t start, gavl_time_t end)
+TimelineWidget::set_selection(gavl_time_t start, gavl_time_t end,
+  bool reset_playback_period)
 {
+  REQUIRE(ruler != NULL);
+  REQUIRE(body != NULL);
+    
   if(start < end)
     {
       selectionStart = start;
@@ -196,6 +202,46 @@ TimelineWidget::set_selection(gavl_time_t start, gavl_time_t end)
       selectionStart = end;
       selectionEnd = start;
     }
+    
+  if(reset_playback_period)
+    {
+      playbackPeriodStart = selectionStart;
+      playbackPeriodEnd = selectionEnd;
+    }
+
+  ruler->queue_draw();
+  body->queue_draw();
+}
+
+gavl_time_t
+TimelineWidget::get_playback_period_start() const
+{
+  return playbackPeriodStart;
+}
+  
+gavl_time_t
+TimelineWidget::get_playback_period_end() const
+{
+  return playbackPeriodEnd;
+}
+  
+void
+TimelineWidget::set_playback_period(gavl_time_t start, gavl_time_t end)
+{
+  REQUIRE(ruler != NULL);
+  REQUIRE(body != NULL);
+  
+  if(start < end)
+    {
+      playbackPeriodStart = start;
+      playbackPeriodEnd = end;
+    }
+  else
+    {
+      // The period is back-to-front, flip it round
+      playbackPeriodStart = end;
+      playbackPeriodEnd = start;
+    }
 
   ruler->queue_draw();
   body->queue_draw();
diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp
index aba15cc04..004a01ca6 100644
--- a/src/gui/widgets/timeline-widget.hpp
+++ b/src/gui/widgets/timeline-widget.hpp
@@ -120,8 +120,30 @@ public:
   
   /**
    * Sets the period of the selection.
+   * @param start The start time.
+   * @param end The end time.
+   * @param reset_playback_period Specifies whether to set the playback
+   * period to the same as this new selection.
    */
-  void set_selection(gavl_time_t start, gavl_time_t end);
+  void set_selection(gavl_time_t start, gavl_time_t end,
+    bool reset_playback_period = true);
+  
+  /**
+   * Gets the time at which the playback period begins.
+   */
+  gavl_time_t get_playback_period_start() const;
+  
+  /**
+   * Gets the time at which the playback period ends.
+   */
+  gavl_time_t get_playback_period_end() const;
+  
+  /**
+   * Sets the playback period.
+   * @param start The start time.
+   * @param end The end time.
+   */
+  void set_playback_period(gavl_time_t start, gavl_time_t end);
   
   /**
    * Gets the type of the tool currently active.
@@ -169,6 +191,8 @@ protected:
   // Selection State
   gavl_time_t selectionStart;
   gavl_time_t selectionEnd;
+  gavl_time_t playbackPeriodStart;
+  gavl_time_t playbackPeriodEnd;
 
   int totalHeight;
 
diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp
index 64f545190..231193105 100644
--- a/src/gui/widgets/timeline/timeline-body.cpp
+++ b/src/gui/widgets/timeline/timeline-body.cpp
@@ -349,23 +349,18 @@ TimelineBody::register_styles() const
 {
   GtkWidgetClass *klass = GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS(gobj()));
 
-  gtk_widget_class_install_style_property(
-    GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS(gobj())), 
-    g_param_spec_boxed("background",
-      "Track Background",
+  gtk_widget_class_install_style_property(klass, 
+    g_param_spec_boxed("background", "Track Background",
       "The background colour of timeline tracks",
       GDK_TYPE_COLOR, G_PARAM_READABLE));
   
-  gtk_widget_class_install_style_property(
-    GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS(gobj())), 
-    g_param_spec_boxed("selection",
-      "End lines of a selection",
+  gtk_widget_class_install_style_property(klass, 
+    g_param_spec_boxed("selection", "End lines of a selection",
       "The colour of selection limit lines",
       GDK_TYPE_COLOR, G_PARAM_READABLE));
       
   gtk_widget_class_install_style_property(klass, 
-    g_param_spec_float("selection_alpha",
-    "Selection Alpha",
+    g_param_spec_float("selection_alpha", "Selection Alpha",
     "The transparency of the selection marque.",
     0, 1.0, 0.5, G_PARAM_READABLE));
 }
diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp
index 92a4376f8..fe3ce5d23 100644
--- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp
+++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp
@@ -90,6 +90,7 @@ IBeamTool::on_button_press_event(GdkEventButton* event)
   Tool::on_button_press_event(event);
   
   TimelineWidget *timeline_widget = get_timeline_widget();
+  REQUIRE(timeline_widget != NULL);
   
   if(event->button == 1)
     {
@@ -181,12 +182,16 @@ void
 IBeamTool::set_leading_x(const int x)
 {
   TimelineWidget *timeline_widget = get_timeline_widget();
+  REQUIRE(timeline_widget != NULL);
 
+  const bool set_playback_period = dragType == Selection;
   const gavl_time_t time = timeline_widget->x_to_time(x);
   if(time > pinnedDragTime)
-    timeline_widget->set_selection(pinnedDragTime, time);
+    timeline_widget->set_selection(
+      pinnedDragTime, time, set_playback_period);
   else
-    timeline_widget->set_selection(time, pinnedDragTime);
+    timeline_widget->set_selection(
+      time, pinnedDragTime, set_playback_period);
 }
 
 void
diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp
index 3f7d808b9..17a9a1445 100644
--- a/src/gui/widgets/timeline/timeline-ruler.cpp
+++ b/src/gui/widgets/timeline/timeline-ruler.cpp
@@ -45,6 +45,8 @@ namespace timeline {
 TimelineRuler::TimelineRuler(
   lumiera::gui::widgets::TimelineWidget *timeline_widget) :
   Glib::ObjectBase("TimelineRuler"),
+  isDragging(false),
+  pinnedDragTime(0),
   mouseChevronOffset(0),
   annotationHorzMargin(0),
   annotationVertMargin(0),
@@ -54,6 +56,9 @@ TimelineRuler::TimelineRuler(
   minDivisionWidth(100),
   mouseChevronSize(5),
   selectionChevronSize(5),
+  playbackArrowAlpha(0.5f),
+  playbackArrowSize(10),
+  playbackArrowStemSize(3),
   timelineWidget(timeline_widget)
 {
   REQUIRE(timelineWidget != NULL);
@@ -82,7 +87,10 @@ TimelineRuler::on_realize()
   Widget::on_realize();
   
   // Set event notifications
-  add_events(Gdk::POINTER_MOTION_MASK | Gdk::SCROLL_MASK);
+  add_events(Gdk::POINTER_MOTION_MASK |
+    Gdk::SCROLL_MASK |
+    Gdk::BUTTON_PRESS_MASK |
+    Gdk::BUTTON_RELEASE_MASK);
 
   // Load styles
   read_styles();
@@ -130,16 +138,47 @@ TimelineRuler::on_expose_event(GdkEventExpose* event)
   // Draw the overlays
   draw_mouse_chevron(cr, allocation);
   draw_selection(cr, allocation);
+  draw_playback_period(cr, allocation);
 
   return true;
 }
 
+
+bool
+TimelineRuler::on_button_press_event(GdkEventButton* event)
+{
+  REQUIRE(event != NULL);
+  
+  if(event->button == 1)
+  {
+    pinnedDragTime = timelineWidget->x_to_time(event->x);
+    isDragging = true;
+  }
+  
+  return true;
+}
+
+bool
+TimelineRuler::on_button_release_event(GdkEventButton* event)
+{
+  REQUIRE(event != NULL);
+  
+  if(event->button == 1)
+    isDragging = false;
+    
+  return true;
+}
+
 bool
 TimelineRuler::on_motion_notify_event(GdkEventMotion *event)
 {
   REQUIRE(event != NULL);
 
   set_mouse_chevron_offset(event->x);
+  
+  if(isDragging)
+    set_leading_x(event->x);
+  
   return true;
 }
 
@@ -162,6 +201,18 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation)
   rulerImage.clear(); // The widget has changed size - redraw
 }
 
+void
+TimelineRuler::set_leading_x(const int x)
+{
+  REQUIRE(timelineWidget != NULL);
+
+  const gavl_time_t time = timelineWidget->x_to_time(x);
+  if(time > pinnedDragTime)
+    timelineWidget->set_playback_period(pinnedDragTime, time);
+  else
+    timelineWidget->set_playback_period(time, pinnedDragTime);
+}
+
 void
 TimelineRuler::draw_ruler(Cairo::RefPtr cr,
     const Gdk::Rectangle ruler_rect)
@@ -299,6 +350,74 @@ TimelineRuler::draw_selection(Cairo::RefPtr cr,
     }
 }
 
+void
+TimelineRuler::draw_playback_period(Cairo::RefPtr cr,
+    const Gdk::Rectangle ruler_rect)
+{
+  REQUIRE(cr);
+  REQUIRE(ruler_rect.get_width() > 0);
+  REQUIRE(ruler_rect.get_height() > 0);
+  REQUIRE(timelineWidget != NULL);
+  
+  // Calculate coordinates
+  const float halfSize = playbackArrowSize / 2;
+  
+  const float a = timelineWidget->time_to_x(
+    timelineWidget->playbackPeriodStart) + 1 + 0.5f;
+  const float b = a + halfSize;
+  const float d = timelineWidget->time_to_x(
+    timelineWidget->playbackPeriodEnd) + 0.5f;
+  const float c = d - halfSize;
+  
+  const float e = ruler_rect.get_height() - playbackArrowSize - 0.5f;
+  const float f = e + (playbackArrowSize - playbackArrowStemSize) / 2;
+  const float g = ruler_rect.get_height() - playbackArrowSize / 2
+    - 0.5f;
+  const float i = ruler_rect.get_height() - 0.5f;
+  const float h = i - (playbackArrowSize - playbackArrowStemSize) / 2;
+
+  // Contruct the path
+  if(d - a >= playbackArrowSize)
+    {
+      // Draw an arrow: <===>
+      cr->move_to(a, g);
+      cr->line_to(b, e);
+      cr->line_to(b, f);
+      cr->line_to(c, f);
+      cr->line_to(c, e);
+      cr->line_to(d, g);
+      cr->line_to(c, i);
+      cr->line_to(c, h);  
+      cr->line_to(b, h);
+      cr->line_to(b, i);
+      cr->line_to(a, g);
+    }
+  else
+    { 
+      // The space is too narrow for an arrow, so draw calipers: > < 
+      cr->move_to(a, g);
+      cr->rel_line_to(-halfSize, -halfSize);
+      cr->rel_line_to(0, playbackArrowSize);
+      
+      cr->move_to(d, g);
+      cr->rel_line_to(halfSize, -halfSize);
+      cr->rel_line_to(0, playbackArrowSize);
+    }
+
+  // Fill
+  cr->set_source_rgba(
+    (float)playbackArrowColour.red / 0xFFFF,
+    (float)playbackArrowColour.green / 0xFFFF,
+    (float)playbackArrowColour.blue / 0xFFFF,
+    playbackArrowAlpha);
+  cr->fill_preserve();
+  
+  // Stroke
+  gdk_cairo_set_source_color(cr->cobj(), &playbackArrowColour);
+  cr->set_line_width(1);
+  cr->stroke();
+}
+
 gavl_time_t 
 TimelineRuler::calculate_major_spacing() const
 {
@@ -402,6 +521,29 @@ TimelineRuler::register_styles() const
     "Selection Chevron Size",
     "The height of the selection chevrons in pixels.",
     0, G_MAXINT, 5, G_PARAM_READABLE));
+    
+  gtk_widget_class_install_style_property(klass, 
+    g_param_spec_boxed("playback_arrow_colour",
+      "End lines of a selection",
+      "The colour of selection limit lines",
+      GDK_TYPE_COLOR, G_PARAM_READABLE));
+      
+  gtk_widget_class_install_style_property(klass, 
+    g_param_spec_float("playback_arrow_alpha", "Playback Arrow Alpha",
+    "The transparency of the playback period arrow.",
+    0, 1.0, 0.5, G_PARAM_READABLE));
+    
+  gtk_widget_class_install_style_property(klass, 
+    g_param_spec_int("playback_arrow_size",
+    "Playback Arrow Head Size",
+    "The height of the playback arrow head in pixels.",
+    0, G_MAXINT, 10, G_PARAM_READABLE));
+    
+  gtk_widget_class_install_style_property(klass, 
+    g_param_spec_int("playback_arrow_stem_size",
+    "Playback Arrow Stem Size",
+    "The height of the playback arrow head in pixels.",
+    0, G_MAXINT, 3, G_PARAM_READABLE));
 }
 
 void
@@ -415,6 +557,13 @@ TimelineRuler::read_styles()
   get_style_property("min_division_width", minDivisionWidth);
   get_style_property("mouse_chevron_size", mouseChevronSize);
   get_style_property("selection_chevron_size", selectionChevronSize);
+  
+  playbackArrowColour = WindowManager::read_style_colour_property(
+    *this, "playback_arrow_colour", 0, 0, 0);
+  get_style_property("playback_arrow_alpha", playbackArrowAlpha);
+  get_style_property("playback_arrow_size", playbackArrowSize);
+  get_style_property("playback_arrow_stem_size",
+    playbackArrowStemSize);
 }
   
 }   // namespace timeline
diff --git a/src/gui/widgets/timeline/timeline-ruler.hpp b/src/gui/widgets/timeline/timeline-ruler.hpp
index 236eddd95..9bd6651bb 100644
--- a/src/gui/widgets/timeline/timeline-ruler.hpp
+++ b/src/gui/widgets/timeline/timeline-ruler.hpp
@@ -59,14 +59,34 @@ protected:
 
   bool on_expose_event(GdkEventExpose *event);
   
+  /**
+   * The event handler for button press events.
+   */
+  bool on_button_press_event(GdkEventButton* event);
+  
+  /**
+   * The event handler for button release events.
+   */
+  bool on_button_release_event(GdkEventButton* event);
+  
+  /**
+   * The event handler for mouse move events.
+   */
   bool on_motion_notify_event(GdkEventMotion *event);
   
   void on_size_request(Gtk::Requisition *requisition);
 
   void on_size_allocate(Gtk::Allocation& allocation);
   
-  /* ===== Internals ===== */
 private:
+  /* ===== Internal Methods ===== */
+
+  /**
+   * As the user drags, this function is called to update the position
+   * of the moving end of the playback period.
+   */
+  void set_leading_x(const int x);
+
   void draw_ruler(Cairo::RefPtr cairo,
     const Gdk::Rectangle ruler_rect);
 
@@ -75,6 +95,9 @@ private:
     
   void draw_selection(Cairo::RefPtr cr,
     const Gdk::Rectangle ruler_rect);
+    
+  void draw_playback_period(Cairo::RefPtr cr,
+    const Gdk::Rectangle ruler_rect);
 
   gavl_time_t calculate_major_spacing() const;
   
@@ -83,6 +106,16 @@ private:
   void read_styles();
   
 private:
+
+  // State values
+  bool isDragging;
+  
+  /**
+   * During a selection drag, one end of the selection is moving with
+   * the mouse, the other is pinned. pinnedDragTime specifies the time
+   * of that point.
+   */
+  gavl_time_t pinnedDragTime;
   
   // Indicated values
   int mouseChevronOffset;
@@ -96,7 +129,11 @@ private:
   int minDivisionWidth;
   int mouseChevronSize;
   int selectionChevronSize;
-  
+  GdkColor playbackArrowColour;
+  float playbackArrowAlpha;
+  int playbackArrowSize;
+  int playbackArrowStemSize;
+
   // Owner
   lumiera::gui::widgets::TimelineWidget *timelineWidget;
   

From b0b436cb925494daa018b13fe15665b95b046c36 Mon Sep 17 00:00:00 2001
From: Joel Holdsworth 
Date: Sat, 30 Aug 2008 23:10:00 +0100
Subject: [PATCH 09/11] Added some documentation to body and ruler

---
 src/gui/widgets/timeline/timeline-body.cpp  | 186 ++++++++++----------
 src/gui/widgets/timeline/timeline-body.hpp  |  47 ++++-
 src/gui/widgets/timeline/timeline-ruler.hpp |  91 +++++++++-
 3 files changed, 219 insertions(+), 105 deletions(-)

diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp
index 231193105..795bc249d 100644
--- a/src/gui/widgets/timeline/timeline-body.cpp
+++ b/src/gui/widgets/timeline/timeline-body.cpp
@@ -125,6 +125,99 @@ TimelineBody::on_realize()
   tool->apply_cursor();
 }
 
+bool
+TimelineBody::on_expose_event(GdkEventExpose* event)
+{
+  Cairo::Matrix view_matrix;
+  
+  REQUIRE(event != NULL);
+  REQUIRE(timelineWidget != NULL);
+  
+  // This is where we draw on the window
+  Glib::RefPtr window = get_window();
+  if(!window)
+    return false;
+  
+  // Makes sure the widget styles have been loaded
+  read_styles();
+  
+  // Prepare to render via cairo
+  Glib::RefPtr