diff --git a/SConstruct b/SConstruct index 93c3e39fd..2b3e2cfd0 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 @@ -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() @@ -340,16 +336,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 = 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. */ 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/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 709a18ac5..65e9ae6b7 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,14 +43,24 @@ 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 + 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)); @@ -58,8 +72,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 @@ -93,6 +109,12 @@ TimelinePanel::on_zoom_out() update_zoom_buttons(); } +void +TimelinePanel::on_mouse_hover(gavl_time_t time) +{ + show_time(time); +} + void TimelinePanel::update_tool_buttons() { @@ -114,6 +136,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 37491c0d9..b46218e87 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -55,9 +55,15 @@ 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(); + + void show_time(gavl_time_t time); private: @@ -65,6 +71,7 @@ private: // Widgets Gtk::Toolbar toolbar; + Gtk::HBox toolStrip; TimelineWidget timelineWidget; // Toolbar Widgets @@ -76,6 +83,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..c7463768b 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) { @@ -108,7 +110,8 @@ TimelineWidget::set_time_offset(gavl_time_t time_offset) timeOffset = time_offset; horizontalAdjustment.set_value(time_offset); - ruler->update_view(); + + viewChangedSignal.emit(); } int64_t @@ -127,7 +130,7 @@ TimelineWidget::set_time_scale(int64_t time_scale) const int view_width = body->get_allocation().get_width(); horizontalAdjustment.set_page_size(timeScale * view_width); - ruler->update_view(); + viewChangedSignal.emit(); } void @@ -183,8 +186,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 +203,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(); @@ -215,11 +262,23 @@ TimelineWidget::set_tool(ToolType tool_type) body->set_tool(tool_type); } +sigc::signal +TimelineWidget::view_changed_signal() const +{ + return viewChangedSignal; +} + +sigc::signal +TimelineWidget::mouse_hover_signal() const +{ + return mouseHoverSignal; +} + void TimelineWidget::on_scroll() { timeOffset = horizontalAdjustment.get_value(); - ruler->update_view(); + viewChangedSignal.emit(); } void @@ -309,6 +368,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..ef13ef556 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. @@ -132,6 +154,12 @@ public: * Sets the type of the tool currently active. */ void set_tool(timeline::ToolType tool_type); + +public: + /* ===== Signals ===== */ + sigc::signal view_changed_signal() const; + + sigc::signal mouse_hover_signal() const; /* ===== Events ===== */ protected: @@ -165,6 +193,8 @@ protected: // Selection State gavl_time_t selectionStart; gavl_time_t selectionEnd; + gavl_time_t playbackPeriodStart; + gavl_time_t playbackPeriodEnd; int totalHeight; @@ -179,9 +209,18 @@ protected: Gtk::Adjustment horizontalAdjustment, verticalAdjustment; Gtk::HScrollbar horizontalScroll; Gtk::VScrollbar verticalScroll; + + // Signals + sigc::signal viewChangedSignal; + sigc::signal mouseHoverSignal; /* ===== Constants ===== */ public: + /** + * The maximum scale for timeline display. + * @remarks At MaxScale, every pixel on the timeline is equivalent + * to 30000000 gavl_time_t increments. + */ static const int64_t MaxScale; protected: diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 64f545190..a3602572a 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -55,10 +55,8 @@ TimelineBody::TimelineBody(lumiera::gui::widgets::TimelineWidget REQUIRE(timelineWidget != NULL); // Connect up some events - timelineWidget->horizontalAdjustment.signal_value_changed().connect( - sigc::mem_fun(this, &TimelineBody::on_scroll) ); - timelineWidget->verticalAdjustment.signal_value_changed().connect( - sigc::mem_fun(this, &TimelineBody::on_scroll) ); + timelineWidget->view_changed_signal().connect(sigc::mem_fun( + this, &TimelineBody::on_update_view) ); // Install style properties register_styles(); @@ -109,6 +107,12 @@ TimelineBody::set_tool(timeline::ToolType tool_type) tool->apply_cursor(); } +void +TimelineBody::on_update_view() +{ + queue_draw(); +} + void TimelineBody::on_realize() { @@ -125,112 +129,6 @@ TimelineBody::on_realize() tool->apply_cursor(); } -void -TimelineBody::on_scroll() -{ - queue_draw(); -} - -bool -TimelineBody::on_scroll_event (GdkEventScroll* event) -{ - REQUIRE(event != NULL); - REQUIRE(timelineWidget != NULL); - - if(event->state & GDK_CONTROL_MASK) - { - switch(event->direction) - { - case GDK_SCROLL_UP: - // User scrolled up. Zoom in - timelineWidget->zoom_view(event->x, 16); - break; - - case GDK_SCROLL_DOWN: - // User scrolled down. Zoom out - timelineWidget->zoom_view(event->x, -16); - break; - } - } - else - { - switch(event->direction) - { - case GDK_SCROLL_UP: - // User scrolled up. Shift 1/16th left - timelineWidget->shift_view(-16); - break; - - case GDK_SCROLL_DOWN: - // User scrolled down. Shift 1/16th right - timelineWidget->shift_view(16); - break; - } - } -} - -bool -TimelineBody::on_button_press_event(GdkEventButton* event) -{ - mouseDownX = event->x; - mouseDownY = event->y; - - switch(event->button) - { - case 2: - begin_shift_drag(); - break; - - default: - dragType = None; - break; - } - - // Forward the event to the tool - tool->on_button_press_event(event); - - return true; -} - -bool -TimelineBody::on_button_release_event(GdkEventButton* event) -{ - // Terminate any drags - dragType = None; - - // Forward the event to the tool - tool->on_button_release_event(event); - - return true; -} - -bool -TimelineBody::on_motion_notify_event(GdkEventMotion *event) -{ - REQUIRE(event != NULL); - - switch(dragType) - { - case Shift: - { - const int64_t scale = timelineWidget->get_time_scale(); - gavl_time_t offset = beginShiftTimeOffset + - (int64_t)(mouseDownX - event->x) * scale; - timelineWidget->set_time_offset(offset); - - set_vertical_offset((int)(mouseDownY - event->y) + - beginShiftVerticalOffset); - break; - } - } - - // Forward the event to the tool - tool->on_motion_notify_event(event); - - // false so that the message is passed up to the owner TimelineWidget - return false; -} - bool TimelineBody::on_expose_event(GdkEventExpose* event) { @@ -323,6 +221,106 @@ TimelineBody::on_expose_event(GdkEventExpose* event) return true; } + +bool +TimelineBody::on_scroll_event (GdkEventScroll* event) +{ + REQUIRE(event != NULL); + REQUIRE(timelineWidget != NULL); + + if(event->state & GDK_CONTROL_MASK) + { + switch(event->direction) + { + case GDK_SCROLL_UP: + // User scrolled up. Zoom in + timelineWidget->zoom_view(event->x, 1); + break; + + case GDK_SCROLL_DOWN: + // User scrolled down. Zoom out + timelineWidget->zoom_view(event->x, -1); + break; + } + } + else + { + switch(event->direction) + { + case GDK_SCROLL_UP: + // User scrolled up. Shift 1/16th left + timelineWidget->shift_view(-16); + break; + + case GDK_SCROLL_DOWN: + // User scrolled down. Shift 1/16th right + timelineWidget->shift_view(16); + break; + } + } +} + +bool +TimelineBody::on_button_press_event(GdkEventButton* event) +{ + mouseDownX = event->x; + mouseDownY = event->y; + + switch(event->button) + { + case 2: + begin_shift_drag(); + break; + + default: + dragType = None; + break; + } + + // Forward the event to the tool + tool->on_button_press_event(event); + + return true; +} + +bool +TimelineBody::on_button_release_event(GdkEventButton* event) +{ + // Terminate any drags + dragType = None; + + // Forward the event to the tool + tool->on_button_release_event(event); + + return true; +} + +bool +TimelineBody::on_motion_notify_event(GdkEventMotion *event) +{ + REQUIRE(event != NULL); + + switch(dragType) + { + case Shift: + { + const int64_t scale = timelineWidget->get_time_scale(); + gavl_time_t offset = beginShiftTimeOffset + + (int64_t)(mouseDownX - event->x) * scale; + timelineWidget->set_time_offset(offset); + + set_vertical_offset((int)(mouseDownY - event->y) + + beginShiftVerticalOffset); + break; + } + } + + // Forward the event to the tool + tool->on_motion_notify_event(event); + + // false so that the message is passed up to the owner TimelineWidget + return false; +} void TimelineBody::begin_shift_drag() @@ -349,23 +347,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-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 8fa72cd89..9fcf15885 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -37,33 +37,72 @@ class TimelineWidget; namespace timeline { +/** + * Implementation of the timeline body subwidget. This widget is + * displayed in the centre of the timeline widget, and displays the + * content of all timeline tracks. + */ class TimelineBody : public Gtk::DrawingArea { public: + + /** + * Constructor + * @param timeline_widget The owner widget of this ruler. + */ TimelineBody(lumiera::gui::widgets::TimelineWidget *timeline_widget); + /** + * Destructor + */ ~TimelineBody(); + /** + * Returns the type of the currently selected timeline tool. + */ ToolType get_tool() const; + /** + * Selects a tool of a specified type. + * @param tool_type The type of tool to set. + */ void set_tool(ToolType tool_type); /* ===== Events ===== */ protected: - void on_realize(); - void on_scroll(); + /** + * An event handler for when the view window of the timeline changes. + */ + void on_update_view(); + + /** + * An event handler for when the widget is realized. + */ + void on_realize(); + + /** + * An event handler for when the window must be redrawn. + */ + bool on_expose_event(GdkEventExpose* event); bool on_scroll_event(GdkEventScroll* event); - bool on_button_press_event (GdkEventButton* event); + /** + * The event handler for button press events. + */ + bool on_button_press_event(GdkEventButton* event); - bool on_button_release_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); - bool on_expose_event(GdkEventExpose* event); - /* ===== Internals ===== */ private: void begin_shift_drag(); @@ -72,8 +111,14 @@ private: void set_vertical_offset(int offset); + /** + * Registers all the styles that this class will respond to. + */ void register_styles() const; + /** + * Reads styles from the present stylesheet. + */ void read_styles(); private: 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..08eee617c 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,10 +56,17 @@ TimelineRuler::TimelineRuler( minDivisionWidth(100), mouseChevronSize(5), selectionChevronSize(5), + playbackArrowAlpha(0.5f), + playbackArrowSize(10), + playbackArrowStemSize(3), timelineWidget(timeline_widget) { REQUIRE(timelineWidget != NULL); + // Connect event handlers + timelineWidget->view_changed_signal().connect(sigc::mem_fun( + this, &TimelineRuler::on_update_view) ); + // Install style properties register_styles(); } @@ -70,7 +79,7 @@ TimelineRuler::set_mouse_chevron_offset(int offset) } void -TimelineRuler::update_view() +TimelineRuler::on_update_view() { rulerImage.clear(); queue_draw(); @@ -82,7 +91,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 +142,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 +205,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) @@ -171,8 +226,8 @@ TimelineRuler::draw_ruler(Cairo::RefPtr cr, REQUIRE(ruler_rect.get_height() > 0); REQUIRE(timelineWidget != NULL); - const gavl_time_t left_offset = timelineWidget->timeOffset; - const int64_t time_scale = timelineWidget->timeScale; + const gavl_time_t left_offset = timelineWidget->get_time_offset(); + const int64_t time_scale = timelineWidget->get_time_scale(); // Preparation steps const int height = ruler_rect.get_height(); @@ -299,6 +354,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 +525,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 +561,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..affda5adf 100644 --- a/src/gui/widgets/timeline/timeline-ruler.hpp +++ b/src/gui/widgets/timeline/timeline-ruler.hpp @@ -37,54 +37,154 @@ class TimelineWidget; namespace timeline { +/** + * A subwidget of the TimelineWidget. This class implements a ruler + * which is placed along the top edge of the timeline. + */ class TimelineRuler : public Gtk::DrawingArea { public: + /** + * Constructor + * @param timeline_widget The owner widget of this ruler. + */ TimelineRuler( lumiera::gui::widgets::TimelineWidget *timeline_widget); /** - * Sets the offset of the mouse chevron in pixels from the left - * edge of the widget. If offset is less than 0 or greater than the - * width, the chevron will not be visible. + * Sets offset of the mouse chevron + * @param offset The offset of the mouse chevron in pixels from the + * left edge of the widget. If offset is less than 0 or greater than + * the width, the chevron will not be visible. */ void set_mouse_chevron_offset(int offset); - void update_view(); - /* ===== Events ===== */ -protected: +private: + /** + * An event handler for when the view window of the timeline changes. + * @remarks Causes the ruler to be redrawn from scratch. The cached + * ruler backdrop is destroyed and redrawn. + */ + void on_update_view(); + /** + * An event handler for when the widget is realized. + */ void on_realize(); + /** + * An event handler for when the window must be redrawn. + */ 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); + /** + * The handler for when the widget must calculate it's new shape. + */ void on_size_request(Gtk::Requisition *requisition); + /** + * The handler for when the widget must take the size of a given + * area. + */ void on_size_allocate(Gtk::Allocation& allocation); - /* ===== Internals ===== */ private: - void draw_ruler(Cairo::RefPtr cairo, + /* ===== 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); + + /** + * Draws the ruler graduations. + * @param cr The cairo context to draw the ruler into. + * @param ruler_rect The area of the ruler widget. + */ + void draw_ruler(Cairo::RefPtr cr, const Gdk::Rectangle ruler_rect); + /** + * Overlays the mouse chevron. + * @param cr The cairo context to draw the chevron into. + * @param ruler_rect The area of the ruler widget. + */ void draw_mouse_chevron(Cairo::RefPtr cr, const Gdk::Rectangle ruler_rect); + /** + * Overlays the currently selected period. + * @param cr The cairo context to draw the selection into. + * @param ruler_rect The area of the ruler widget. + */ void draw_selection(Cairo::RefPtr cr, const Gdk::Rectangle ruler_rect); - - gavl_time_t calculate_major_spacing() const; + /** + * Overlays the currently selected playback period. + * @param cr The cairo context to draw the period into. + * @param ruler_rect The area of the ruler widget. + */ + void draw_playback_period(Cairo::RefPtr cr, + const Gdk::Rectangle ruler_rect); + + /** + * Given the current zoom, this function calculates the preiod + * between major graduations on the ruler scale. + * @return The period as a gavl_time_t + */ + gavl_time_t calculate_major_spacing() const; + + /** + * Registers all the styles that this class will respond to. + */ void register_styles() const; + /** + * Reads styles from the present stylesheet. + */ void read_styles(); private: + + // State values + + /** + * This value is set to true if the user is dragging with the left + * mouse button. + */ + 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 + /** + * The offset from the left of the control in pixels to draw the + * mouse chevron. If offset is less than 0 or greater than + * the width, the chevron will not be visible. + */ int mouseChevronOffset; // Style values @@ -96,11 +196,23 @@ private: int minDivisionWidth; int mouseChevronSize; int selectionChevronSize; - - // Owner + GdkColor playbackArrowColour; + float playbackArrowAlpha; + int playbackArrowSize; + int playbackArrowStemSize; + + /** + * The owner widget + */ lumiera::gui::widgets::TimelineWidget *timelineWidget; - // Cached ruler image + /** + * The caches image of the ruler, over which the chevrons etc. will + * be drawn. + * @remarks This backdrop is cached because it changes relatively + * infrequently in comparison to the chevrons, thus improving + * performance somewhat. + */ Cairo::RefPtr rulerImage; }; 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: diff --git a/wiki/draw/LumiLogo.png b/wiki/draw/LumiLogo.png new file mode 100644 index 000000000..1fd263aed Binary files /dev/null and b/wiki/draw/LumiLogo.png differ diff --git a/wiki/index.html b/wiki/index.html index c8a1a7ad2..fd3fe210e 100644 --- a/wiki/index.html +++ b/wiki/index.html @@ -2150,16 +2150,16 @@ This distributed wiki might be used instead the pipapo.org wiki, investigate tha Wiki works. It is simple to use and just flexible enough to handle the task. I don't go to install any other software for such tasks on my server. While the design progresses I'd propose to move our work into git repositories and eventually phase this wiki pages out anyways. I'd rather like to start out distributed/git right away .. but git gives us only a fine storage layer, for a design process we need some good presentation layer (later when using git and starting the implementation everyones favorite editor serves for that) I have no better ideas yet to solve the presentation problem other than using this wiki (or maybe Bouml). -
-
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