diff --git a/Makefile.am b/Makefile.am index 2edbc6e28..effa2b05b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,6 @@ # Copyright (C) Lumiera.org # 2007, Christian Thaeter +# 2008, Joel Holdsworth # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -47,6 +48,9 @@ include $(top_srcdir)/src/gui/Makefile.am # plugins #include $(top_srcdir)/src... +# resources +include $(top_srcdir)/icons/Makefile.am + # tests include $(top_srcdir)/tests/common/Makefile.am include $(top_srcdir)/tests/components/Makefile.am diff --git a/admin/render-icon.py b/admin/render-icon.py new file mode 100755 index 000000000..9bf4c7dc5 --- /dev/null +++ b/admin/render-icon.py @@ -0,0 +1,175 @@ +#!/usr/bin/python + +# render-icons.py - Icon rendering utility script +# +# Copyright (C) Lumiera.org +# 2008, Joel Holdsworth +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sys +import getopt +from xml.dom import minidom +import os +import shutil +import cairo +import rsvg + +#svgDir = "svg" +#prerenderedDir = "prerendered" +inkscapePath = "/usr/bin/inkscape" +artworkLayerPrefix = "artwork:" + +def createDirectory( name ): + if os.path.exists(name) == False: + os.mkdir(name) + +def copyMergeDirectory( src, dst ): + listing = os.listdir(src) + for file_name in listing: + src_file_path = os.path.join(src, file_name) + dst_file_path = os.path.join(dst, file_name) + shutil.copyfile(src_file_path, dst_file_path) + +def getDocumentSize( svg_element ): + width = float(svg_element.getAttribute("width")) + height = float(svg_element.getAttribute("height")) + return [width, height] + +def findChildLayerElement( parent_element ): + for node in parent_element.childNodes: + if node.nodeType == minidom.Node.ELEMENT_NODE: + if node.tagName == "g": + if node.getAttribute("inkscape:groupmode") == "layer": + return node + return None + +def parsePlateLayer( layer ): + rectangles = [] + for node in layer.childNodes: + if node.nodeType == minidom.Node.ELEMENT_NODE: + if node.tagName == "rect": + x = float(node.getAttribute("x")) + y = float(node.getAttribute("y")) + width = float(node.getAttribute("width")) + height = float(node.getAttribute("height")) + rectangles.append([x, y, width, height]) + return rectangles + +def parseSVG( file_path ): + print "Rendering " + file_path + svgdoc = minidom.parse(file_path) + for root_node in svgdoc.childNodes: + if root_node.nodeType == minidom.Node.ELEMENT_NODE: + if root_node.tagName == "svg": + size = getDocumentSize( root_node ) + layer = findChildLayerElement( root_node ) + if layer != None: + layer_name = layer.getAttribute("inkscape:label") + if layer_name[:len(artworkLayerPrefix)] == artworkLayerPrefix: + artwork_name = layer_name[len(artworkLayerPrefix):] + plate = findChildLayerElement( layer ) + if plate != None: + return artwork_name, size, parsePlateLayer( plate ) + return None + +def renderSvgInkscape(file_path, out_dir, artwork_name, rectangle, doc_size): + + # Calculate the rendering rectangle + x1 = rectangle[0] + y1 = doc_size[1] - rectangle[1] - rectangle[3] + x2 = x1 + rectangle[2] + y2 = y1 + rectangle[3] + + # Call Inkscape to do the render + os.spawnlp(os.P_WAIT, inkscapePath, inkscapePath, + file_path, + "-z", + "-a %g:%g:%g:%g" % (x1, y1, x2, y2), + "-w %g" % (rectangle[2]), "-h %g" % (rectangle[3]), + "--export-png=" + os.path.join(out_dir, "%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name))) + +def renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size): + # Prepare a Cairo context + width = int(rectangle[2]) + height = int(rectangle[3]) + + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + context = cairo.Context(surface) + context.translate(-rectangle[0], -rectangle[1]) + + # Load an render the SVG + svg = rsvg.Handle(file=file_path) + svg.render_cairo(context) + + # Output a PNG file + surface.write_to_png(os.path.join(out_dir, + "%ix%i/%s.png" % (width, height, artwork_name))) + +def renderSvgIcon(file_path, out_dir): + artwork_name, doc_size, rectangles = parseSVG(file_path) + for rectangle in rectangles: + renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size) + +#def renderSvgIcons(): +# listing = os.listdir(svgDir) +# for file_path in listing: +# [root, extension] = os.path.splitext(file_path) +# if extension.lower() == ".svg": +# renderSvgIcon(os.path.join(svgDir, file_path)) + +#def copyPrerenderedIcons(): +# listing = os.listdir(prerenderedDir) +# for list_item in listing: +# src_dir = os.path.join(prerenderedDir, list_item) +# copyMergeDirectory(src_dir, list_item) + +def printHelp(): + print "render-icon.py - An icon rendering utility script for lumiera" + +def parseArguments(argv): + optlist, args = getopt.getopt(argv, "") + + if len(args) == 2: + return args[0], args[1] + + printHelp() + return None, None + +def main(argv): + in_path, out_dir = parseArguments(argv) + + if in_path == None or out_dir == None: + return + + if os.path.exists(out_dir) == False: + print "Directory not found: " + out_dir + return + + # Create the icons folders + createDirectory(os.path.join(out_dir, "48x48")) + createDirectory(os.path.join(out_dir, "32x32")) + createDirectory(os.path.join(out_dir, "24x24")) + createDirectory(os.path.join(out_dir, "22x22")) + createDirectory(os.path.join(out_dir, "16x16")) + + renderSvgIcon(in_path, out_dir) + + # Copy in prerendered icons + #copyPrerenderedIcons() + +if __name__=="__main__": + main(sys.argv[1:]) + diff --git a/icons/Makefile.am b/icons/Makefile.am new file mode 100644 index 000000000..d3adca1fa --- /dev/null +++ b/icons/Makefile.am @@ -0,0 +1,53 @@ +# Copyright (C) Lumiera.org +# 2008, Joel Holdsworth +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +svgdir = $(top_srcdir)/icons/svg +prerendereddir = $(top_srcdir)/icons/prerendered +icondir = $(top_builddir) +iconcommand = python $(top_srcdir)/admin/render-icon.py + +16x16 = $(icondir)/16x16 +22x22 = $(icondir)/22x22 +24x24 = $(icondir)/24x24 +32x32 = $(icondir)/32x32 +48x48 = $(icondir)/48x48 + +16x16pre = $(prerendereddir)/16x16 +22x22pre = $(prerendereddir)/22x22 +24x24pre = $(prerendereddir)/24x24 +32x32pre = $(prerendereddir)/32x32 +48x48pre = $(prerendereddir)/48x48 + +lumigui_DEPENDENCIES += \ + $(16x16)/arrow.png $(22x22)/arrow.png $(24x24)/arrow.png $(32x32)/arrow.png $(48x48)/arrow.png \ + $(16x16)/i-beam.png $(22x22)/i-beam.png $(24x24)/i-beam.png $(32x32)/i-beam.png $(48x48)/i-beam.png \ + $(16x16)/assets-panel.png \ + $(16x16)/timeline-panel.png \ + $(16x16)/viewer-panel.png + +$(16x16)/arrow.png $(22x22)/arrow.png $(24x24)/arrow.png $(32x32)/arrow.png $(48x48)/arrow.png : $(svgdir)/arrow.svg + $(iconcommand) $< $(icondir) +$(16x16)/i-beam.png $(22x22)/i-beam.png $(24x24)/i-beam.png $(32x32)/i-beam.png $(48x48)/i-beam.png : $(svgdir)/i-beam.svg + $(iconcommand) $< $(icondir) + +$(16x16)/assets-panel.png: + cp $(16x16pre)/assets-panel.png $(16x16) +$(16x16)/timeline-panel.png: + cp $(16x16pre)/timeline-panel.png $(16x16) +$(16x16)/viewer-panel.png: + cp $(16x16pre)/viewer-panel.png $(16x16) + diff --git a/icons/assets-panel.png b/icons/prerendered/16x16/assets-panel.png similarity index 100% rename from icons/assets-panel.png rename to icons/prerendered/16x16/assets-panel.png diff --git a/icons/timeline-panel.png b/icons/prerendered/16x16/timeline-panel.png similarity index 100% rename from icons/timeline-panel.png rename to icons/prerendered/16x16/timeline-panel.png diff --git a/icons/viewer-panel.png b/icons/prerendered/16x16/viewer-panel.png similarity index 100% rename from icons/viewer-panel.png rename to icons/prerendered/16x16/viewer-panel.png diff --git a/icons/svg/arrow.svg b/icons/svg/arrow.svg new file mode 100644 index 000000000..2434fad9d --- /dev/null +++ b/icons/svg/arrow.svg @@ -0,0 +1,732 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/svg/i-beam.svg b/icons/svg/i-beam.svg new file mode 100644 index 000000000..250f087c0 --- /dev/null +++ b/icons/svg/i-beam.svg @@ -0,0 +1,690 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index b1560e485..db652d99b 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -1,5 +1,5 @@ -# Copyright (C) Lumiera.org -# 2007, Joel Holdsworth +# Copyright (C) Lumiera.org +# 2007, Joel Holdsworth # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -22,74 +22,69 @@ lumigui_srcdir = $(top_srcdir)/src/gui #lumigui_CPPFLAGS = -I$(top_srcdir)/src/ INCLUDES = \ - -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ - -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ - -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ + -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ + -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ $(GTK_LUMIERA_CFLAGS) bin_PROGRAMS += lumigui -lumigui_SOURCES = \ - $(lumigui_srcdir)/gtk-lumiera.cpp \ - $(lumigui_srcdir)/gtk-lumiera.hpp \ - $(lumigui_srcdir)/window-manager.cpp \ - $(lumigui_srcdir)/window-manager.hpp \ - $(lumigui_srcdir)/workspace/actions.cpp \ - $(lumigui_srcdir)/workspace/actions.hpp \ - $(lumigui_srcdir)/workspace/workspace-window.cpp \ - $(lumigui_srcdir)/workspace/workspace-window.hpp \ - $(lumigui_srcdir)/dialogs/render.cpp \ - $(lumigui_srcdir)/dialogs/render.hpp \ - $(lumigui_srcdir)/dialogs/preferences-dialog.cpp \ - $(lumigui_srcdir)/dialogs/preferences-dialog.hpp \ - $(lumigui_srcdir)/panels/panel.cpp \ - $(lumigui_srcdir)/panels/panel.hpp \ - $(lumigui_srcdir)/panels/timeline-panel.cpp \ - $(lumigui_srcdir)/panels/timeline-panel.hpp \ - $(lumigui_srcdir)/panels/viewer-panel.cpp \ - $(lumigui_srcdir)/panels/viewer-panel.hpp \ - $(lumigui_srcdir)/panels/assets-panel.cpp \ - $(lumigui_srcdir)/panels/asset-panels.hpp \ - $(lumigui_srcdir)/widgets/video-display-widget.cpp \ - $(lumigui_srcdir)/widgets/video-display-widget.hpp \ - $(lumigui_srcdir)/widgets/timeline-widget.cpp \ - $(lumigui_srcdir)/widgets/timeline-widget.hpp \ - $(lumigui_srcdir)/widgets/timeline/header-container.cpp \ - $(lumigui_srcdir)/widgets/timeline/header-container.hpp \ - $(lumigui_srcdir)/widgets/timeline/track.cpp \ - $(lumigui_srcdir)/widgets/timeline/track.hpp \ - $(lumigui_srcdir)/widgets/timeline/video-track.cpp \ - $(lumigui_srcdir)/widgets/timeline/video-track.hpp \ - $(lumigui_srcdir)/widgets/timeline/timeline-body.cpp \ - $(lumigui_srcdir)/widgets/timeline/timeline-body.hpp \ - $(lumigui_srcdir)/widgets/timeline/timeline-ruler.cpp \ - $(lumigui_srcdir)/widgets/timeline/timeline-ruler.hpp \ - $(lumigui_srcdir)/model/project.cpp \ - $(lumigui_srcdir)/model/project.hpp \ - $(lumigui_srcdir)/output/displayer.cpp \ - $(lumigui_srcdir)/output/displayer.hpp \ - $(lumigui_srcdir)/output/gdkdisplayer.cpp \ - $(lumigui_srcdir)/output/gdkdisplayer.hpp \ - $(lumigui_srcdir)/output/xvdisplayer.cpp \ - $(lumigui_srcdir)/output/xvdisplayer.hpp +lumigui_SOURCES = \ + $(lumigui_srcdir)/gtk-lumiera.cpp \ + $(lumigui_srcdir)/gtk-lumiera.hpp \ + $(lumigui_srcdir)/window-manager.cpp \ + $(lumigui_srcdir)/window-manager.hpp \ + $(lumigui_srcdir)/workspace/actions.cpp \ + $(lumigui_srcdir)/workspace/actions.hpp \ + $(lumigui_srcdir)/workspace/workspace-window.cpp \ + $(lumigui_srcdir)/workspace/workspace-window.hpp \ + $(lumigui_srcdir)/dialogs/render.cpp \ + $(lumigui_srcdir)/dialogs/render.hpp \ + $(lumigui_srcdir)/dialogs/preferences-dialog.cpp \ + $(lumigui_srcdir)/dialogs/preferences-dialog.hpp \ + $(lumigui_srcdir)/panels/panel.cpp \ + $(lumigui_srcdir)/panels/panel.hpp \ + $(lumigui_srcdir)/panels/timeline-panel.cpp \ + $(lumigui_srcdir)/panels/timeline-panel.hpp \ + $(lumigui_srcdir)/panels/viewer-panel.cpp \ + $(lumigui_srcdir)/panels/viewer-panel.hpp \ + $(lumigui_srcdir)/panels/assets-panel.cpp \ + $(lumigui_srcdir)/panels/asset-panels.hpp \ + $(lumigui_srcdir)/widgets/video-display-widget.cpp \ + $(lumigui_srcdir)/widgets/video-display-widget.hpp \ + $(lumigui_srcdir)/widgets/timeline-widget.cpp \ + $(lumigui_srcdir)/widgets/timeline-widget.hpp \ + $(lumigui_srcdir)/widgets/timeline/header-container.cpp \ + $(lumigui_srcdir)/widgets/timeline/header-container.hpp \ + $(lumigui_srcdir)/widgets/timeline/track.cpp \ + $(lumigui_srcdir)/widgets/timeline/track.hpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-body.cpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-body.hpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-ruler.cpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-ruler.hpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-tool.cpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-tool.hpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-arrow-tool.cpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-arrow-tool.hpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-ibeam-tool.cpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-ibeam-tool.hpp \ + $(lumigui_srcdir)/model/project.cpp \ + $(lumigui_srcdir)/model/project.hpp \ + $(lumigui_srcdir)/output/displayer.cpp \ + $(lumigui_srcdir)/output/displayer.hpp \ + $(lumigui_srcdir)/output/gdkdisplayer.cpp \ + $(lumigui_srcdir)/output/gdkdisplayer.hpp \ + $(lumigui_srcdir)/output/xvdisplayer.cpp \ + $(lumigui_srcdir)/output/xvdisplayer.hpp lumigui_LDFLAGS = lumigui_LDADD = $(GTK_LUMIERA_LIBS) liblumicommon.a liblumi.a -lumigui_DEPENDENCIES = \ - $(top_builddir)/lumiera_ui.rc \ - $(top_builddir)/assets-panel.png \ - $(top_builddir)/timeline-panel.png \ - $(top_builddir)/viewer-panel.png - +lumigui_DEPENDENCIES = \ + $(top_builddir)/lumiera_ui.rc \ + $(top_builddir)/liblumicommon.a \ + $(top_builddir)/liblumi.a + $(top_builddir)/lumiera_ui.rc: cp $(lumigui_srcdir)/lumiera_ui.rc $(top_builddir) -$(top_builddir)/assets-panel.png: - cp $(top_srcdir)/icons/assets-panel.png $(top_builddir) -$(top_builddir)/timeline-panel.png: - cp $(top_srcdir)/icons/timeline-panel.png $(top_builddir) -$(top_builddir)/viewer-panel.png: - cp $(top_srcdir)/icons/viewer-panel.png $(top_builddir) - - diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 3b6bfed84..b03b4604f 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -25,6 +25,7 @@ using namespace Gtk; using namespace sigc; +using namespace lumiera::gui::widgets; namespace lumiera { namespace gui { @@ -34,26 +35,52 @@ const int TimelinePanel::ZoomToolSteps = 2; // 2 seems comfortable TimelinePanel::TimelinePanel() : Panel("timeline", _("Timeline"), "timeline_panel"), + arrowTool(Gtk::StockID("arrow")), + iBeamTool(Gtk::StockID("i_beam")), zoomIn(Stock::ZOOM_IN), - zoomOut(Stock::ZOOM_OUT) + zoomOut(Stock::ZOOM_OUT), + updatingToolbar(false) { // Setup the toolbar + 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(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in)); toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out)); - toolbar.set_icon_size(IconSize(ICON_SIZE_SMALL_TOOLBAR)); + toolbar.set_icon_size(IconSize(ICON_SIZE_LARGE_TOOLBAR)); toolbar.set_toolbar_style(TOOLBAR_ICONS); // Add the toolbar pack_start(toolbar, PACK_SHRINK); pack_start(timelineWidget, PACK_EXPAND_WIDGET); + update_tool_buttons(); update_zoom_buttons(); } +void +TimelinePanel::on_arrow_tool() +{ + if(updatingToolbar) return; + timelineWidget.set_tool(timeline::Arrow); + update_tool_buttons(); +} + +void +TimelinePanel::on_ibeam_tool() +{ + if(updatingToolbar) return; + timelineWidget.set_tool(timeline::IBeam); + update_tool_buttons(); +} + void TimelinePanel::on_zoom_in() { + timelineWidget.zoom_view(ZoomToolSteps); update_zoom_buttons(); } @@ -65,6 +92,19 @@ TimelinePanel::on_zoom_out() update_zoom_buttons(); } +void +TimelinePanel::update_tool_buttons() +{ + if(!updatingToolbar) + { + updatingToolbar = true; + const timeline::ToolType tool = timelineWidget.get_tool(); + arrowTool.set_active(tool == timeline::Arrow); + iBeamTool.set_active(tool == timeline::IBeam); + updatingToolbar = false; + } +} + void TimelinePanel::update_zoom_buttons() { diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index a14288c41..01791f91b 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -42,10 +42,14 @@ public: private: //----- Event Handlers -----// + void on_arrow_tool(); + void on_ibeam_tool(); + void on_zoom_in(); void on_zoom_out(); private: + void update_tool_buttons(); void update_zoom_buttons(); private: @@ -57,9 +61,17 @@ private: TimelineWidget timelineWidget; // Toolbar Widgets + Gtk::ToggleToolButton arrowTool; + Gtk::ToggleToolButton iBeamTool; + + Gtk::SeparatorToolItem seperator1; + Gtk::ToolButton zoomIn; Gtk::ToolButton zoomOut; + // Internals + bool updatingToolbar; + //----- Constants -----// static const int ZoomToolSteps; }; diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 5011103ac..867595ad3 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -44,6 +44,8 @@ TimelineWidget::TimelineWidget() : totalHeight(0), horizontalAdjustment(0, 0, 0), verticalAdjustment(0, 0, 0), + selectionStart(0), + selectionEnd(0), horizontalScroll(horizontalAdjustment), verticalScroll(verticalAdjustment) { @@ -51,6 +53,8 @@ TimelineWidget::TimelineWidget() : ENSURE(body != NULL); headerContainer = new HeaderContainer(this); ENSURE(headerContainer != NULL); + ruler = new TimelineRuler(this); + ENSURE(ruler != NULL); horizontalAdjustment.signal_value_changed().connect( sigc::mem_fun( this, &TimelineWidget::on_scroll) ); @@ -58,11 +62,12 @@ TimelineWidget::TimelineWidget() : this, &TimelineWidget::on_scroll) ); body->signal_motion_notify_event().connect( sigc::mem_fun( this, &TimelineWidget::on_motion_in_body_notify_event) ); - + set_time_scale(GAVL_TIME_SCALE / 200); + set_selection(2000000, 4000000); attach(*body, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND); - attach(ruler, 1, 2, 0, 1, FILL|EXPAND, SHRINK); + attach(*ruler, 1, 2, 0, 1, FILL|EXPAND, SHRINK); attach(*headerContainer, 0, 1, 1, 2, SHRINK, FILL|EXPAND); attach(horizontalScroll, 1, 2, 2, 3, FILL|EXPAND, SHRINK); attach(verticalScroll, 2, 3, 1, 2, SHRINK, FILL|EXPAND); @@ -71,14 +76,23 @@ TimelineWidget::TimelineWidget() : tracks.push_back(&video2); update_tracks(); + + set_tool(timeline::Arrow); } TimelineWidget::~TimelineWidget() { REQUIRE(body != NULL); - body->unreference(); + if(body != NULL) + body->unreference(); + REQUIRE(headerContainer != NULL); - headerContainer->unreference(); + if(headerContainer != NULL) + headerContainer->unreference(); + + REQUIRE(ruler != NULL); + if(ruler != NULL) + ruler->unreference(); } gavl_time_t @@ -90,9 +104,11 @@ TimelineWidget::get_time_offset() const void TimelineWidget::set_time_offset(gavl_time_t time_offset) { + REQUIRE(ruler != NULL); + timeOffset = time_offset; horizontalAdjustment.set_value(time_offset); - ruler.set_time_offset(time_offset); + ruler->update_view(); } int64_t @@ -104,17 +120,14 @@ TimelineWidget::get_time_scale() const void TimelineWidget::set_time_scale(int64_t time_scale) { - timeScale = time_scale; - ruler.set_time_scale(time_scale); -} - -void -TimelineWidget::shift_view(int shift_size) -{ - const int view_width = body->get_allocation().get_width(); + REQUIRE(ruler != NULL); - set_time_offset(get_time_offset() + - shift_size * timeScale * view_width / 16); + timeScale = time_scale; + + const int view_width = body->get_allocation().get_width(); + horizontalAdjustment.set_page_size(timeScale * view_width); + + ruler->update_view(); } void @@ -148,11 +161,65 @@ TimelineWidget::zoom_view(int point, int zoom_size) set_time_scale(new_time_scale); } +void +TimelineWidget::shift_view(int shift_size) +{ + const int view_width = body->get_allocation().get_width(); + + set_time_offset(get_time_offset() + + shift_size * timeScale * view_width / 16); +} + +gavl_time_t +TimelineWidget::get_selection_start() const +{ + return selectionStart; +} + +gavl_time_t +TimelineWidget::get_selection_end() const +{ + return selectionEnd; +} + +void +TimelineWidget::set_selection(gavl_time_t start, gavl_time_t end) +{ + if(start < end) + { + selectionStart = start; + selectionEnd = end; + } + else + { + // The selection is back-to-front, flip it round + selectionStart = end; + selectionEnd = start; + } + + ruler->queue_draw(); + body->queue_draw(); +} + +ToolType +TimelineWidget::get_tool() const +{ + REQUIRE(body != NULL); + return body->get_tool(); +} + +void +TimelineWidget::set_tool(ToolType tool_type) +{ + REQUIRE(body != NULL); + body->set_tool(tool_type); +} + void TimelineWidget::on_scroll() { timeOffset = horizontalAdjustment.get_value(); - ruler.set_time_offset(timeOffset); + ruler->update_view(); } void @@ -163,6 +230,18 @@ TimelineWidget::on_size_allocate(Allocation& allocation) update_scroll(); } +int +TimelineWidget::time_to_x(gavl_time_t time) const +{ + return (int)((time - timeOffset) / timeScale); +} + +gavl_time_t +TimelineWidget::x_to_time(int x) const +{ + return (gavl_time_t)((int64_t)x * timeScale + timeOffset); +} + void TimelineWidget::update_tracks() { @@ -186,9 +265,14 @@ TimelineWidget::update_scroll() //----- Horizontal Scroll ------// + // TEST CODE horizontalAdjustment.set_upper(1000 * GAVL_TIME_SCALE / 200); horizontalAdjustment.set_lower(-1000 * GAVL_TIME_SCALE / 200); + // Set the page size + horizontalAdjustment.set_page_size( + timeScale * body_allocation.get_width()); + //----- Vertical Scroll -----// // Calculate the vertical length that can be scrolled: @@ -204,10 +288,14 @@ TimelineWidget::update_scroll() verticalAdjustment.set_upper(y_scroll_length); // Hide the scrollbar if no scrolling is possible - if(y_scroll_length == 0 && verticalScroll.is_visible()) +#if 0 + // Having this code included seems to cause a layout loop as the + // window is shrunk + if(y_scroll_length <= 0 && verticalScroll.is_visible()) verticalScroll.hide(); - else if(y_scroll_length != 0 && !verticalScroll.is_visible()) + else if(y_scroll_length > 0 && !verticalScroll.is_visible()) verticalScroll.show(); +#endif } int @@ -220,7 +308,7 @@ bool TimelineWidget::on_motion_in_body_notify_event(GdkEventMotion *event) { REQUIRE(event != NULL); - ruler.set_mouse_chevron_offset(event->x); + ruler->set_mouse_chevron_offset(event->x); return true; } diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index 669060038..ca6be76ec 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -30,8 +30,10 @@ #include "timeline/header-container.hpp" #include "timeline/timeline-body.hpp" #include "timeline/timeline-ruler.hpp" +#include "timeline/timeline-tool.hpp" +#include "timeline/timeline-arrow-tool.hpp" +#include "timeline/timeline-ibeam-tool.hpp" #include "timeline/track.hpp" -#include "timeline/video-track.hpp" namespace lumiera { namespace gui { @@ -96,14 +98,30 @@ public: **/ void shift_view(int shift_size); + gavl_time_t get_selection_start() const; + + gavl_time_t get_selection_end() const; + + void set_selection(gavl_time_t start, gavl_time_t end); + + timeline::ToolType get_tool() const; + + void set_tool(timeline::ToolType tool_type); + /* ===== Events ===== */ protected: void on_scroll(); void on_size_allocate(Gtk::Allocation& allocation); + + /* ===== Utilities ===== */ +protected: + int time_to_x(gavl_time_t time) const; + + gavl_time_t x_to_time(int x) const; /* ===== Internals ===== */ -protected: +private: void update_tracks(); @@ -114,23 +132,29 @@ protected: bool on_motion_in_body_notify_event(GdkEventMotion *event); protected: + + // View State gavl_time_t timeOffset; int64_t timeScale; + + // Selection State + gavl_time_t selectionStart; + gavl_time_t selectionEnd; int totalHeight; - timeline::VideoTrack video1; - timeline::VideoTrack video2; + timeline::Track video1; + timeline::Track video2; std::vector tracks; timeline::HeaderContainer *headerContainer; timeline::TimelineBody *body; - timeline::TimelineRuler ruler; + timeline::TimelineRuler *ruler; Gtk::Adjustment horizontalAdjustment, verticalAdjustment; Gtk::HScrollbar horizontalScroll; Gtk::VScrollbar verticalScroll; - + /* ===== Constants ===== */ public: static const int64_t MaxScale; @@ -142,6 +166,10 @@ protected: friend class timeline::TimelineBody; friend class timeline::HeaderContainer; + friend class timeline::TimelineRuler; + friend class timeline::Tool; + friend class timeline::ArrowTool; + friend class timeline::IBeamTool; }; } // namespace widgets diff --git a/src/gui/widgets/timeline/header-container.cpp b/src/gui/widgets/timeline/header-container.cpp index 5e74da3eb..dfc085fc1 100644 --- a/src/gui/widgets/timeline/header-container.cpp +++ b/src/gui/widgets/timeline/header-container.cpp @@ -39,7 +39,9 @@ HeaderContainer::HeaderContainer(lumiera::gui::widgets::TimelineWidget *timeline { REQUIRE(timeline_widget != NULL); - set_flags(Gtk::NO_WINDOW); // This widget will not have a window at first + // This widget will not have a window at first + set_flags(Gtk::NO_WINDOW); + set_redraw_on_allocate(false); // Connect to the timeline widget's vertical scroll event, diff --git a/src/gui/widgets/timeline/header-container.hpp b/src/gui/widgets/timeline/header-container.hpp index 69d13b953..3a6d43065 100644 --- a/src/gui/widgets/timeline/header-container.hpp +++ b/src/gui/widgets/timeline/header-container.hpp @@ -46,80 +46,80 @@ class Track; * associated with timeline tracks. */ class HeaderContainer : public Gtk::Container - { - public: - /** - * Constructor - * - * @param[in] timeline_widget A pointer to the owner timeline widget - */ - HeaderContainer(lumiera::gui::widgets::TimelineWidget *timeline_widget); - - /** - * Attaches the header all the header widgets of root - * tracks to this control. - * - * @note This must be called when the track list changes - * to synchronise the headers with the timeline body and - * the backend. - */ - void update_headers(); - - /* ===== Overrides ===== */ - private: - void on_realize(); - void on_unrealize(); +{ +public: + /** + * Constructor + * + * @param[in] timeline_widget A pointer to the owner timeline widget + */ + HeaderContainer(lumiera::gui::widgets::TimelineWidget *timeline_widget); - void on_size_allocate (Gtk::Allocation& allocation); - void on_size_request (Gtk::Requisition* requisition); - - void forall_vfunc(gboolean include_internals, GtkCallback callback, - gpointer callback_data); - - /* ===== Events ===== */ - private: - void on_scroll(); + /** + * Attaches the header all the header widgets of root + * tracks to this control. + * + * @note This must be called when the track list changes + * to synchronise the headers with the timeline body and + * the backend. + */ + void update_headers(); + + /* ===== Overrides ===== */ +private: + void on_realize(); + void on_unrealize(); + + void on_size_allocate (Gtk::Allocation& allocation); + void on_size_request (Gtk::Requisition* requisition); - /* ===== Internals ===== */ - private: + void forall_vfunc(gboolean include_internals, GtkCallback callback, + gpointer callback_data); - /** - * Moves all the header widgets to the correct position - * given scroll, stacking etc. - */ - void layout_headers(); + /* ===== Events ===== */ +private: + void on_scroll(); - private: + /* ===== Internals ===== */ +private: + + /** + * Moves all the header widgets to the correct position + * given scroll, stacking etc. + */ + void layout_headers(); - /** - * The owner TimelineWidget of which this class is a helper - */ - lumiera::gui::widgets::TimelineWidget *timelineWidget; +private: - /** - * A structure to represent a header widget and it's - * associated track - */ - struct RootHeader - { - Glib::RefPtr widget; - Track *track; - }; - - /** - * Contains a list of the root currently present on - * the timeline view - */ - std::vector< RootHeader > rootHeaders; + /** + * The owner TimelineWidget of which this class is a helper + */ + lumiera::gui::widgets::TimelineWidget *timelineWidget; - /** - * The widget's window object. - * - * @note This is needed for the sake of clipping when - * widgets are scrolled. - */ - Glib::RefPtr gdkWindow; + /** + * A structure to represent a header widget and it's + * associated track + */ + struct RootHeader + { + Glib::RefPtr widget; + Track *track; }; + + /** + * Contains a list of the root currently present on + * the timeline view + */ + std::vector< RootHeader > rootHeaders; + + /** + * The widget's window object. + * + * @note This is needed for the sake of clipping when + * widgets are scrolled. + */ + Glib::RefPtr gdkWindow; +}; } // namespace timeline } // namespace widgets diff --git a/src/gui/widgets/timeline/video-track.cpp b/src/gui/widgets/timeline/timeline-arrow-tool.cpp similarity index 62% rename from src/gui/widgets/timeline/video-track.cpp rename to src/gui/widgets/timeline/timeline-arrow-tool.cpp index 4242f0e3f..109d6190e 100644 --- a/src/gui/widgets/timeline/video-track.cpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.cpp @@ -1,5 +1,5 @@ /* - video-track.cpp - Implementation of the timeline video track object + timeline-arrow-tool.cpp - Implementation of the ArrowTool class Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -20,38 +20,48 @@ * *****************************************************/ -#include "video-track.hpp" - -using namespace Gtk; +#include "timeline-arrow-tool.hpp" namespace lumiera { namespace gui { namespace widgets { namespace timeline { -VideoTrack::VideoTrack() : - headerWidget("HeaderTest") - { +ArrowTool::ArrowTool(TimelineBody *timeline_body) : + Tool(timeline_body) +{ - } +} -Gtk::Widget& -VideoTrack::get_header_widget() - { - return headerWidget; - } +ToolType +ArrowTool::get_type() const +{ + return Arrow; +} -int -VideoTrack::get_height() - { - return 100; - } +Gdk::Cursor +ArrowTool::get_cursor() const +{ + return Gdk::Cursor(Gdk::ARROW); +} void -VideoTrack::draw_track(Cairo::RefPtr cairo) - { - - } +ArrowTool::on_button_press_event(GdkEventButton* event) +{ + Tool::on_button_press_event(event); +} + +void +ArrowTool::on_button_release_event(GdkEventButton* event) +{ + Tool::on_button_release_event(event); +} + +void +ArrowTool::on_motion_notify_event(GdkEventMotion *event) +{ + Tool::on_motion_notify_event(event); +} } // namespace timeline } // namespace widgets diff --git a/src/gui/widgets/timeline/video-track.hpp b/src/gui/widgets/timeline/timeline-arrow-tool.hpp similarity index 60% rename from src/gui/widgets/timeline/video-track.hpp rename to src/gui/widgets/timeline/timeline-arrow-tool.hpp index d72b4b1c9..b7bfb6138 100644 --- a/src/gui/widgets/timeline/video-track.hpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.hpp @@ -1,5 +1,5 @@ /* - video-track.hpp - Declaration of the timeline video track object + timeline-arrow-tool.hpp - Declaration of the ArrowTool class Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -19,40 +19,40 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/** @file video-track.hpp - ** This file contains the definition of video track object +/** @file timeline-arrow-tool.hpp + ** This file contains the definition of the arrow tool class */ -#ifndef VIDEO_TRACK_HPP -#define VIDEO_TRACK_HPP +#ifndef TIMELINE_ARROW_TOOL_HPP +#define TIMELINE_ARROW_TOOL_HPP -#include "track.hpp" +#include +#include "timeline-tool.hpp" namespace lumiera { namespace gui { namespace widgets { namespace timeline { -class VideoTrack : public Track - { - public: - VideoTrack(); +class ArrowTool : public Tool +{ +public: + ArrowTool(TimelineBody *timeline_body); - virtual Gtk::Widget& get_header_widget(); - - virtual int get_height(); - - virtual void draw_track(Cairo::RefPtr cairo); - - protected: - Gtk::Label headerWidget; - Gtk::Frame headerFrame; - }; + ToolType get_type() const; + +protected: + Gdk::Cursor get_cursor() const; + +protected: + void on_button_press_event(GdkEventButton* event); + void on_button_release_event(GdkEventButton* event); + void on_motion_notify_event(GdkEventMotion *event); +}; } // namespace timeline } // namespace widgets } // namespace gui } // namespace lumiera -#endif // VIDEO_TRACK_HPP - +#endif // TIMELINE_ARROW_TOOL_HPP diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index d2e711638..50da41348 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -27,6 +27,9 @@ #include "../timeline-widget.hpp" #include "../../window-manager.hpp" +#include "timeline-arrow-tool.hpp" +#include "timeline-ibeam-tool.hpp" + using namespace Gtk; using namespace std; using namespace lumiera::gui; @@ -38,8 +41,10 @@ namespace gui { namespace widgets { namespace timeline { -TimelineBody::TimelineBody(lumiera::gui::widgets::TimelineWidget *timeline_widget) : +TimelineBody::TimelineBody(lumiera::gui::widgets::TimelineWidget + *timeline_widget) : Glib::ObjectBase("TimelineBody"), + tool(NULL), dragType(None), mouseDownX(0), mouseDownY(0), @@ -61,7 +66,51 @@ TimelineBody::TimelineBody(lumiera::gui::widgets::TimelineWidget *timeline_widge "Track Background", "The background colour of timeline tracks", GDK_TYPE_COLOR, G_PARAM_READABLE)); +} +TimelineBody::~TimelineBody() +{ + REQUIRE(tool != NULL); + if(tool != NULL) + delete tool; +} + +ToolType +TimelineBody::get_tool() const +{ + REQUIRE(tool != NULL); + if(tool != NULL) + return tool->get_type(); + return lumiera::gui::widgets::timeline::None; +} + +void +TimelineBody::set_tool(timeline::ToolType tool_type) +{ + // Tidy up old tool + if(tool != NULL) + { + // Do we need to change tools? + if(tool->get_type() == tool_type) + return; + + delete tool; + } + + // Create the new tool + switch(tool_type) + { + case timeline::Arrow: + tool = new ArrowTool(this); + break; + + case timeline::IBeam: + tool = new IBeamTool(this); + break; + } + + // Apply the cursor if possible + tool->apply_cursor(); } void @@ -69,12 +118,15 @@ TimelineBody::on_realize() { Widget::on_realize(); - // We wish to receive all event notifications + // We wish to receive event notifications add_events( Gdk::POINTER_MOTION_MASK | Gdk::SCROLL_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK); + + // Apply the cursor if possible + tool->apply_cursor(); } void @@ -136,7 +188,12 @@ TimelineBody::on_button_press_event(GdkEventButton* event) default: dragType = None; break; - } + } + + // Forward the event to the tool + tool->on_button_press_event(event); + + return true; } bool @@ -144,11 +201,16 @@ 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) @@ -166,6 +228,9 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) } } + // 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; } @@ -173,6 +238,8 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) bool TimelineBody::on_expose_event(GdkEventExpose* event) { + Cairo::Matrix view_matrix; + REQUIRE(event != NULL); REQUIRE(timelineWidget != NULL); @@ -194,6 +261,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event) // Translate the view by the scroll distance cairo->translate(0, -get_vertical_offset()); + cairo->get_matrix(view_matrix); // Interate drawing each track BOOST_FOREACH( Track* track, timelineWidget->tracks ) @@ -215,8 +283,44 @@ TimelineBody::on_expose_event(GdkEventExpose* event) // Shift for the next track cairo->translate(0, height + TimelineWidget::TrackPadding); - } + } + + //----- Draw the selection -----// + const int start_x = timelineWidget->time_to_x( + timelineWidget->get_selection_start()); + const int end_x = timelineWidget->time_to_x( + timelineWidget->get_selection_end()); + cairo->set_matrix(view_matrix); + + // Draw the cover + if(end_x > 0 && start_x < allocation.get_width()) + { + cairo->set_source_rgba(1.0, 0, 0, 0.5); + cairo->rectangle(start_x + 0.5, 0, + end_x - start_x, allocation.get_height()); + cairo->fill(); + } + + cairo->set_source_rgb(1.0, 0, 0); + cairo->set_line_width(1); + + // Draw the start + if(start_x >= 0 && start_x < allocation.get_width()) + { + cairo->move_to(start_x + 0.5, 0); + cairo->line_to(start_x + 0.5, allocation.get_height()); + cairo->stroke_preserve(); + } + + // Draw the end + if(end_x >= 0 && end_x < allocation.get_width()) + { + cairo->move_to(end_x + 0.5, 0); + cairo->line_to(end_x + 0.5, allocation.get_height()); + cairo->stroke_preserve(); + } + return true; } diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 86ce40a47..cebe4d5e1 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -27,6 +27,7 @@ #define TIMELINE_BODY_HPP #include "../../gtk-lumiera.hpp" +#include "timeline-tool.hpp" namespace lumiera { namespace gui { @@ -40,6 +41,12 @@ class TimelineBody : public Gtk::DrawingArea { public: TimelineBody(lumiera::gui::widgets::TimelineWidget *timeline_widget); + + ~TimelineBody(); + + ToolType get_tool() const; + + void set_tool(ToolType tool_type); /* ===== Events ===== */ protected: @@ -76,15 +83,20 @@ private: Shift }; - // UI State Data - DragType dragType; + timeline::Tool *tool; double mouseDownX, mouseDownY; + + // Scroll State + DragType dragType; gavl_time_t beginShiftTimeOffset; - int beginShiftVerticalOffset; + int beginShiftVerticalOffset; GdkColor background; lumiera::gui::widgets::TimelineWidget *timelineWidget; + + friend class ArrowTool; + friend class IBeamTool; }; } // namespace timeline diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp new file mode 100644 index 000000000..1940a04b1 --- /dev/null +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -0,0 +1,101 @@ +/* + timeline-ibeam-tool.cpp - Implementation of the IBeamTool class + + Copyright (C) Lumiera.org + 2008, Joel Holdsworth + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +#include "timeline-ibeam-tool.hpp" +#include "../timeline-widget.hpp" + +namespace lumiera { +namespace gui { +namespace widgets { +namespace timeline { + +IBeamTool::IBeamTool(TimelineBody *timeline_body) : + Tool(timeline_body) +{ +} + +ToolType +IBeamTool::get_type() const +{ + return IBeam; +} + +Gdk::Cursor +IBeamTool::get_cursor() const +{ + return Gdk::Cursor(Gdk::XTERM); +} + +void +IBeamTool::on_button_press_event(GdkEventButton* event) +{ + Tool::on_button_press_event(event); + + REQUIRE(timelineBody != NULL); + lumiera::gui::widgets::TimelineWidget *timeline_widget = + timelineBody->timelineWidget; + REQUIRE(timeline_widget != NULL); + + if(event->button == 1) + { + drag_start_time = timeline_widget->x_to_time(event->x); + timeline_widget->set_selection(drag_start_time, drag_start_time); + } +} + +void +IBeamTool::on_button_release_event(GdkEventButton* event) +{ + Tool::on_button_release_event(event); + + if(event->button == 1) + set_leading_x(event->x); +} + +void +IBeamTool::on_motion_notify_event(GdkEventMotion *event) +{ + Tool::on_motion_notify_event(event); + + if(isDragging) + set_leading_x(event->x); +} + +void IBeamTool::set_leading_x(const int x) +{ + REQUIRE(timelineBody != NULL); + lumiera::gui::widgets::TimelineWidget *timeline_widget = + timelineBody->timelineWidget; + REQUIRE(timeline_widget != NULL); + + const gavl_time_t time = timeline_widget->x_to_time(x); + if(time > drag_start_time) + timeline_widget->set_selection(drag_start_time, time); + else + timeline_widget->set_selection(time, drag_start_time); +} + +} // namespace timeline +} // namespace widgets +} // namespace gui +} // namespace lumiera + diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp new file mode 100644 index 000000000..fa0021ce1 --- /dev/null +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp @@ -0,0 +1,66 @@ +/* + timeline-ibeam-tool.hpp - Declaration of the ArrowTool class + + Copyright (C) Lumiera.org + 2008, Joel Holdsworth + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +/** @file timeline-ibeam-tool.hpp + ** This file contains the definition of ibeam tool class + ** tool objects + */ + +#ifndef TIMELINE_IBEAM_TOOL_HPP +#define TIMELINE_IBEAM_TOOL_HPP + +#include +#include "timeline-tool.hpp" + +namespace lumiera { +namespace gui { +namespace widgets { +namespace timeline { + +class IBeamTool : public Tool +{ +public: + IBeamTool(TimelineBody *timeline_body); + + ToolType get_type() const; + +protected: + Gdk::Cursor get_cursor() const; + +protected: + void on_button_press_event(GdkEventButton* event); + void on_button_release_event(GdkEventButton* event); + void on_motion_notify_event(GdkEventMotion *event); + +private: + void set_leading_x(const int x); + +protected: + //----- Internals -----// + gavl_time_t drag_start_time; +}; + +} // namespace timeline +} // namespace widgets +} // namespace gui +} // namespace lumiera + +#endif // TIMELINE_IBEAM_TOOL_HPP diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index 44cf4e307..0b28472b9 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -23,6 +23,7 @@ #include #include "timeline-ruler.hpp" +#include "../timeline-widget.hpp" #include "../../window-manager.hpp" extern "C" { @@ -41,39 +42,24 @@ namespace gui { namespace widgets { namespace timeline { -TimelineRuler::TimelineRuler() : +TimelineRuler::TimelineRuler( + lumiera::gui::widgets::TimelineWidget *timeline_widget) : Glib::ObjectBase("TimelineRuler"), - timeOffset(-1), - timeScale(1), mouseChevronOffset(0), annotationHorzMargin(0), annotationVertMargin(0), majorTickHeight(0), minorLongTickHeight(0), minorShortTickHeight(0), - minDivisionWidth(100) -{ + minDivisionWidth(100), + timelineWidget(timeline_widget) +{ + REQUIRE(timelineWidget != NULL); + // Install style properties register_styles(); } -void -TimelineRuler::set_time_offset(gavl_time_t time_offset) -{ - timeOffset = time_offset; - rulerImage.clear(); - queue_draw(); -} - -void -TimelineRuler::set_time_scale(int64_t time_scale) -{ - REQUIRE(time_scale > 0); - timeScale = time_scale; - rulerImage.clear(); - queue_draw(); -} - void TimelineRuler::set_mouse_chevron_offset(int offset) { @@ -81,6 +67,13 @@ TimelineRuler::set_mouse_chevron_offset(int offset) queue_draw(); } +void +TimelineRuler::update_view() +{ + rulerImage.clear(); + queue_draw(); +} + void TimelineRuler::on_realize() { @@ -132,8 +125,9 @@ TimelineRuler::on_expose_event(GdkEventExpose* event) cairo->set_source(rulerImage, 0, 0); cairo->paint(); - // Draw the mouse chevron + // Draw the overlays draw_mouse_chevron(cairo, allocation); + draw_selection(cairo, allocation); return true; } @@ -168,11 +162,15 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation) void TimelineRuler::draw_ruler(Cairo::RefPtr cairo, - Gdk::Rectangle ruler_rect) + const Gdk::Rectangle ruler_rect) { REQUIRE(cairo); REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); + REQUIRE(timelineWidget != NULL); + + const gavl_time_t left_offset = timelineWidget->timeOffset; + const int64_t time_scale = timelineWidget->timeScale; // Preparation steps const int height = ruler_rect.get_height(); @@ -187,7 +185,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr cairo, cairo->clip(); // Make sure we don't have impossible zoom - if(timeScale <= 0) + if(time_scale <= 0) return; // Render ruler annotations @@ -196,16 +194,16 @@ TimelineRuler::draw_ruler(Cairo::RefPtr cairo, const gavl_time_t major_spacing = calculate_major_spacing(); const gavl_time_t minor_spacing = major_spacing / 10; - int64_t time_offset = timeOffset - timeOffset % major_spacing; - if(timeOffset < 0) + int64_t time_offset = left_offset - left_offset % major_spacing; + if(left_offset < 0) time_offset -= major_spacing; int x = 0; - const int64_t x_offset = timeOffset / timeScale; + const int64_t x_offset = left_offset / time_scale; do { - x = (int)(time_offset / timeScale - x_offset); + x = (int)(time_offset / time_scale - x_offset); cairo->set_line_width(1); @@ -241,7 +239,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr cairo, void TimelineRuler::draw_mouse_chevron(Cairo::RefPtr cairo, - Gdk::Rectangle ruler_rect) + const Gdk::Rectangle ruler_rect) { REQUIRE(cairo); REQUIRE(ruler_rect.get_width() > 0); @@ -258,19 +256,55 @@ TimelineRuler::draw_mouse_chevron(Cairo::RefPtr cairo, cairo->move_to(mouseChevronOffset + 0.5, ruler_rect.get_height()); - cairo->line_to(mouseChevronOffset + mouseChevronSize + 0.5, - ruler_rect.get_height() - mouseChevronSize); - cairo->line_to(mouseChevronOffset - mouseChevronSize + 0.5, - ruler_rect.get_height() - mouseChevronSize); + cairo->rel_line_to(-mouseChevronSize, -mouseChevronSize); + cairo->rel_line_to(2 * mouseChevronSize, 0); cairo->fill(); } +void +TimelineRuler::draw_selection(Cairo::RefPtr cairo, + const Gdk::Rectangle ruler_rect) +{ + REQUIRE(cairo); + REQUIRE(ruler_rect.get_width() > 0); + REQUIRE(ruler_rect.get_height() > 0); + REQUIRE(timelineWidget != NULL); + + Glib::RefPtr