diff --git a/.gitignore b/.gitignore index 10dbaee23..cf7ebb45d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,11 @@ *~ *.tar.* .[^.]* +*.o *.os *.gch ,valgrind.log* -admin/scons/*.pyc +*.pyc optcache Makefile.in build/* @@ -13,5 +14,8 @@ bin/* autom4te.cache/* scripts/* configure +config.log aclocal.m4 semantic.cache +wiki/backups/* +doc/devel/draw/*.png diff --git a/SConstruct b/SConstruct index 173803853..5b2980130 100644 --- a/SConstruct +++ b/SConstruct @@ -37,6 +37,7 @@ BINDIR = 'bin' TESTDIR = 'tests' ICONDIR = 'icons' VERSION = '0.1+pre.01' +SVGRENDERER = 'admin/render-icon' #-----------------------------------Configuration # NOTE: scons -h for help. @@ -68,7 +69,7 @@ def setupBasicEnvironment(): , CCFLAGS='-Wall ' # -fdiagnostics-show-option ) - RegisterPrecompiledHeader_Builder(env) + RegisterIcon_Builder(env,SVGRENDERER) handleNoBugSwitches(env) env.Append(CPPDEFINES = '_GNU_SOURCE') @@ -225,6 +226,10 @@ def configurePlatform(env): if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): print 'Unable to configure GTK--, exiting.' Exit(1) + + if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): + print 'Unable to configure Lib glib--, exiting.' + Exit(1) if not conf.CheckPkgConfig('cairomm-1.0', 0.6): print 'Unable to configure Cairo--, exiting.' @@ -234,6 +239,10 @@ def configurePlatform(env): print 'Unable to configure the GNOME DevTool Library, exiting.' Exit(1) + if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): + print 'Need rsvg Library for rendering icons.' + Exit(1) + if not conf.CheckPkgConfig('xv'): Exit(1) # if not conf.CheckPkgConfig('xext'): Exit(1) # if not conf.CheckPkgConfig('sm'): Exit(1) @@ -281,26 +290,29 @@ def defineBuildTargets(env, artifacts): + env.StaticLibrary('$BINDIR/lumi.la', objlib) ) - # use PCH to speed up building - precomp = ( env.PrecompiledHeader('$SRCDIR/pre') - ) - env.Depends(objproc, precomp) - env.Depends(objlib, precomp) artifacts['lumiera'] = env.Program('$BINDIR/lumiera', ['$SRCDIR/main.cpp']+ core ) artifacts['plugins'] = env.SharedLibrary('$BINDIR/lumiera-plugin', objplug) # the Lumiera GTK GUI - envgtk = env.Clone().mergeConf(['gtkmm-2.4','cairomm-1.0','gdl-1.0','xv','xext','sm']) + envgtk = env.Clone().mergeConf(['gtkmm-2.4','cairomm-1.0','gdl-1.0','librsvg-2.0','xv','xext','sm']) objgui = srcSubtree(envgtk,'$SRCDIR/gui') + # render and install Icons + vector_icon_dir = env.subst('$ICONDIR/svg') + prerendered_icon_dir = env.subst('$ICONDIR/prerendered') + artifacts['icons'] = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconCopy(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + artifacts['lumigui'] = ( envgtk.Program('$BINDIR/lumigui', objgui + core) - + env.Install('$BINDIR', env.Glob('$ICONDIR/*.png')) + env.Install('$BINDIR', env.Glob('$SRCDIR/gui/*.rc')) + + artifacts['icons'] ) # call subdir SConscript(s) for independent components SConscript(dirs=[SRCDIR+'/tool'], exports='env artifacts core') + SConscript(dirs=['admin'], exports='env envgtk artifacts core') SConscript(dirs=[TESTDIR], exports='env artifacts core') diff --git a/admin/SConscript b/admin/SConscript new file mode 100644 index 000000000..d0ad7b84e --- /dev/null +++ b/admin/SConscript @@ -0,0 +1,17 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for helper tools (called by SConstruct) +## + +Import('env','envgtk','artifacts','core') + +vgsuppr = env.Program('#$BINDIR/vgsuppression',['vgsuppression.c']+core) +rsvg = envgtk.Program('#$BINDIR/rsvg-convert','rsvg-convert.c') + +artifacts['tools'] += [ vgsuppr ## for supressing false valgrind alarms + + rsvg ## for rendering SVG icons (uses librsvg) + ] + +# Rendering the SVG Icons depends on rsvg-convert +env.Depends(artifacts['icons'], rsvg) + diff --git a/admin/render-icon.py b/admin/render-icon.py new file mode 100755 index 000000000..26bdb3653 --- /dev/null +++ b/admin/render-icon.py @@ -0,0 +1,178 @@ +#!/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 + +#svgDir = "svg" +#prerenderedDir = "prerendered" +inkscapePath = "/usr/bin/inkscape" +rsvgPath = "./rsvg-convert" +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 "Parsing " + 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]) + + if not os.path.exists(rsvgPath): + print "Error: executable %s not found." % rsvgPath + + os.spawnlp(os.P_WAIT, rsvgPath, rsvgPath, + "--source-rect=%g:%g:%g:%g" % (rectangle[0], rectangle[1], rectangle[2], rectangle[3]), + "--output=" + os.path.join(out_dir, "%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name)), + file_path) + +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 getTargetNames(file_path): + """get a list of target names to be rendered from the given source SVG + usable to setup the build targets for SCons + """ + artwork_name, _ , rectangles = parseSVG(file_path) + return ["%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name) for rectangle in rectangles ] + +#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 SRCFILE.svg TARGETDIR" + print "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/admin/rsvg-convert.c b/admin/rsvg-convert.c new file mode 100644 index 000000000..7ed5a9910 --- /dev/null +++ b/admin/rsvg-convert.c @@ -0,0 +1,219 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- + + rsvg-convert.c: Command line utility for exercising rsvg with cairo. + + Copyright (C) 2005 Red Hat, Inc. + Copyright (C) 2005 Dom Lachowicz + Copyright (C) 2005 Caleb Moore + Copyright (C) 2008 Joel Holdsworth + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Carl Worth , + Caleb Moore , + Dom Lachowicz , + Joel Holdsworth +*/ + +#ifndef N_ +#define N_(X) X +#endif + +#include +#include +#include + +#include +#include + +#ifdef CAIRO_HAS_PS_SURFACE +#include +#endif + +#ifdef CAIRO_HAS_PDF_SURFACE +#include +#endif + +#ifdef CAIRO_HAS_SVG_SURFACE +#include +#endif + +#ifndef _ +#define _(X) X +#endif + +struct RsvgSizeCallbackData { + gint width; + gint height; +}; + +struct RsvgSourceRectangle { + double left; + double top; + double width; + double height; +}; + +static void +display_error (GError * err) +{ + if (err) { + g_print ("%s", err->message); + g_error_free (err); + } +} + +static void +rsvg_cairo_size_callback (int *width, int *height, gpointer data) +{ + RsvgDimensionData *dimensions = data; + *width = dimensions->width; + *height = dimensions->height; +} + +static cairo_status_t +rsvg_cairo_write_func (void *closure, const unsigned char *data, unsigned int length) +{ + fwrite (data, 1, length, (FILE *) closure); + return CAIRO_STATUS_SUCCESS; +} + +int +main (int argc, char **argv) +{ + GOptionContext *g_option_context; + int width = -1; + int height = -1; + char *source_rect_string = NULL; + char *output = NULL; + GError *error = NULL; + char *filename = NULL; + + char **args = NULL; + RsvgHandle *rsvg; + cairo_surface_t *surface = NULL; + cairo_t *cr = NULL; + RsvgDimensionData dimensions; + FILE *output_file = stdout; + + struct RsvgSourceRectangle source_rect = {0, 0, 0, 0}; + + GOptionEntry options_table[] = { + {"width", 'w', 0, G_OPTION_ARG_INT, &width, + N_("width [optional; defaults to the SVG's width]"), N_("")}, + {"height", 'h', 0, G_OPTION_ARG_INT, &height, + N_("height [optional; defaults to the SVG's height]"), N_("")}, + {"source-rect", 'r', 0, G_OPTION_ARG_STRING, &source_rect_string, + N_("source rectangle [optional; defaults to rectangle of the SVG document]"), N_("left:top:width:height")}, + {"output", 'o', 0, G_OPTION_ARG_STRING, &output, + N_("output filename"), NULL}, + {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("FILE")}, + {NULL} + }; + + g_thread_init(NULL); + + g_option_context = g_option_context_new (_("- SVG Converter")); + g_option_context_add_main_entries (g_option_context, options_table, NULL); + g_option_context_set_help_enabled (g_option_context, TRUE); + if (!g_option_context_parse (g_option_context, &argc, &argv, &error)) { + display_error (error); + exit (1); + } + + g_option_context_free (g_option_context); + + if (output != NULL) { + output_file = fopen (output, "wb"); + if (!output_file) { + fprintf (stderr, _("Error saving to file: %s\n"), output); + exit (1); + } + } + + if (args[0] != NULL) { + filename = args[0]; + } + + /* Parse the source rect */ + if(source_rect_string != NULL) { + const int n = sscanf(source_rect_string, "%lg:%lg:%lg:%lg", + &source_rect.left, &source_rect.top, + &source_rect.width, &source_rect.height); + if(n != 4 || source_rect.width <= 0.0 || source_rect.height < 0.0) { + fprintf (stderr, _("Invalid source rect: %s\n"), source_rect_string); + exit(1); + } + } + + rsvg_init (); + + rsvg = rsvg_handle_new_from_file (filename, &error); + + if (!rsvg) { + fprintf (stderr, _("Error reading SVG:")); + display_error (error); + fprintf (stderr, "\n"); + exit (1); + } + + /* if the user did not specify a source rectangle, get the page size from the SVG */ + if(source_rect_string == NULL) { + rsvg_handle_set_size_callback (rsvg, rsvg_cairo_size_callback, &dimensions, NULL); + source_rect.left = 0; + source_rect.top = 0; + source_rect.width = dimensions.width; + source_rect.height = dimensions.height; + } + + rsvg_handle_get_dimensions (rsvg, &dimensions); + + if(width != -1 && height != -1) { + dimensions.width = width; + dimensions.height = height; + } else if(source_rect_string != NULL) { + dimensions.width = source_rect.width; + dimensions.height = source_rect.height; + } + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + dimensions.width, dimensions.height); + + cr = cairo_create (surface); + + cairo_translate(cr, -source_rect.left, -source_rect.top); + + if(width != -1 && height != -1 && source_rect_string != NULL) { + cairo_scale(cr, (double)dimensions.width / (double)source_rect.width, + (double)dimensions.height / (double)source_rect.height); + } + + rsvg_handle_render_cairo (rsvg, cr); + + cairo_surface_write_to_png_stream (surface, rsvg_cairo_write_func, output_file); + + g_object_unref (G_OBJECT (rsvg)); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + + fclose (output_file); + + rsvg_term (); + + return 0; +} + diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 02f17b2b4..38940424c 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -28,6 +28,8 @@ import fnmatch import re import tarfile +from SCons.Action import Action + # @@ -58,13 +60,13 @@ def srcSubtree(env,tree,isShared=False,builder=None, **args): else: builder = lambda f: env.Object(f, **args) - return [builder(f) for f in scanSrcSubtree(root)] + return [builder(f) for f in scanSubtree(root)] SRCPATTERNS = ['*.c','*.cpp','*.cc'] -def scanSrcSubtree(roots): +def scanSubtree(roots, patterns=SRCPATTERNS): """ first expand (possible) wildcards and filter out non-dirs. Then scan the given subtree for source filesnames (python generator function) @@ -73,7 +75,7 @@ def scanSrcSubtree(roots): for (dir,_,files) in os.walk(root): if dir.startswith('./'): dir = dir[2:] - for p in SRCPATTERNS: + for p in patterns: for f in fnmatch.filter(files, p): yield os.path.join(dir,f) @@ -102,22 +104,50 @@ def filterNodes(nlist, removeName=None): return filter(predicate, nlist) -def RegisterPrecompiledHeader_Builder(env): - """ Registeres an Custom Builder for generating a precompiled Header. - Note you should define a dependency to the PCH file - """ - def genCmdline(source, target, env, for_signature): - return '$CXXCOM -x c++-header %s' % source[0] - def fixSourceDependency(target, source, env): - print "precompiled header: %s --> %s" % (source[0],target[0]) - return (target, source) + +def getDirname(dir): + """ extract directory name without leading path """ + dir = os.path.realpath(dir) + if not os.path.isdir(dir): + dir,_ = os.path.split(dir) + _, name = os.path.split(dir) + return name - gchBuilder = env.Builder( generator = genCmdline - , emitter = fixSourceDependency - , suffix = '.gch' - , src_suffix = '.hpp' - ) - env.Append(BUILDERS = {'PrecompiledHeader' : gchBuilder}) + + +def RegisterIcon_Builder(env, renderer): + """ Registeres Custom Builders for generating and installing Icons. + Additionally you need to build the tool (rsvg-convert.c) + used to generate png from the svg source using librsvg. + """ + renderer = __import__(renderer) # load python script for invoking the render + renderer.rsvgPath = env.subst("$BINDIR/rsvg-convert") + + def invokeRenderer(target, source, env): + source = str(source[0]) + targetdir = env.subst("$BINDIR") + renderer.main([source,targetdir]) + return 0 + + def createIconTargets(target,source,env): + """ parse the SVG to get the target file names """ + source = str(source[0]) + targetdir = os.path.basename(str(target[0])) + targetfiles = renderer.getTargetNames(source) # parse SVG + return (["$BINDIR/%s" % name for name in targetfiles], source) + + def IconCopy(env, source): + """Copy icon to corresponding icon dir. """ + subdir = getDirname(source) + return env.Install("$BINDIR/%s" % subdir, source) + + + buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS") + , single_source = True + , emitter = createIconTargets + ) + env.Append(BUILDERS = {'IconRender' : buildIcon}) + env.AddMethod(IconCopy) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 82d419a65..00c217351 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -81,6 +81,12 @@ class LumieraEnvironment(Environment): """ pattern = self.subst(pattern) return glob.glob(pattern) + + def AddMethod (self, function): + """ temporary workaround; newer versions of SCons provide this as a global function """ + self.__dict__[function.__name__] = function.__get__(self) + + class LumieraConfigContext(SConf): diff --git a/icons/prerendered/16x16/panel-timeline.png b/icons/prerendered/16x16/panel-timeline.png new file mode 100644 index 000000000..e61a0aea1 Binary files /dev/null and b/icons/prerendered/16x16/panel-timeline.png differ diff --git a/icons/prerendered/32x32/panel-viewer.png b/icons/prerendered/32x32/panel-viewer.png new file mode 100644 index 000000000..b449f37a5 Binary files /dev/null and b/icons/prerendered/32x32/panel-viewer.png differ diff --git a/icons/svg/tool-arrow.svg b/icons/svg/tool-arrow.svg new file mode 100644 index 000000000..026b6633f --- /dev/null +++ b/icons/svg/tool-arrow.svg @@ -0,0 +1,732 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/lumiera_ui.rc b/src/gui/lumiera_ui.rc index 428b7bed4..02f6d90fc 100644 --- a/src/gui/lumiera_ui.rc +++ b/src/gui/lumiera_ui.rc @@ -124,6 +124,8 @@ class "GtkProgressBar" style:highest "lumiera_progressbars" style "timeline_body" { gtkmm__CustomObject_TimelineBody::background = "#7E838B" + gtkmm__CustomObject_TimelineBody::selection = "#2D2D90" + gtkmm__CustomObject_TimelineBody::selection_alpha = 0.5 } style "timeline_ruler" = "default_base" @@ -137,16 +139,16 @@ style "timeline_ruler" = "default_base" gtkmm__CustomObject_TimelineRuler::annotation_vert_margin = 0 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"