From f2c9e67fbc50208dd12bbba2ac06bcab0e5b33fc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 5 Sep 2008 05:54:14 +0200 Subject: [PATCH 001/140] merge generating icons from SVG source --- .gitignore | 6 +- SConstruct | 28 +- admin/SConscript | 17 + admin/render-icon.py | 178 +++++ admin/rsvg-convert.c | 219 ++++++ admin/scons/Buildhelper.py | 66 +- admin/scons/LumieraEnvironment.py | 6 + icons/prerendered/16x16/panel-timeline.png | Bin 0 -> 453 bytes icons/prerendered/32x32/panel-viewer.png | Bin 0 -> 1502 bytes icons/svg/tool-arrow.svg | 732 +++++++++++++++++++++ src/gui/lumiera_ui.rc | 14 +- 11 files changed, 1233 insertions(+), 33 deletions(-) create mode 100644 admin/SConscript create mode 100755 admin/render-icon.py create mode 100644 admin/rsvg-convert.c create mode 100644 icons/prerendered/16x16/panel-timeline.png create mode 100644 icons/prerendered/32x32/panel-viewer.png create mode 100644 icons/svg/tool-arrow.svg 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 0000000000000000000000000000000000000000..e61a0aea10b398eb7984c86ccb7a839352584f78 GIT binary patch literal 453 zcmV;$0XqJPP){Yk18DncoxhzKH~6mP)nyA^Mso8Evc;XCIfGp`F1ja@}_lBdGc_)&)a28%_8LvId|yioI3yv%ZovidIA`h7Z=krDQ9v| z{lZ~5>zj?LBtCpvlO*1)zLg~2EWMT_7GJ%TBoZPe8OMM1(lb3{59@fwj_pkrmQCVaHb{{mgdom*2N01s zk`eMXX$AYlTiTznj;BzR7avw6-LUf8_G)2r*;yLXQeLSu%2 zh%{<(tV9Cc-T?0%N#dt~CY6tm6`IQ{^m<+8y}vX=Kx^Ho zR3Z~3aCkVvD9AEDp}tf(-j*K8GDoM=quV{i``jZ+DXfj~KJef{hkt+kDRCSdV15Xq zO2vi{I6cj%$JvDHNx;(0f^(iPzdWSZJ0t`_DTB2JP$&hUa4xX6{xnOQOWghN@76gt zOTc^Ih$3r6@Sa{}H82fYLC-~A`c&1E|`3ANTO z8%4RhL%=x-(CPRKX`0dP9+4y?Kx3^w1J2v=_gp|FUf`8iU*@yz9h6e783G_nk+B2` zg=+)lX;!-5`@r$>fRmFUB4BN0M(uP9;9Q{JcQlthQ52!IUYe5wEZN8sLcqCzVsc9* z$%uY`NC*LAq6vlb+&*bj3{Fyf2oxBz1iW{bjIjit6VNK(Qln8yl4OVkv^E%H3T5-v z&L@?7K)lD=2ne&=(ED)78pFtEj1o^W9OAv8ltOERQuZ-wrwL4sS*tiYOzPh>3;!tA%eIak-l(FnHWM%TyNCac$NvL14jrR zYb;ro%^~1ij8dejJ6s-ovkKtX-Y~EKk4jV^~DEy#7pnIF(ygrhZ$_9s7#z?(SlYKAj=py?_08@BaRs zG))`-YJOJ&%bcv`L}9uZR6_KuU6}|+T2UyU@*wG zwzsonn6y9q+ufhF+wC3TbgT{lKR##v?Gp@80jfat`i;$Zzy0#JuH5*>b*+?QYioP% zq!+(=GkWE_--+(uyMIk<{hM~X{X-xW)dQ{|av=d-)=H&PG1gpOTVL147_`zn_uRF! zn8Nw?6A@hIXsxbjrPVWq5Hr5sJf)8+7O>uX-E6gv_PgCzjkTs;Ur;N}6`IW!&1Q?` zrO=FRIbUHR&j zpH$=8_v-c9VtrvDRI615FiH{`3H6|^8f$< literal 0 HcmV?d00001 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" From 84052f1fabf1a65819afcc449567aad49a7b747b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 5 Sep 2008 07:06:46 +0200 Subject: [PATCH 002/140] add placeholder gui main to make the compile work --- admin/vgsuppression.c | 5 +- src/gui/gtk-lumiera.cpp | 214 ++++++++++++++++++++++++++++++++++++++++ src/gui/gtk-lumiera.hpp | 163 ++++++++++++++++++++++++++++++ 3 files changed, 378 insertions(+), 4 deletions(-) create mode 100644 src/gui/gtk-lumiera.cpp create mode 100644 src/gui/gtk-lumiera.hpp diff --git a/admin/vgsuppression.c b/admin/vgsuppression.c index cad026e52..514be9ecb 100644 --- a/admin/vgsuppression.c +++ b/admin/vgsuppression.c @@ -23,15 +23,12 @@ just place any problematic calls where valgrind whines about in main (with comments please) */ -#include "lib/safeclib.h" int main () { - /* debian etch glibc is lazy about cleaning up TLS */ - lumiera_tmpbuf_provide (100); - lumiera_tmpbuf_freeall (); + /* dummy placeholder */ return 0; } diff --git a/src/gui/gtk-lumiera.cpp b/src/gui/gtk-lumiera.cpp new file mode 100644 index 000000000..1b7676dd2 --- /dev/null +++ b/src/gui/gtk-lumiera.cpp @@ -0,0 +1,214 @@ +/* + gtk-lumiera.cpp - simple placeholder to make the compile work + +* *****************************************************/ + +#include +#include + +#include + +using namespace Gtk; + + +#ifdef ENABLE_NLS +# include +#endif + +#include +#include +#include +#include +#include + +#include "gtk-lumiera.hpp" + +NOBUG_CPP_DEFINE_FLAG(gui); + +using namespace Gtk; +using namespace Glib; +using namespace sigc; +using namespace lumiera::gui; + +GtkLumiera the_application; + +int +main (int argc, char *argv[]) +{ + return the_application.main(argc, argv); +} + + + +namespace lumiera { +namespace gui { + +int +GtkLumiera::main(int argc, char *argv[]) +{ + NOBUG_INIT; + + Main kit(argc, argv); + + Glib::set_application_name(AppTitle); + + workspace::WorkspaceWindow main_window; + + kit.run(main_window); + + return 23; +} + + +GtkLumiera& +application() +{ + return the_application; +} + + +namespace workspace { + +WorkspaceWindow::WorkspaceWindow() +: actions(*this) +{ + + layout = NULL; + create_ui(); +} + +WorkspaceWindow::~WorkspaceWindow() +{ + REQUIRE(layout != NULL); + g_object_unref(layout); +} + + +void +WorkspaceWindow::create_ui() +{ + //----- Configure the Window -----// + set_title(AppTitle); + set_default_size(1024, 768); + + //----- Set up the UI Manager -----// + // The UI will be nested within a VBox + add(baseContainer); + + uiManager = Gtk::UIManager::create(); + uiManager->insert_action_group(actions.actionGroup); + + //Layout the actions in a menubar and toolbar: + Glib::ustring ui_info = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + + try + { + uiManager->add_ui_from_string(ui_info); + } + catch(const Glib::Error& ex) + { + ERROR(gui, "Building menus failed: %s", ex.what().data()); + return; + } + + //----- Set up the Menu Bar -----// + Gtk::Widget* menu_bar = uiManager->get_widget("/MenuBar"); + ASSERT(menu_bar != NULL); + baseContainer.pack_start(*menu_bar, Gtk::PACK_SHRINK); + + //----- Set up the Tool Bar -----// + Gtk::Toolbar* toolbar = dynamic_cast( + uiManager->get_widget("/ToolBar")); + ASSERT(toolbar != NULL); + toolbar->set_toolbar_style(TOOLBAR_ICONS); + baseContainer.pack_start(*toolbar, Gtk::PACK_SHRINK); + + + //----- Create the Dock -----// + dock = Glib::wrap(gdl_dock_new()); + layout = gdl_dock_layout_new((GdlDock*)dock->gobj()); + + dockbar = Glib::wrap(gdl_dock_bar_new ((GdlDock*)dock->gobj())); + + dockContainer.pack_start(*dockbar, PACK_SHRINK); + dockContainer.pack_end(*dock, PACK_EXPAND_WIDGET); + baseContainer.pack_start(dockContainer, PACK_EXPAND_WIDGET); + + //----- Create the status bar -----// + statusBar.set_has_resize_grip(); + baseContainer.pack_start(statusBar, PACK_SHRINK); + + show_all_children(); + + +} + + + /* -- Actions -- */ + + +Actions::Actions(WorkspaceWindow &workspace_window) : + workspaceWindow(workspace_window), + is_updating_action_state(false) +{ + workspace_window.signal_show ().connect_notify(mem_fun(this, &Actions::update_action_state)); + + //----- Create the Action Group -----// + actionGroup = ActionGroup::create(); + + // File menu + actionGroup->add(Action::create("FileMenu", _("_File"))); + actionGroup->add(Action::create("FileQuit", Stock::QUIT), + sigc::mem_fun(*this, &Actions::on_menu_file_quit)); + + // Help Menu + actionGroup->add(Action::create("HelpMenu", _("_Help")) ); + actionGroup->add(Action::create("HelpAbout", _("say hello...")), + sigc::mem_fun(*this, &Actions::on_menu_help_about) ); +} + +void +Actions::update_action_state() +{ + is_updating_action_state = true; + is_updating_action_state = false; +} + +/* ===== File Menu Event Handlers ===== */ + +void +Actions::on_menu_file_quit() +{ + workspaceWindow.hide(); // Closes the main window to stop the Gtk::Main::run(). +} + +/* ===== Help Menu Event Handlers ===== */ + +void +Actions::on_menu_help_about() +{ + g_message("Hello Lumi World"); +} + + + +} // namespace workspace + + + +} // namespace gui +} // namespace lumiera + + diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp new file mode 100644 index 000000000..3a20b7731 --- /dev/null +++ b/src/gui/gtk-lumiera.hpp @@ -0,0 +1,163 @@ +/* + gtk-lumiera.cpp - simple placeholder to make the compile work + +* *****************************************************/ + + +#ifndef GTK_LUMIERA_HPP +#define GTK_LUMIERA_HPP + +#include +#include +#include + +#include + +extern "C" { +#include +} + +NOBUG_DECLARE_FLAG(gui); + +#ifdef ENABLE_NLS +# include +# define _(String) gettext (String) +# define gettext_noop(String) String +# define N_(String) gettext_noop (String) +#else +# define _(String) (String) +# define N_(String) String +# define textdomain(Domain) +# define bindtextdomain(Package, Directory) +#endif + +namespace lumiera { + +/** + * The namespace of all GUI code. + */ +namespace gui { + +/* ===== Global Constants ===== */ + +/** + * The name of the application + */ +static const gchar* AppTitle = "Lumiera"; + + + +/* ===== The Application Class ===== */ + +/** + * The main application class. + */ +class GtkLumiera +{ +public: + int main(int argc, char *argv[]); + + +}; + +/** + * Returns a reference to the global application object + */ +GtkLumiera& application(); + + + +namespace workspace { + +class WorkspaceWindow; + +/** +* A helper class which registers and handles +* user action events. +*/ +class Actions +{ +private: + Actions(WorkspaceWindow &workspace_window); + + /* ===== Internals ===== */ +private: + /** + * Updates the state of the menu/toolbar actions + * to reflect the current state of the workspace */ + void update_action_state(); + + /** A reference to the MainWindow */ + WorkspaceWindow &workspaceWindow; + + /* ===== Event Handlers ===== */ +private: + void on_menu_file_quit(); + void on_menu_help_about(); + + // Temporary Junk + void on_menu_others(); + + + /* ===== Actions ===== */ +private: + Glib::RefPtr actionGroup; + + /* ===== Internals ===== */ +private: + bool is_updating_action_state; + + friend class WorkspaceWindow; +}; + + + +/** +* A Mock main window +*/ +class WorkspaceWindow : public Gtk::Window +{ +public: + WorkspaceWindow(); + + ~WorkspaceWindow(); + +private: + void create_ui(); + + + /* ===== UI ===== */ +private: + Glib::RefPtr uiManager; + Gtk::VBox baseContainer; + Gtk::HBox dockContainer; + + //----- Dock Frame -----// + Gtk::Widget *dock; + Gtk::Widget *dockbar; + GdlDockLayout *layout; + + //----- Status Bar -----// + Gtk::Statusbar statusBar; + + /** instance of the actions helper class, which + * registers and handles user action events */ + Actions actions; + + friend class Actions; + +}; + + + + + +} // namespace workspace + + +} // namespace gui +} // namespace lumiera + +#endif // GTK_LUMIERA_HPP + + From 4286c860886dcf3d1023ec0abcf4a4d5fc287cbc Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 18 Oct 2008 05:29:35 +0200 Subject: [PATCH 003/140] merge after landing config system --- SConstruct | 2 +- icons/svg/tool-arrow.svg | 237 +++++++++++++++++++++++++-------------- src/gui/gtk-lumiera.cpp | 4 +- src/gui/gtk-lumiera.hpp | 3 - src/gui/lumiera_ui.rc | 25 +++-- src/proc/lumiera.hpp | 73 +++++++++++- src/tool/SConscript | 1 + src/tool/luidgen.c | 51 +++++++++ tests/SConscript | 20 ++-- tests/test.h | 2 +- tests/test.sh | 2 +- 11 files changed, 306 insertions(+), 114 deletions(-) create mode 100644 src/tool/luidgen.c diff --git a/SConstruct b/SConstruct index 9660003ef..07b791cb0 100644 --- a/SConstruct +++ b/SConstruct @@ -293,7 +293,7 @@ def defineBuildTargets(env, artifacts): objplug = srcSubtree(env,'$SRCDIR/plugin', isShared=True) core = ( env.StaticLibrary('$BINDIR/lumiback.la', objback) + env.StaticLibrary('$BINDIR/lumiproc.la', objproc) - + env.StaticLibrary('$BINDIR/lumi.la', objlib) + + env.StaticLibrary('$BINDIR/lumiera.la', objlib) ) diff --git a/icons/svg/tool-arrow.svg b/icons/svg/tool-arrow.svg index 026b6633f..3f0b72102 100644 --- a/icons/svg/tool-arrow.svg +++ b/icons/svg/tool-arrow.svg @@ -510,6 +510,89 @@ y1="-129.52815" x2="252.00447" y2="-135.47408" /> + + + + + + + + - - - - - + + + - - - - - - + + + + - - - - - - - + + + + + + + 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 "lib/luid.h" + +#include + +/** + * @file + * Generate amd print a Lumiera uid as octal escaped string + */ + +int +main (int argc, char** argv) +{ + lumiera_uid luid; + lumiera_uid_gen (&luid); + + printf ("\""); + for (int i = 0; i < 16; ++i) + printf ("\\%.3hho", *(((char*)&luid)+i)); + printf ("\"\n"); + + return 0; +} + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/tests/SConscript b/tests/SConscript index b817cdece..d47b9d943 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -36,26 +36,32 @@ def testCollection(env,dir): """ treat a Directory containing a collection of standalone tests. Link each of them into an independent executable """ + srcpatt = ['test-*.c'] exeName = lambda p: path.basename(path.splitext(p)[0]) buildIt = lambda p: env.Program("#$BINDIR/"+exeName(p), [p] + core) - return [buildIt(f) for f in scanSubtree(dir)] + return [buildIt(f) for f in scanSubtree(dir,srcpatt)] def treatPluginTestcase(env): """ Special case: the test-plugin executable """ + tree = 'backend' env = env.Clone() - env.Append(CPPPATH='plugin') - prfx = 'plugin/example_plugin' + env.Append(CPPPATH=tree) + prfx = path.join(tree,'example_plugin') oC = env.SharedObject(prfx, prfx+'.c') oCPP = env.SharedObject(prfx+'_cpp', prfx+'.cpp') testplugin = ( env.SharedLibrary('#$BINDIR/.libs/example_plugin', oC, SHLIBPREFIX='') + env.SharedLibrary('#$BINDIR/.libs/example_plugin_cpp', oCPP, SHLIBPREFIX='') ) - testExe = env.Program('#$BINDIR/test-plugin', ['plugin/plugin_main.c'] + core) - env.Depends(testExe, testplugin) - return testExe - #-- it depentds (at the moment) on a specific isolated test-plugin, +# testExe = env.Program('#$BINDIR/test-plugin', ['plugin/plugin_main.c'] + core) +# env.Depends(testExe, testplugin) +# return testExe + + # 10/2008 example_plugin moved to backend directory. + # ...we should try to find some convention here + return testplugin + #-- it depends (at the moment) on a specific isolated test-plugin, # which is not integrated in the "normal procedure" for building Plugins # (which is not yet implemented as of 8/07) # TODO: handle this case automatically diff --git a/tests/test.h b/tests/test.h index 060d007fc..987892956 100644 --- a/tests/test.h +++ b/tests/test.h @@ -33,7 +33,7 @@ LUMIERA_ERROR_DEFINE (TEST, "test error"); #define TESTS_BEGIN \ int \ -main (int argc, char** argv) \ +main (int argc, const char** argv) \ { \ NOBUG_INIT; \ NOBUG_INIT_FLAG (tests); \ diff --git a/tests/test.sh b/tests/test.sh index d919ed073..e2d5159b7 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -30,7 +30,7 @@ export LC_ALL=C arg0="$0" srcdir="$(dirname "$arg0")" -ulimit -S -t 1 -v 524288 +ulimit -S -t 2 -v 524288 valgrind="" if [ "$VALGRINDFLAGS" = 'DISABLE' ]; then echo "valgrind explicit disabled" From 9ff04e676937bca3d092daa78857106f11dfb070 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 18 Oct 2008 05:34:33 +0200 Subject: [PATCH 004/140] (fix) luidgen needs lib/luid.h --- src/lib/luid.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib/luid.h | 97 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 src/lib/luid.c create mode 100644 src/lib/luid.h diff --git a/src/lib/luid.c b/src/lib/luid.c new file mode 100644 index 000000000..c8d766094 --- /dev/null +++ b/src/lib/luid.c @@ -0,0 +1,113 @@ +/* + luid - Lumiera unique identifiers + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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 "lib/luid.h" + +#include +#include +#include +#include +#include +#include +#include + +void +lumiera_uid_set_ptr (lumiera_uid* luid, void* ptr) +{ + memset (luid, 0, 16); + *(void**)luid = ptr; +} + + +void* +lumiera_uid_ptr_get (lumiera_uid* luid) +{ + return *(void**)luid; +} + + +void +lumiera_uid_gen (lumiera_uid* luid) +{ + static int fd = -2; + if (!luid) + return; + + if (fd == -2) + { + fd = open ("/dev/urandom", O_RDONLY); + /* on linux /dev/random would be way to slow for our purpose, so we comment that out for now. + other unixiods offer a /dev/random which has the same semantics as linux /dev/urandom has, + configuration should do this right some day. + if (fd == -1) + fd = open ("/dev/random", O_RDONLY); + */ + if (fd >= 0) + fcntl (fd, F_SETFD, FD_CLOEXEC); + else + srand (getpid () + time (NULL)); + } + + do + { + if (fd < 0) + { + for (int i = 0; i < 16; ++i) + ((unsigned char*)luid)[i] = (unsigned char)(rand()>>7); + } + else + { + if (read (fd, luid, 16) < 16) + abort (); + } + } + /* we identify generic pointers by having some zeros in the luid, + * this happens very unlikely to be in a random luid, just regenerate it then */ + while (!*(((intptr_t*)luid)+1)); +} + + +void +lumiera_uid_copy (lumiera_uid* dest, lumiera_uid* src) +{ + memcpy (dest, src, 16); +} + + +int +lumiera_uid_eq (lumiera_uid* luida, lumiera_uid* luidb) +{ + return !memcmp (luida, luidb, 16); +} + +size_t +lumiera_uid_hash (lumiera_uid* luid) +{ + return *(size_t*)luid; +} + +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ diff --git a/src/lib/luid.h b/src/lib/luid.h new file mode 100644 index 000000000..553d22765 --- /dev/null +++ b/src/lib/luid.h @@ -0,0 +1,97 @@ +/* + luid - Lumiera unique identifiers + + Copyright (C) Lumiera.org + 2008, Christian Thaeter + + 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. +*/ +#ifndef LUMIERA_LUID_H +#define LUMIERA_LUID_H + +#include + +/** + * @file + * Lumiera unique identifiers are 128 byte random value. Unlike standard uuid's we + * don't tag a version within them and we may store generic pointers in the space + * occupied by an luid. + */ + +typedef unsigned char lumiera_uid[16]; +typedef lumiera_uid* LumieraUid; + +/* + C++ can't initialize arrays from string literals with the trailing \0 cropped + C can't initialize non constant members, + there we go +*/ +#ifdef __cplusplus +#define LUMIERA_UID_INITIALIZER(l) \ + { \ + l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7], \ + l[8], l[9], l[10], l[11], l[12], l[13], l[14], l[15] \ + } +#else +#define LUMIERA_UID_INITIALIZER(l) l +#endif + +/** + * Retrieve a generic pointer stored in a luid + */ +void* +lumiera_uid_ptr_get (lumiera_uid* luid); + +/** + * Generate a new luid + */ +void +lumiera_uid_gen (lumiera_uid* luid); + +/** + * Store a generic pointer in a luid + */ +void +lumiera_uid_set_ptr (lumiera_uid* luid, void* ptr); + + +/** + * Copy an luid + */ +void +lumiera_uid_copy (lumiera_uid* dest, lumiera_uid* src); + + +/** + * Test 2 luid's for equality + */ +int +lumiera_uid_eq (lumiera_uid* luida, lumiera_uid* luidb); + + +/** + * Generate a hashsum over an luid + */ +size_t +lumiera_uid_hash (lumiera_uid* luid); + +#endif +/* +// Local Variables: +// mode: C +// c-file-style: "gnu" +// indent-tabs-mode: nil +// End: +*/ From a89b7f8eba3d27bdce5a71b8e704722efc8e8334 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 27 Oct 2008 00:00:40 +0100 Subject: [PATCH 005/140] fix (to make the pseudo gtk_lumiera compile again) --- src/gui/gtk-lumiera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gtk-lumiera.cpp b/src/gui/gtk-lumiera.cpp index 66698150c..9c0528bbd 100644 --- a/src/gui/gtk-lumiera.cpp +++ b/src/gui/gtk-lumiera.cpp @@ -161,7 +161,7 @@ Actions::Actions(WorkspaceWindow &workspace_window) : workspaceWindow(workspace_window), is_updating_action_state(false) { - workspace_window.signal_show ().connect_notify(mem_fun(this, &Actions::update_action_state)); + workspace_window.signal_show ().connect_notify(sigc::mem_fun(this, &Actions::update_action_state)); //----- Create the Action Group -----// actionGroup = ActionGroup::create(); From 62922d357beffce111a4794fca751f15e33877c5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 20 Dec 2008 03:40:28 +0100 Subject: [PATCH 006/140] (irrelevant) changes to make the dummy code build and load shared module Explanation: together with the bare SCons build system, on this branch I added some dummy codefiles, to validate the build system is working as merged in from master --- admin/scons/ToolCCache.py | 65 ++++++++++++++++++++ src/common/dummy-func.cpp | 42 +++++++++++++ src/common/dummy-func.hpp | 20 ++++++ src/gui/gtk-lumiera.cpp | 10 +++ src/gui/gtk-lumiera.hpp | 8 ++- src/include/nobugcfg.h | 39 ------------ src/lumiera/main.cpp | 58 ++---------------- src/proc/common.hpp | 7 +-- src/tool/SConscript | 1 - src/tool/luidgen.c | 124 -------------------------------------- tests/00test.tests | 1 - tests/bugs/buggy.cpp | 2 +- 12 files changed, 151 insertions(+), 226 deletions(-) create mode 100644 admin/scons/ToolCCache.py create mode 100644 src/common/dummy-func.cpp create mode 100644 src/common/dummy-func.hpp delete mode 100644 src/tool/luidgen.c diff --git a/admin/scons/ToolCCache.py b/admin/scons/ToolCCache.py new file mode 100644 index 000000000..ecc45d011 --- /dev/null +++ b/admin/scons/ToolCCache.py @@ -0,0 +1,65 @@ +# -*- python -*- +## +## ToolDistCC.py - SCons tool for distributed compilation using DistCC +## + +# Copyright (C) Lumiera.org and FreeOrion.org +# 2008, Hermann Vosseler +# +# 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. +##################################################################### + +# This SCons builder was extracted from http://www.freeorion.org/ +# FreeOrion is an open-source platform-independent galactic conquest game +# +# history: 12/2008 adapted for Lumiera build system + + +import os +from Buildhelper import * + + +def generate(env): + """ Modify the environment such as to redirect any + C/C++ compiler invocations through DistCC. Additionally + pull in the environment config variables used by DistCC + """ + if not exists(env): return + + assert env['DISTCC'] + if not env['DISTCC'] in env['CC']: + env['CC'] = env.subst('$DISTCC $CC') + if not env['DISTCC'] in env['CXX']: + env['CXX'] = env.subst('$DISTCC $CXX') + print env.subst("* Build using $DISTCC") + for i in ['HOME' + ,'DISTCC_HOSTS' + ,'DISTCC_VERBOSE' + ,'DISTCC_FALLBACK' + ,'DISTCC_LOG' + ,'DISTCC_MMAP' + ,'DISTCC_SAVE_TEMPS' + ,'DISTCC_TCP_CORK' + ,'DISTCC_SSH' + ]: + if os.environ.has_key(i) and not env.has_key(i): + env['ENV'][i] = os.environ[i] + + +def exists(env): + """ Ensure DistCC exists. + """ + return checkCommandOption(env, 'DISTCC', cmdName='distcc') + diff --git a/src/common/dummy-func.cpp b/src/common/dummy-func.cpp new file mode 100644 index 000000000..08b7f69a0 --- /dev/null +++ b/src/common/dummy-func.cpp @@ -0,0 +1,42 @@ +/* + dummy-func.cpp - placeholder with dummy functions to demonstrate building shared modules + +* ******************************************************************************************/ + + +#include "common/dummy-func.hpp" + +#include +#include + + + + + +namespace lumiera { + + const char * const GUI_MODULE_NAME = ".libs/gtk_gui.lum"; + + typedef void (*VoidFunc)(void); + + + void + loadDummyGui() + { + void* handle = dlopen (GUI_MODULE_NAME, RTLD_LAZY|RTLD_LOCAL); + if (handle) + { + VoidFunc entryPoint = (VoidFunc) dlsym (handle, "start_dummy_gui"); + + if (!entryPoint) + ERROR (lumiera, "unable to resolve the entry point symbol after loading the GUI module."); + + else + (*entryPoint) (); + } + else + ERROR (lumiera, "unable to load %s", GUI_MODULE_NAME); + } + + +} // namespace lumiera diff --git a/src/common/dummy-func.hpp b/src/common/dummy-func.hpp new file mode 100644 index 000000000..cbf3a96bb --- /dev/null +++ b/src/common/dummy-func.hpp @@ -0,0 +1,20 @@ +/* + dummy-func.hpp - placeholder with dummy functions to demonstrate building shared modules + +* ******************************************************************************************/ + + +#include "include/nobugcfg.h" + + +namespace lumiera { + + /** this is a function located in the liblumieracore.so, + * which attempts to load the "pseudo-gui" as shared module + * and invoke the gui-main. The sole purpose of this function + * is to demonstarte that the SCons build system is working. + */ + void loadDummyGui(); + + +} // namespace lumiera diff --git a/src/gui/gtk-lumiera.cpp b/src/gui/gtk-lumiera.cpp index 9c0528bbd..4dbc6ec5e 100644 --- a/src/gui/gtk-lumiera.cpp +++ b/src/gui/gtk-lumiera.cpp @@ -37,6 +37,16 @@ main (int argc, char *argv[]) return the_application.main(argc, argv); } +extern "C" +void +start_dummy_gui () +{ + NOTICE(gui, "This is a placeholder for the Lumiera GTK-GUI starting...."); +} + + + + namespace gui { diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index f543f1fde..a55160530 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -12,14 +12,20 @@ #include #include #include -#include "lib/util.hpp" #include extern "C" { #include + + +/** Dummy function just to demonstrate loading the GUI as + * shared module from the SCons build. */ +void start_dummy_gui (); + } + NOBUG_DECLARE_FLAG(gui); #ifdef ENABLE_NLS diff --git a/src/include/nobugcfg.h b/src/include/nobugcfg.h index cfcedd6b7..5e1a33eb1 100644 --- a/src/include/nobugcfg.h +++ b/src/include/nobugcfg.h @@ -24,37 +24,6 @@ /** @file nobugcfg.h ** This header is for including and configuring NoBug. - ** The idea is that configuration and some commonly used flag - ** declarations are to be kept in one central location. Subsystems - ** are free to define and use additional flags for local use. Typically, - ** this header will be included via some of the basic headers like error.hpp, - ** which in turn gets included e.g. by proc/common.hpp - ** - ** This header can thus be assumed to be effectively global. It should contain - ** only declarations of global relevance, as any change causes the whole project - ** to be rebuilt. Moreover, for C++ this header assures automatic initialisation - ** of NoBug by placing a static ctor call. - ** - ** Besides the usual guarded declarations, this header contains one section - ** with the corresponding definitions. This section is to be included once - ** by some translation unit (currently this is lumiera/nobugcfg.cpp) in order to - ** generate the necessary definitions. - ** - ** @par Logging configuration - ** By default, logging is configured such as to emit a small number of informative - ** messages on the starting terminal and to report fatal errors. But besides the - ** usual fine-grained tracing messages, we define a small number of distinct - ** thematic Logging Channels providing a consistent high-level view of - ** what is going on with regards to a specific aspect of the application - ** - \c operate documents a high-level overall view of what the application \em does - ** - \c render focuses on the working of the render engine (without logging each frame) - ** - \c config shows anything of relevance regarding the configured state of App and session - ** - \c memory allows to diagnose a high-level view of memory management - ** - ** Any log level can be overridden by an environment variable, for example - ** \code NOBUG_LOG='operate:INFO' ./lumiera \endcode - ** - ** @todo logging to files? */ @@ -67,14 +36,6 @@ #ifdef __cplusplus /* ============= C++ ================ */ -#include "include/lifecycle.h" -#include "include/error.hpp" ///< make assertions throw instead of abort() - -namespace lumiera { - void initialise_NoBug (); - namespace { - LifecycleHook trigger_it_ (ON_BASIC_INIT, &initialise_NoBug); -} } #endif /* =====================(End) C++ ================ */ diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp index 8dce65c54..f148965d6 100644 --- a/src/lumiera/main.cpp +++ b/src/lumiera/main.cpp @@ -23,67 +23,17 @@ #include "include/nobugcfg.h" -#include "include/error.hpp" -#include "common/appstate.hpp" -#include "common/option.hpp" - -#include "backend/enginefacade.hpp" -#include "backend/netnodefacade.hpp" -#include "backend/scriptrunnerfacade.hpp" -#include "proc/facade.hpp" -#include "gui/guifacade.hpp" - -using util::Cmdline; -using lumiera::Subsys; -using lumiera::AppState; -using lumiera::ON_GLOBAL_INIT; - -namespace { - Subsys& engine = backend::EngineFacade::getDescriptor(); - Subsys& netNode = backend::NetNodeFacade::getDescriptor(); - Subsys& script = backend::ScriptRunnerFacade::getDescriptor(); - Subsys& builder = proc::Facade::getBuilderDescriptor(); - Subsys& session = proc::Facade::getSessionDescriptor(); - Subsys& lumigui = gui::GuiFacade::getDescriptor(); -} +#include "common/dummy-func.hpp" int main (int argc, const char* argv[]) { + NOBUG_INIT; + NOTICE (lumiera, "*** Lumiera NLE for Linux ***"); - AppState& application = AppState::instance(); - try - { - Cmdline args (argc,argv); - lumiera::Option options (args); - application.init (options); - - session.depends (builder); - netNode.depends (session); - netNode.depends (engine); -// lumigui.depends (session); //////TODO commented out in order to be able to start up a dummy GuiStarterPlugin -// lumigui.depends (engine); - script.depends (session); - script.depends (engine); - - application.maybeStart (session); - application.maybeStart (netNode); - application.maybeStart (lumigui); - application.maybeStart (script); - - return application.maybeWait(); - } + lumiera::loadDummyGui(); - - catch (lumiera::Error& problem) - { - return application.abort (problem); - } - catch (...) - { - return application.abort(); - } } diff --git a/src/proc/common.hpp b/src/proc/common.hpp index 970811102..8c9c7fe78 100644 --- a/src/proc/common.hpp +++ b/src/proc/common.hpp @@ -41,11 +41,8 @@ /* common types frequently used... */ -#include "lib/p.hpp" -#include "lib/util.hpp" -#include "lib/lumitime.hpp" -#include "include/symbol.hpp" -#include "include/error.hpp" ///< pulls in NoBug via nobugcfg.h +#include "include/nobugcfg.h" +#include "common/dummy-func.hpp" /** diff --git a/src/tool/SConscript b/src/tool/SConscript index 2336c3553..41a1593d1 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -8,7 +8,6 @@ Import('env','artifacts','core') # build the ubiquitous Hello World application (note: C source) artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') - + env.Program('#$BINDIR/luidgen', ['luidgen.c']+core) + env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature... ] diff --git a/src/tool/luidgen.c b/src/tool/luidgen.c deleted file mode 100644 index 730395b6c..000000000 --- a/src/tool/luidgen.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - luidgen.c - generate a lumiera uuid - - Copyright (C) Lumiera.org - 2008 Christian Thaeter - - 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 "lib/safeclib.h" -#include "lib/luid.h" - -#include -#include -#include -#include -#include -#include - -/** - * @file - * Generate amd print a Lumiera uid as octal escaped string - * or process a file replaceing 'LUIDGEN' with a octal escaped string - */ - - -int -main (int argc, char** argv) -{ - NOBUG_INIT; - lumiera_uid luid; - - if (argc == 1) - { - lumiera_uid_gen (&luid); - printf ("\""); - for (int i = 0; i < 16; ++i) - printf ("\\%.3hho", *(((char*)&luid)+i)); - printf ("\"\n"); - } - else - { - for (int i = 1; i < argc; ++i) - { - FILE* in = fopen (argv[i], "r"); - if (!in) - { - fprintf (stderr, "Failed to open file %s for reading: %s\n", argv[i], strerror (errno)); - continue; - } - - char* outname = lumiera_tmpbuf_snprintf (SIZE_MAX, "%s.luidgen", argv[i]); - FILE* out = fopen (outname, "wx"); - if (!out) - { - fprintf (stderr, "Failed to open file %s for writing: %s\n", outname, strerror (errno)); - fclose (in); - continue; - } - - char buf[4096]; - char luidbuf[67]; - - printf ("Luidgen %s ", argv[i]); fflush (stdout); - - while (fgets (buf, 4096, in)) - { - char* pos; - while ((pos = strstr(buf, "LUIDGEN"))) - { - memmove (pos+66, pos+7, strlen (pos+7)+1); - lumiera_uid_gen (&luid); - sprintf (luidbuf, "\""LUMIERA_UID_FMT"\"", LUMIERA_UID_ELEMENTS(luid)); - memcpy (pos, luidbuf, 66); - putchar ('.'); fflush (stdout); - } - fputs (buf, out); - } - - fclose (out); - fclose (in); - - char* backup = lumiera_tmpbuf_snprintf (SIZE_MAX, "%s~", argv[i]); - unlink (backup); - - if (!!rename (argv[i], backup)) - { - fprintf (stderr, "Failed to create backupfile %s: %s\n", backup, strerror (errno)); - continue; - } - - if (!!rename (outname, argv[i])) - { - fprintf (stderr, "Renaming %s to %s failed: %s\n", outname, argv[i], strerror (errno)); - rename (backup, argv[i]); - continue; - } - - printf (" done\n"); - } - } - - return 0; -} - -/* -// Local Variables: -// mode: C -// c-file-style: "gnu" -// indent-tabs-mode: nil -// End: -*/ diff --git a/tests/00test.tests b/tests/00test.tests index d1205b8c1..71b7edccb 100644 --- a/tests/00test.tests +++ b/tests/00test.tests @@ -21,5 +21,4 @@ return: 2 END PLANNED "this may never happen" -END diff --git a/tests/bugs/buggy.cpp b/tests/bugs/buggy.cpp index b856f2a44..630d3b00b 100644 --- a/tests/bugs/buggy.cpp +++ b/tests/bugs/buggy.cpp @@ -23,7 +23,7 @@ #include -#include "proc/lumiera.hpp" +#include "proc/common.hpp" using std::cout; From 83e0c44a49047693bec15b7182cfb046b59b61f2 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 15 Jan 2009 14:48:07 +0100 Subject: [PATCH 007/140] (irrelevant) fixes to make the dummy/demo build work again --- src/gui/gtk-lumiera.cpp | 8 +- src/gui/gtk-lumiera.hpp | 2 + src/tool/SConscript | 2 +- tests/SConscript | 2 +- tests/plugin/examplepluginc/example_plugin.c | 169 +----------------- .../plugin/test-cpp-plugin/example_plugin.cpp | 129 ------------- 6 files changed, 11 insertions(+), 301 deletions(-) delete mode 100644 tests/plugin/test-cpp-plugin/example_plugin.cpp diff --git a/src/gui/gtk-lumiera.cpp b/src/gui/gtk-lumiera.cpp index 102c424dd..4d8f9bda5 100644 --- a/src/gui/gtk-lumiera.cpp +++ b/src/gui/gtk-lumiera.cpp @@ -37,7 +37,11 @@ extern "C" void start_dummy_gui () { - NOTICE(gui, "This is a placeholder for the Lumiera GTK-GUI starting...."); + NOTICE(gui, "Dummy Lumiera GTK-GUI starting...."); + + int argc =0; + char *argv[] = {}; // faked command line for GTK + gui::application().main(argc, argv); } @@ -90,7 +94,7 @@ void WorkspaceWindow::create_ui() { //----- Configure the Window -----// - set_title(AppTitle); + set_title(GtkLumiera::AppTitle); set_default_size(1024, 768); //----- Set up the UI Manager -----// diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index c4efb6259..832a460ad 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -16,6 +16,8 @@ #include #include +#include + extern "C" { #include diff --git a/src/tool/SConscript b/src/tool/SConscript index 0ca19490d..f5c22a478 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -16,7 +16,7 @@ rsvg = envGtk.Program('#$BINDIR/rsvg-convert','rsvg-convert.c') # build additional test and administrative tools.... artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') #### hello world (checks C build) + env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature... - + luidgen +# + luidgen + vgsuppr + rsvg ] diff --git a/tests/SConscript b/tests/SConscript index e914ca001..65ac7aa79 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -55,7 +55,7 @@ moduledirs = globRootdirs('*') -artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['lib','components'] ] +artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # was: ['lib','components'] + [ testCollection(env, dir) for dir in moduledirs if not dir in specials] + createPlugins(envPlu, 'plugin') ) diff --git a/tests/plugin/examplepluginc/example_plugin.c b/tests/plugin/examplepluginc/example_plugin.c index 60992745d..b18212522 100644 --- a/tests/plugin/examplepluginc/example_plugin.c +++ b/tests/plugin/examplepluginc/example_plugin.c @@ -1,50 +1,11 @@ /* - example_plugin - example plugin for testing the interface/plugin system - - Copyright (C) Lumiera.org - 2008, Christian Thaeter - - 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. + example_plugin - dummy example plugin * *****************************************************/ #include -#include "common/interfacedescriptor.h" -#include "common/config_interface.h" - -#include "tests/common/hello_interface.h" - - -LUMIERA_PLUGIN_INTERFACEHANDLE; - -LumieraInterface -myopen (LumieraInterface self, LumieraInterface interfaces) -{ - LUMIERA_PLUGIN_STORE_INTERFACEHANDLE (interfaces); - - fprintf (stderr, "opened %p global interfaces %p\n", self, interfaces); - return self; -} - -void -myclose (LumieraInterface self) -{ - fprintf (stderr, "closed %p\n", self); -} void hallo (void) { @@ -66,131 +27,3 @@ void bye (const char* m) printf ("Bye %s\n", m); } - -void yeahbabe (void) -{ - LUMIERA_INTERFACE_HANDLE (lumieraorg_testhello, 0) german = - LUMIERA_INTERFACE_OPEN (lumieraorg_testhello, 0, 0, lumieraorg_hello_german); - - LUMIERA_INTERFACE_HANDLE (lumieraorg_testhello, 0) english = - LUMIERA_INTERFACE_OPEN (lumieraorg_testhello, 0, 0, lumieraorg_hello_english); - - LUMIERA_INTERFACE_HANDLE (lumieraorg_configuration, 0) config = - LUMIERA_INTERFACE_OPEN (lumieraorg_configuration, 0, 0, lumieraorg_configuration); - - const char* path; - if (config->wordlist_get ("config.path", &path)) - printf ("config path is: %s\n", path); - if (config->wordlist_get ("plugin.path", &path)) - printf ("plugin path is: %s\n", path); - - LUMIERA_INTERFACE_CLOSE (config); - - german->hello (); - english->hello (); - english->goodbye ("World!"); - german->goodbye ("Welt!"); - - LUMIERA_INTERFACE_CLOSE (german); - LUMIERA_INTERFACE_CLOSE (english); -} - - - -LUMIERA_INTERFACE_INSTANCE (lumieraorg_interfacedescriptor, 0, - lumieraorg_exampleplugin_descriptor, - NULL, NULL, NULL, - LUMIERA_INTERFACE_INLINE (name, "\003\307\005\305\201\304\175\377\120\105\332\016\136\354\251\022", - const char*, (LumieraInterface iface), - {return "LumieraTest";} - ), - LUMIERA_INTERFACE_INLINE (brief, "\303\047\265\010\242\210\365\340\024\030\350\310\067\171\170\260", - const char*, (LumieraInterface iface), - {return "Lumiera Test suite examples";} - ), - LUMIERA_INTERFACE_INLINE (homepage, "\363\125\352\312\056\255\274\322\351\245\051\350\120\024\115\263", - const char*, (LumieraInterface iface), - {return "http://www.lumiera.org/develompent.html";} - ), - LUMIERA_INTERFACE_INLINE (version, "\114\043\133\175\354\011\232\002\117\240\107\141\234\157\217\176", - const char*, (LumieraInterface iface), - {return "No Version";} - ), - LUMIERA_INTERFACE_INLINE (author, "\313\300\055\156\126\320\144\247\140\023\261\002\270\367\017\267", - const char*, (LumieraInterface iface), - {return "Christian Thaeter";} - ), - LUMIERA_INTERFACE_INLINE (email, "\163\051\312\276\137\317\267\305\237\274\133\012\276\006\255\160", - const char*, (LumieraInterface iface), - {return "ct@pipapo.org";} - ), - LUMIERA_INTERFACE_INLINE (copyright, "\160\246\161\204\123\262\375\351\157\276\333\073\355\036\062\341", - const char*, (LumieraInterface iface), - { - return - "Copyright (C) Lumiera.org\n" - " 2008 Christian Thaeter "; - } - ), - LUMIERA_INTERFACE_INLINE (license, "\007\311\044\214\064\223\201\326\331\111\233\356\055\264\211\201", - const char*, (LumieraInterface iface), - { - return - "This program is free software; you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 2 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program; if not, write to the Free Software\n" - "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; - } - ), - - LUMIERA_INTERFACE_INLINE (state, "\331\353\126\162\067\376\340\242\232\175\167\105\122\177\306\354", - int, (LumieraInterface iface), - {return LUMIERA_INTERFACE_EXPERIMENTAL;} - ), - - LUMIERA_INTERFACE_INLINE (versioncmp, "\363\145\363\224\325\104\177\057\344\023\367\111\376\221\152\135", - int, (const char* a, const char* b), - {return 0;} - ) - ); - - -LUMIERA_EXPORT( - LUMIERA_INTERFACE_DEFINE (lumieraorg_testhello, 0, - lumieraorg_hello_german, - LUMIERA_INTERFACE_REF (lumieraorg_interfacedescriptor, 0, lumieraorg_exampleplugin_descriptor), - myopen, - myclose, - LUMIERA_INTERFACE_MAP (hello, "\167\012\306\023\031\151\006\362\026\003\125\017\170\022\100\333", - hallo), - LUMIERA_INTERFACE_MAP (goodbye, "\324\267\214\166\340\213\155\053\157\125\064\264\167\235\020\223", - tschuess) - ), - LUMIERA_INTERFACE_DEFINE (lumieraorg_testhello, 0, - lumieraorg_hello_english, - LUMIERA_INTERFACE_REF (lumieraorg_interfacedescriptor, 0, lumieraorg_exampleplugin_descriptor), - myopen, - myclose, - LUMIERA_INTERFACE_MAP (hello, "\326\247\370\247\032\103\223\357\262\007\356\042\051\330\073\116", - hello), - LUMIERA_INTERFACE_MAP (goodbye, "\365\141\371\047\101\230\050\106\071\231\022\235\325\112\354\241", - bye) - ), - LUMIERA_INTERFACE_DEFINE (lumieraorg_testtest, 0, - lumieraorg_test_both, - LUMIERA_INTERFACE_REF (lumieraorg_interfacedescriptor, 0, lumieraorg_exampleplugin_descriptor), - myopen, - myclose, - LUMIERA_INTERFACE_MAP (testit, "\101\060\122\277\370\023\164\257\347\247\164\325\157\266\323\370", - yeahbabe) - ) - ) diff --git a/tests/plugin/test-cpp-plugin/example_plugin.cpp b/tests/plugin/test-cpp-plugin/example_plugin.cpp deleted file mode 100644 index b99d951d3..000000000 --- a/tests/plugin/test-cpp-plugin/example_plugin.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - example_plugin.cpp - example plugin (C++) for testing the interface/plugin system - - Copyright (C) Lumiera.org - 2008, Christian Thaeter , - Hermann Vosseler - - 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 -#include - -#include "common/interfacedescriptor.h" -#include "common/config_interface.h" - -extern "C" { -#include "common/interface.h" -#include "common/interfacedescriptor.h" - -#include "tests/common/hello_interface.h" -} - -using boost::format; -using std::cout; -using std::endl; - - - -class example_plugin - { - public: - static LumieraInterface - myopen (LumieraInterface self, LumieraInterface interfaces) - { - static format fmt("opened %x global interfaces %x"); - cout << fmt % self % interfaces << endl; - return self; - } - - static void - myclose (LumieraInterface) - { - std::cout << "closed" << endl; - } - }; - - -class example_plugin_de - : public example_plugin - { - public: - static void - griazi () - { - std::cout << "Hallo Welt!" << endl; - } - - static void - servus (const char* m) - { - std::cout << "Tschuess " << m << endl; - } - }; - - -class example_plugin_en - : public example_plugin - { - public: - static void - hello () - { - std::cout << "Hello World!" << endl; - } - - static void - bye (const char* m) - { - std::cout << "Bye " << m << endl; - } - }; - - - - -extern "C" { /* ================== define two lumieraorg_testhello instance ======================= */ - - - - LUMIERA_EXPORT( /* ===================== PLUGIN EXPORTS ================================== */ - - LUMIERA_INTERFACE_DEFINE (lumieraorg_testhello, 0 - ,lumieraorg_hello_german_cpp - , NULL /* no descriptor given */ - , example_plugin::myopen - , example_plugin::myclose - , LUMIERA_INTERFACE_MAP (hello, "\300\244\125\265\235\312\175\263\335\044\371\047\247\263\015\322", - example_plugin_de::griazi) - , LUMIERA_INTERFACE_MAP (goodbye, "\115\365\126\102\201\104\012\257\153\232\006\210\010\346\076\070", - example_plugin_de::servus) - ), - LUMIERA_INTERFACE_DEFINE (lumieraorg_testhello, 0 - ,lumieraorg_hello_english_cpp - , NULL /* no descriptor given */ - , example_plugin::myopen - , example_plugin::myclose - , LUMIERA_INTERFACE_MAP (hello, "\303\367\107\154\077\063\237\066\034\034\050\136\170\220\260\226", - example_plugin_en::hello) - , LUMIERA_INTERFACE_MAP (goodbye, "\107\207\072\105\101\102\150\201\322\043\104\110\232\023\205\161", - example_plugin_en::bye) - ) - ); - -} // extern "C" From bdcef03834bb542e345920858cd978438e4ccb03 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 24 Apr 2009 17:33:03 +0200 Subject: [PATCH 008/140] Fix: (re)add gthread to make the dummy work --- SConstruct | 2 +- src/tool/SConscript | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SConstruct b/SConstruct index c1466d66b..de7a7edc2 100644 --- a/SConstruct +++ b/SConstruct @@ -353,7 +353,7 @@ def defineBuildTargets(env, artifacts): # the Lumiera GTK GUI envGtk = env.Clone() - envGtk.mergeConf(['gtkmm-2.4','cairomm-1.0','gdl','xv','xext','sm']) + envGtk.mergeConf(['gtkmm-2.4','cairomm-1.0','gdl','gthread-2.0','xv','xext','sm']) envGtk.Append(CPPDEFINES='LUMIERA_PLUGIN', LIBS=core) objgui = srcSubtree(envGtk,'$SRCDIR/gui') diff --git a/src/tool/SConscript b/src/tool/SConscript index e475fc057..d5e89270d 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -8,7 +8,7 @@ Import('env','artifacts','core') support_lib = artifacts['support'] envSvg = env.Clone() -envSvg.mergeConf(['librsvg-2.0']) +envSvg.mergeConf(['librsvg-2.0','gthread-2.0']) envSvg.Append(LIBS=support_lib) @@ -19,7 +19,7 @@ rsvg = envSvg.Program('#$BINDIR/rsvg-convert','rsvg-convert.c') ## f # build additional test and administrative tools.... artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') #### hello world (checks C build) + env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature... - + luidgen +# + luidgen + vgsuppr + rsvg ] From 8e9edad9e7c7af023d350568ef00ae59c16f24aa Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 11 Oct 2009 07:10:29 +0200 Subject: [PATCH 009/140] fixes to make this dummy compile again --- src/gui/gtk-lumiera.hpp | 2 -- src/tool/try.cpp | 15 --------------- 2 files changed, 17 deletions(-) diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index 99b0a5efc..951b861ef 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -17,8 +17,6 @@ #include #include -#include "lib/lumitime.hpp" - #include extern "C" { diff --git a/src/tool/try.cpp b/src/tool/try.cpp index fb41545d7..14eb754ff 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -20,7 +20,6 @@ #include -using std::rand; using std::string; using std::cout; @@ -31,20 +30,6 @@ main (int, char**) //(int argc, char* argv[]) NOBUG_INIT; - for (int i=0; i<10; ++i) - { - TestIt1 testrosteron (i); - - if (testrosteron) - cout << "doIt \n"; - if (!testrosteron) - cout << i << "\n"; - } - cout << "size=" << sizeof(TestIt1) <<"\n"; - - char* horror = 0; - ERROR (all, "note: %s is a horrible thing", horror); - cout << "\n.gulp.\n"; return 0; From f0aed2c2cc79d77e434efec75b2e26f01ff10fd2 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 21 May 2010 03:50:41 +0200 Subject: [PATCH 010/140] fix dummy compilation/tests --- src/proc/common.hpp | 2 -- tests/00test.tests | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/proc/common.hpp b/src/proc/common.hpp index 31e885f1d..8c9c7fe78 100644 --- a/src/proc/common.hpp +++ b/src/proc/common.hpp @@ -43,8 +43,6 @@ #include "include/nobugcfg.h" #include "common/dummy-func.hpp" -#include "lib/lumitime.hpp" -#include "lib/symbol.hpp" /** diff --git a/tests/00test.tests b/tests/00test.tests index 71b7edccb..84dc646bb 100644 --- a/tests/00test.tests +++ b/tests/00test.tests @@ -15,8 +15,8 @@ END TESTING "Run the Dummy Test Executable" ./test-bugs TEST "I am a dummy test" < Date: Thu, 3 Jun 2010 04:18:34 +0200 Subject: [PATCH 011/140] Bump required nobug version to 201006.1 --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index dc4dc392a..eaf03c54e 100644 --- a/SConstruct +++ b/SConstruct @@ -232,7 +232,7 @@ def configurePlatform(env): else: print 'Valgrind not found. The use of Valgrind is optional; building without.' - if not conf.CheckPkgConfig('nobugmt', 201005.1): + if not conf.CheckPkgConfig('nobugmt', 201006.1): problems.append('Did not find NoBug [http://www.lumiera.org/nobug_manual.html].') else: conf.env.mergeConf('nobugmt') From 002f024ed8bdc67ca7b911aae8b3216d2aa07c46 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 26 Jun 2010 05:08:16 +0200 Subject: [PATCH 012/140] SCons: improve dependency handling for the testsuite --- tests/SConscript | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/SConscript b/tests/SConscript index 5fe43738d..6e97f1c5a 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -5,6 +5,8 @@ import os from os import path +from glob import glob + from Buildhelper import srcSubtree from Buildhelper import scanSubtree from Buildhelper import globRootdirs @@ -58,8 +60,9 @@ moduledirs = globRootdirs('*') artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # was: ['lib','components'] + [ testCollection(env, dir) for dir in moduledirs if not dir in specials] + createPlugins(envPlu, 'plugin') - ) - + + env.File(glob('*.tests')) # depending on the test definition files for test.sh + ) + @@ -70,8 +73,9 @@ artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # # - the product of running the Testsuite is the ",testlog" # - it depends on all artifacts defined as "ts" above -# - if not set via options switch, the environment variable TESTSUITES -# is explicitly propagated to test.sh +# - including the tests/*.tests (suite definition files) +# - if not set via options switch, the environment variables +# TESTSUITES and VALGRINDFLAGS are explicitly propagated to test.sh # testEnv = env.Clone() @@ -80,25 +84,25 @@ if not valgrind and not env['VALGRIND']: valgrind = 'DISABLE' testEnv.Append(ENV = { 'VALGRINDFLAGS' : valgrind - , 'LUMIERA_CONFIG_PATH' : './' + , 'LUMIERA_CONFIG_PATH' : './' }) - + testsuites = env['TESTSUITES'] or os.environ.get('TESTSUITES') if testsuites: - testEnv['ENV']['TESTSUITES'] = testsuites + testEnv['ENV']['TESTSUITES'] = testsuites pluginpath = os.environ.get('LUMIERA_PLUGIN_PATH') if testsuites: - testEnv['ENV']['LUMIERA_PLUGIN_PATH'] = pluginpath + testEnv['ENV']['LUMIERA_PLUGIN_PATH'] = pluginpath # specify path to test.conf -testEnv['ENV']['TEST_CONF'] = env.File("test.conf").abspath +testEnv['ENV']['TEST_CONF'] = env.File("test.conf").abspath + - testDir = env.Dir('#$BINDIR') runTest = env.File("test.sh").abspath -runTs = testEnv.Command(',testlog', ts, runTest, chdir=testDir) +runTs = testEnv.Command('#$BINDIR/,testlog', ts, runTest, chdir=testDir) @@ -111,4 +115,4 @@ env.Alias('testcode', ts ) env.Alias('check', runTs ) # allow tempfiles of test.sh to be cleaned -env.Clean ('check', [',testlog.pre',',expect_stdout',',stdout',',stderr',',testtmp','.libs']) +env.Clean ('check', [',testlog',',testlog.pre',',expect_stdout',',stdout',',stderr',',testtmp','.libs']) From a29591c2992c307bc08258ae82b483b14fc21795 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 26 Jul 2010 03:24:15 +0200 Subject: [PATCH 013/140] SCons: several small improvements, e.g. valgrind-suppressionfile --- admin/scons/ToolCCache.py | 53 +++++++++++++++++------------ src/tool/SConscript | 4 +-- tests/SConscript | 4 +++ tests/test.conf | 3 ++ tests/test.h | 39 +++++++++++---------- tests/test.sh | 2 +- {src => tests}/tool/vgsuppression.c | 0 7 files changed, 60 insertions(+), 45 deletions(-) rename {src => tests}/tool/vgsuppression.c (100%) diff --git a/admin/scons/ToolCCache.py b/admin/scons/ToolCCache.py index ecc45d011..7d93fb7a4 100644 --- a/admin/scons/ToolCCache.py +++ b/admin/scons/ToolCCache.py @@ -1,6 +1,6 @@ # -*- python -*- ## -## ToolDistCC.py - SCons tool for distributed compilation using DistCC +## ToolCCache.py - SCons tool for integrating with CCache compiler cache ## # Copyright (C) Lumiera.org and FreeOrion.org @@ -33,33 +33,44 @@ from Buildhelper import * def generate(env): """ Modify the environment such as to redirect any - C/C++ compiler invocations through DistCC. Additionally - pull in the environment config variables used by DistCC + C/C++ compiler invocations through CCache, while using + CCache config variables found in the os.environment. """ if not exists(env): return - assert env['DISTCC'] - if not env['DISTCC'] in env['CC']: - env['CC'] = env.subst('$DISTCC $CC') - if not env['DISTCC'] in env['CXX']: - env['CXX'] = env.subst('$DISTCC $CXX') - print env.subst("* Build using $DISTCC") + assert env['CCACHE'] + if not env['CCACHE'] in env['CC']: + env['CC'] = env.subst('$CCACHE $CC') + if not env['CCACHE'] in env['CXX']: + env['CXX'] = env.subst('$CCACHE $CXX') + print env.subst("* Build using $CCACHE") + for i in ['HOME' - ,'DISTCC_HOSTS' - ,'DISTCC_VERBOSE' - ,'DISTCC_FALLBACK' - ,'DISTCC_LOG' - ,'DISTCC_MMAP' - ,'DISTCC_SAVE_TEMPS' - ,'DISTCC_TCP_CORK' - ,'DISTCC_SSH' + ,'CCACHE_DIR' + ,'CCACHE_TEMPDIR' + ,'CCACHE_LOGFILE' + ,'CCACHE_PATH' + ,'CCACHE_CC' + ,'CCACHE_CPP2' + ,'CCACHE_PREFIX' + ,'CCACHE_DISABLE' + ,'CCACHE_READONLY' + ,'CCACHE_NOSTATS' + ,'CCACHE_NLEVELS' + ,'CCACHE_HARDLINK' + ,'CCACHE_RECACHE' + ,'CCACHE_UMASK' + ,'CCACHE_HASHDIR' + ,'CCACHE_UNIFY' + ,'CCACHE_EXTENSION' ]: if os.environ.has_key(i) and not env.has_key(i): env['ENV'][i] = os.environ[i] -def exists(env): - """ Ensure DistCC exists. - """ - return checkCommandOption(env, 'DISTCC', cmdName='distcc') + +def exists(env): + """ Ensure CCache is available. + """ + return checkCommandOption(env, 'CCACHE', cmdName='ccache') diff --git a/src/tool/SConscript b/src/tool/SConscript index d5e89270d..bae8d7d12 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -8,11 +8,10 @@ Import('env','artifacts','core') support_lib = artifacts['support'] envSvg = env.Clone() -envSvg.mergeConf(['librsvg-2.0','gthread-2.0']) +envSvg.mergeConf(['librsvg-2.0']) envSvg.Append(LIBS=support_lib) -vgsuppr = env.Program('#$BINDIR/vgsuppression','vgsuppression.c', LIBS=core)## for suppressing false valgrind alarms luidgen = env.Program('#$BINDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs rsvg = envSvg.Program('#$BINDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) @@ -20,7 +19,6 @@ rsvg = envSvg.Program('#$BINDIR/rsvg-convert','rsvg-convert.c') ## f artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') #### hello world (checks C build) + env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature... # + luidgen - + vgsuppr + rsvg ] diff --git a/tests/SConscript b/tests/SConscript index 6e97f1c5a..5e97f03e6 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -66,6 +66,10 @@ artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] +# for creating a Valgrind-Suppression file +vgsuppr = env.Program('#$BINDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms +artifacts['tools'] += [vgsuppr] +Depends(ts,vgsuppr) # diff --git a/tests/test.conf b/tests/test.conf index 38cf8082d..c3abbce1c 100755 --- a/tests/test.conf +++ b/tests/test.conf @@ -1 +1,4 @@ LOGSUPPRESS='^\(\*\*[0-9]*\*\* \)\?[0-9]\{10,\}[:!] \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\|TODO\|PLANNED\|FIXME\|DEPRECATED\|UNIMPLEMENTED\|RESOURCE_ANNOUNCE\|RESOURCE_ENTER\|RESOURCE_STATE\|RESOURCE_LEAVE\):' + +LIMIT_VG_CPU=55 +LIMIT_VG_TIME=60 diff --git a/tests/test.h b/tests/test.h index 01374701a..15e88529c 100644 --- a/tests/test.h +++ b/tests/test.h @@ -22,40 +22,39 @@ #ifndef TEST_H #define TEST_H +#include +#include +#include #include -#include "lib/error.h" - -#include - -NOBUG_DEFINE_FLAG (TESTS); -LUMIERA_ERROR_DEFINE (TEST, "test error"); +NOBUG_DEFINE_FLAG_LIMIT(TEST, LOG_DEBUG); #define TESTS_BEGIN \ int \ main (int argc, const char** argv) \ { \ NOBUG_INIT; \ - NOBUG_INIT_FLAG (TESTS); \ + NOBUG_INIT_FLAG (TEST); \ + unsigned testcnt=0; \ + int ret = 0; \ \ if (argc == 1) \ - { \ - fprintf (stderr, "missing argument\n"); \ - return 1; \ - } + fprintf (stderr, "supported tests:\n"); #define TEST(name) \ - else if (!strcmp(argv[1], name)) + if (argc == 1) \ + fprintf (stderr, " "#name"\n"); \ + else if (!strcmp(argv[1], #name) && ++testcnt) +#define PLANNED_TEST(name) \ + if (argc == 1) \ + fprintf (stderr, " "#name" (planned)\n"); \ + else if (!++testcnt) -#define TESTS_END \ - else \ - { \ - fprintf (stderr, "unknown test\n"); \ - return 1; \ - } \ - \ - return 0; \ +#define TESTS_END \ + if (!testcnt && argc !=1) \ + fprintf (stderr,"no such test: %s\n", argv[1]); \ + return ret; \ } diff --git a/tests/test.sh b/tests/test.sh index 77eae39e7..6b4944d55 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -141,7 +141,7 @@ if [ "$VALGRINDFLAGS" = 'DISABLE' ]; then echo "valgrind explicit disabled" else if [ "$(which valgrind)" ]; then - ulimit -S -t ${ULIMIT_VG_CPU:-20} -v ${ULIMIT_VG_VSZ:-524288} + ulimit -S -t ${LIMIT_VG_CPU:-20} -v ${LIMIT_VG_VSZ:-524288} LIMIT_TIME_REAL="$LIMIT_VG_TIME" if [[ -x 'vgsuppression' ]]; then if [[ 'vgsuppression' -nt 'vgsuppression.supp' ]]; then diff --git a/src/tool/vgsuppression.c b/tests/tool/vgsuppression.c similarity index 100% rename from src/tool/vgsuppression.c rename to tests/tool/vgsuppression.c From f7588c36c412d7527eafd556277f18816bfd8329 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 28 Dec 2010 05:22:20 +0100 Subject: [PATCH 014/140] basic time value entites defined roughly complete --- src/lib/time/timevalue.hpp | 141 +++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 38 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 47ee097e9..363fd4d6d 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -25,6 +25,7 @@ #define LIB_TIME_TIMEVALUE_H #include +#include #include extern "C" { @@ -100,7 +101,6 @@ namespace time { boost::multipliable > > { - public: TimeVar (TimeValue time = TimeValue()) : TimeValue(time) @@ -133,31 +133,14 @@ namespace time { }; - class Offset; - + /** - * Lumiera's internal time value datatype + * Offset measures a distance in time. + * It may be used to relate two points in time, + * or to create a modification for time-like entities. + * Similar to (basic) time values, offsets can be compared, + * but are otherwise opaque and immutable. */ - class Time - : public TimeValue - { - public: - static const Time MAX ; - static const Time MIN ; - - explicit - Time (TimeValue val= TimeValue(0)) - : TimeValue(val) - { } - - Time ( long millis - , uint secs - , uint mins =0 - , uint hours=0 - ); - }; - - class Offset : public TimeValue { @@ -167,29 +150,99 @@ namespace time { Offset (TimeValue const& distance) : TimeValue(distance) { } + + Offset (TimeValue const& origin, TimeValue const& target) + : TimeValue(TimeVar(target) -= origin) + { } + + TimeValue + abs() const + { + return TimeValue(std::llabs (t_)); + } }; - -//////////////////////////////////////////////////////////////TODO this seems rather like a bad idea, -// because it opens a lot of implicit conversions which we don't want! -//inline Offset -//operator- (TimeValue const& end, TimeValue const& start) -//{ -// TimeVar distance(end); -// distance -= start; -// return Offset(distance); -//} - - typedef const Offset TimeDistance; - class Duration + + /* ======= specific Time entities ==================== */ + + /** + * Lumiera's internal time value datatype. + * This is a TimeValue, but now more specifically denoting + * a point in time, measured in reference to an internal + * (opaque) time scale. + * + * Lumiera Time provides some limited capabilities for + * direct manipulation; Time values can be created directly + * from \c (h,m,s,ms) specification and there is an string + * representation intended for internal use (reporting and + * debugging). Any real output, formatting and persistent + * storage should be based on the (quantised) timecode + * formats though, which can be generated from time values. + * + * Non constant Time objects can receive an encapsulated + * \em mutation message, which is also the basis for + * changing time spans, durations and for re-aligning + * quantised values to some other grid. + * + * @todo define these mutations + */ + class Time : public TimeValue { - // always positive + public: + static const Time MAX ; + static const Time MIN ; + + explicit + Time (TimeValue const& val =TimeValue(0)) + : TimeValue(val) + { } + + Time ( long millis + , uint secs + , uint mins =0 + , uint hours=0 + ); + + /** @internal diagnostics */ + operator std::string () const; + }; + + /** + * Duration is the internal Lumiera time metric. + * It is an absolute (positive) value, but can be + * promoted from an offset. Similar to Time, + * Duration can receive mutation messages. + * + * @todo define these mutations + */ + class Duration + : public Offset + { + public: + Duration (Offset const& distance) + : Offset(distance.abs()) + { } + }; + + + + /** + * A time interval anchored at a specific point in time. + * The start point of this timespan is also its nominal + * position, and the end point is normalised to happen + * never before the start point. A TimeSpan is enough + * to fully specify the temporal properties of an + * object within the model. + * + * Similar to Time and Duration, a TimeSpan also may + * receive an (opaque) mutation message + */ class TimeSpan : public Time { @@ -200,6 +253,18 @@ namespace time { : Time(start) , dur_(length) { } + + operator Duration() const + { + return dur_; + } + + Time + getEnd() const + { + TimeVar startPoint (*this); + return Time(startPoint + dur_); + } }; From 3d4227d374c3f67256d3b7c9fb3c7c7e0eb28a58 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 28 Dec 2010 07:24:54 +0100 Subject: [PATCH 015/140] cover offsets, durations and timespans by unit test --- src/lib/time/lumitime.cpp | 33 ++++++++++++ src/lib/time/timevalue.hpp | 28 ++++++++-- tests/lib/time/time-value-test.cpp | 85 +++++++++++++++++++++++++++--- 3 files changed, 134 insertions(+), 12 deletions(-) diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index 5794b445e..8d9bba707 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -41,6 +41,39 @@ namespace time { const Time Time::MIN ( TimeValue (-std::numeric_limits::max()) ); + /** convenience constructor to build an + * internal Lumiera Time value from the usual parts + * of an hexagesimal time specification. Arbitrary integral + * values are acceptable and will be summed up accordingly. + * The minute and hour part can be omitted. + * @warning internal Lumiera time values refer to an + * implementation dependent time origin/scale. + * The given value will be used as-is, without + * any further adjustments. + */ + Time::Time ( long millis + , uint secs + , uint mins + , uint hours + ) + : TimeValue(lumiera_build_time (millis,secs,mins,hours)) + { } + + + /** displaying an internal Lumiera Time value + * for diagnostic purposes or internal reporting. + * @warning internal Lumiera time values refer to an + * implementation dependent time origin/scale. + * @return string rendering of the actual, underlying + * implementation value, as \c h:m:s:ms + */ + Time::operator string() const + { + return string (lumiera_tmpbuf_print_time (t_)); + } + + + }} // namespace lib::Time ///////////////////////////////////////////////////////////////////////////TODO leftover of the existing/initial lumitime-Implementation diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 363fd4d6d..5e0e6f28a 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -93,6 +93,10 @@ namespace time { /** a mutable time value, * behaving like a plain number, * allowing copy and re-accessing + * @note supports scaling by a factor, + * which \em deliberately is chosen + * as int, not gavl_time_t, because the + * multiplying times is meaningless. */ class TimeVar : public TimeValue @@ -139,7 +143,10 @@ namespace time { * It may be used to relate two points in time, * or to create a modification for time-like entities. * Similar to (basic) time values, offsets can be compared, - * but are otherwise opaque and immutable. + * but are otherwise opaque and immutable. Yet they allow + * to build derived values, including + * - the \em absolute (positive) distance for this offset + * - a combined offset by chaining another offset */ class Offset : public TimeValue @@ -160,6 +167,14 @@ namespace time { { return TimeValue(std::llabs (t_)); } + + Offset + operator+ (Offset const& toChain) const + { + TimeVar distance(*this); + distance += toChain; + return Offset(distance); + } }; @@ -200,6 +215,10 @@ namespace time { : TimeValue(val) { } + Time (TimeVar const& calcResult) + : TimeValue(calcResult) + { } + Time ( long millis , uint secs , uint mins =0 @@ -240,8 +259,8 @@ namespace time { * to fully specify the temporal properties of an * object within the model. * - * Similar to Time and Duration, a TimeSpan also may - * receive an (opaque) mutation message + * Similar to Time and Duration, a TimeSpan may + * also receive an (opaque) mutation message. */ class TimeSpan : public Time @@ -253,6 +272,7 @@ namespace time { : Time(start) , dur_(length) { } + ///////////TODO creating timespans needs to be more convenient.... operator Duration() const { @@ -263,7 +283,7 @@ namespace time { getEnd() const { TimeVar startPoint (*this); - return Time(startPoint + dur_); + return (startPoint + dur_); } }; diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index d939ea525..a56d0cd93 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -26,14 +26,16 @@ #include "lib/util.hpp" #include -//#include +#include //#include +#include using boost::lexical_cast; using util::isnil; //using std::rand; -//using std::cout; -//using std::endl; +using std::cout; +using std::endl; +using std::string; namespace lib { @@ -53,7 +55,7 @@ namespace test{ random_or_get (Arg arg) { if (isnil(arg)) - return (rand() % 10000); + return 1 + (rand() % 10000); else return lexical_cast (arg[1]); } @@ -66,8 +68,9 @@ namespace test{ checkBasicTimeValues (ref); checkMutableTime (ref); - checkComparisons (ref); - checkComponentAccess(); + createOffsets (ref); + buildDuration (ref); + buildTimeSpan (ref); } @@ -106,6 +109,11 @@ namespace test{ } + /** @test time variables can be used for the typical calculations, + * like summing and subtracting values, as well as multiplication + * with a scale factor. Additionally, the raw time value is + * accessible by conversion. + */ void checkMutableTime (TimeValue org) { @@ -138,16 +146,77 @@ namespace test{ void - checkComparisons (TimeValue org) + createOffsets (TimeValue org) { + TimeValue four(4); + TimeValue five(5); + + Offset off5 (five); + CHECK (0 < off5); + + TimeVar point(org); + point += off5; + CHECK (org < point); + + Offset reverse(point,org); + CHECK (reverse < off5); + CHECK (reverse.abs() == off5); + + CHECK (0 == off5 + reverse); + + // chaining and copy construction + Offset off9 (off5 + Offset(four)); + CHECK (9 == off9); } void - checkComponentAccess() + buildDuration (TimeValue org) { + TimeValue zero; + TimeVar point(org); + point += TimeValue(5); + CHECK (org < point); + + Offset backwards(point,org); + CHECK (backwards < zero); + + Duration distance(backwards); + CHECK (distance > zero); + CHECK (distance == backwards.abs()); + + point = backwards; + point *= 2; + CHECK (point < zero); + CHECK (point < backwards); + + CHECK (distance + point < zero); // using the duration as offset + CHECK (distance == backwards.abs()); // while this didn't alter the duration as such } + + void + buildTimeSpan (TimeValue org) + { + TimeValue zero; + TimeValue five(5); + + TimeSpan interval (Time(org), Duration(Offset (org,five))); + + // the time span behaves like a time + CHECK (org == interval); + CHECK (string(Time(org)) == string(interval)); + + // can get the length by direct conversion + Duration theLength(interval); + CHECK (theLength == Offset(org,five).abs()); + + Time endpoint = interval.getEnd(); + CHECK (Offset(interval,endpoint) == Offset(org,five).abs()); + + cout << "Interval: " << string(interval) + << " Endpoint: " << string(endpoint) << endl; + } }; From ff87b62387bf7ab2692d73d390c41edf084b08b4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 29 Dec 2010 04:26:14 +0100 Subject: [PATCH 016/140] decide how to categorise time grids: they are Meta Assets. --- wiki/renderengine.html | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 28b45fef5..4704cf021 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -747,7 +747,7 @@ Even if the low-level memory manager(s) may use raw storage, we require that the &rarr; see MemoryManagement -
+
Asset management is a subsystem on its own. Assets are "things" that can be loaded into a session, like Media, Clips, Effects, Transitions. It is the "bookkeeping view", while the Objects in the Session relate to the "manipulation and process view". Some Assets can be //loaded// and a collection of Assets is saved with each Session. Besides, there is a collection of basic Assets always available by default.
 
 The Assets are important reference points holding the information needed to access external resources. For example, an Clip asset can reference a Media asset, which in turn holds the external filename from which to get the media stream. For Effects, the situation is similar. Assets thus serve two quite distinct purposes. One is to load, list, group search and browse them, and to provide an entry point to create new or get at existing MObject in the Session, while the other purpose is to provide attribute and property informations to the inner parts of the engine, while at the same time isolating and decoupling them from environmental details. 
@@ -772,13 +772,13 @@ Some software component able to work on media data in the Lumiera Render engine
 Some of the building blocks providing the framework for the objects placed into the current Session. Notable examples are [[processing pipes|Pipe]] within the high-level-model, Viewer attachment points, Tracks, Sequences, Timelines etc.
 * __outward interface operations__ include...
 * __inward interface operations__ include...
-&rarr; StructAsset {{red{to be defined}}}
+&rarr; StructAsset {{red{still a bit vague...}}}
 
 !Meta Asset
-Some additional, virtual facilities created in the course of the editing process. Examples are Automation data sets, Labels and reference points, Meta Clips (nested sub-sequences)
+Any resources related to the //reflective recurse of the application on itself,// including parametrisation and customisation aspects and similar metadata, are categorised and tracked apart of the primary entities. Examples being types, scales and quantisation grids, decision rules, control data stores (automation data), labels and annotations, inventory entities etc.
 * __outward interface operations__ include...
 * __inward interface operations__ include...
-&rarr; MetaAsset {{red{to be defined}}}
+&rarr; MetaAsset {{red{just emerging as of 12/2010}}}
 
 !!!!still to be worked out..
 is how to implement the relationship between [[MObject]]s and Assets. Do we use direct pointers, or do we prefer an ID + central registry approach? And how to handle the removal of an Asset.
@@ -2731,6 +2731,13 @@ For the case here in question this seems to be the ''resource allocation is cons
 
 !using Factories
 And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the session or the sequences, which are created once and don't bear the danger of causing memory pressure. Besides, the ProcLayer code shouldn't issue "new" and "delete" when it comes in hand, rather it should use some centralized [[Factories]] for all allocation and freeing, so we can redirect these calls down to the backend, which may use pooling or special placement allocators or the like. The rationale is, for modern hardware/architectures, care has to be taken with heap allocations, esp. with many small objects and irregular usage patterns.
+
+
+
+
This category is comprised of the various aspects of the way the application controls and manages its own behaviour. They are more related to the way the application behaves, as oposed to the way the editied data is structured and organised.
+* StreamType
+* ScaleGrid
+
 
@@ -4658,6 +4665,10 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin
A facility allowing the Proc-Layer to work with abstracted [[media stream types|StreamType]], linking (abstract or opaque) [[type tags|StreamTypeDescriptor]] to an [[library|MediaImplLib]], which provides functionality for acutally dealing with data of this media stream type. Thus, the stream type manager is a kind of registry of all the external libraries which can be bridged and accessed by Lumiera (for working with media data, that is). The most basic set of libraries is instelled here automatically at application start, most notably the [[GAVL]] library for working with uncompressed video and audio data. //Later on, when plugins will introduce further external libraries, these need to be registered here too.//
+
+
A scale grid controls the way of measuring and aligining a quantity the application has to deal with. The most prominent example is the way to handle time in fixed atomic chunks (''frames'') addressed through a fixed format (''timecode''): while internally the application uses time values of sufficiently fine grained resolution, the acutally visible timing coordinates of objects within the session are ''quantised'' to some predefined and fixed time grid.
+
+
A link to relate a compound of [[nested placement scopes|PlacementScope]] to the //current// session and the //current//&nbsp; [[focus for querying|QueryFocus]] and exploring the structure. ScopeLocator is a singleton service, allowing to ''explore'' a [[Placement]] as a scope, i.e. discover any other placements within this scope, and allowing to locate the position of this scope by navigating up the ScopePath finally to reach the root scope of the HighLevelModel.
 

From 640d061de679b9224d6c6b90a8ab6de78f889ceb Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Thu, 30 Dec 2010 04:30:33 +0100
Subject: [PATCH 017/140] Meta Assets as a framework for specifiyng a TimeGrid

---
 src/proc/asset.cpp                |  2 +-
 src/proc/asset/entry-id.hpp       |  8 +++-
 src/proc/asset/meta.cpp           | 68 +++++++++++++++++++++++----
 src/proc/asset/meta.hpp           | 34 +++++++++++---
 src/proc/asset/meta/time-grid.cpp | 51 ++++++++++++++++++++
 src/proc/asset/meta/time-grid.hpp | 77 +++++++++++++++++++++++++++++++
 src/proc/asset/pipe.hpp           |  2 +-
 src/proc/asset/struct.cpp         |  6 +--
 src/proc/asset/struct.hpp         |  1 -
 wiki/renderengine.html            | 42 +++++++++++++----
 10 files changed, 256 insertions(+), 35 deletions(-)
 create mode 100644 src/proc/asset/meta/time-grid.cpp
 create mode 100644 src/proc/asset/meta/time-grid.hpp

diff --git a/src/proc/asset.cpp b/src/proc/asset.cpp
index 81e5ab5f9..69bcd8a38 100644
--- a/src/proc/asset.cpp
+++ b/src/proc/asset.cpp
@@ -59,7 +59,7 @@ namespace asset {
   /** Asset is a Interface class; usually, objects of 
    *  concrete subclasses are created via specialised Factories
    */
-  Asset::Asset (const Ident& idi) 
+  Asset::Asset (Ident const& idi) 
     : ident(idi)
     , id(AssetManager::reg (this, idi))
     , enabled(true)
diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp
index b7ff98cce..0d51e7fa2 100644
--- a/src/proc/asset/entry-id.hpp
+++ b/src/proc/asset/entry-id.hpp
@@ -31,7 +31,7 @@
  ** 
  ** @note as of 3/2010 this is an experimental setup and exists somewhat in parallel
  **       to the assets. We're still in the process of finding out what's really required
- **       to keep track of all the various kinds of objects.
+ **       to keep track of all the various kinds of objects.                 ///////////////////TICKET #739
  ** 
  ** @see asset::Asset::Ident
  ** @see entry-id-test.cpp
@@ -79,7 +79,8 @@ namespace asset {
      *        char[] of the LUID as a LuidH class, which is ugly, but granted to work.
      *  @todo several unsolved design problems. How to deal with std hash values in
      *        conjunction with LUID. How to create a LuidH instance, if not generating
-     *        a new random value
+     *        a new random value. How to make EntryID and asset::Ident interchangeable,  /////////TICKET #739
+     *        which would require both to yield the same hash values....
      *  @warning this code isn't portable and breaks if sizeof(size_t) < sizeof(void*)
      */
     inline LuidH
@@ -198,6 +199,9 @@ namespace asset {
       /** generate an Asset identification tuple
        *  based on this EntryID's symbolic ID and type information.
        *  The remaining fields are filled in with hardwired defaults.
+       * @note there is a twist, as this asset identity tuple generates
+       *       a different hash as the EntryID. It would be desirable
+       *       to make those two addressing systems interchangeable.      /////////////TICKET #739
        */
       Asset::Ident
       getIdent()  const
diff --git a/src/proc/asset/meta.cpp b/src/proc/asset/meta.cpp
index 795ea0e74..9ac77cac1 100644
--- a/src/proc/asset/meta.cpp
+++ b/src/proc/asset/meta.cpp
@@ -27,31 +27,79 @@
 #include "include/logging.h"
 
 
-namespace asset
-  {
+namespace asset {
+  
+  using meta::Descriptor;
   
   namespace // Implementation details
   {
     /** helper: .....*/
-  } 
-
-
+  }
   
-  MetaFactory Meta::create;  ///< storage for the static MetaFactory instance
+  namespace meta {
+    
+    Descriptor::~Descriptor() { } // emit vtable here...
+    
+  }
+  
+  
+  /**storage for the static MetaFactory instance */
+  MetaFactory Meta::create;
   
   
   
-  /** Factory method for Metadata Asset instances. ....
-   *  @todo actually define
+  /** Generic factory method for Metadata Asset instances.
+   *  @param  EntryID specifying the type and a human readable name-ID
    *  @return an Meta smart ptr linked to the internally registered smart ptr
    *          created as a side effect of calling the concrete Meta subclass ctor.
    */
-  MetaFactory::PType 
-  MetaFactory::operator() (Asset::Ident& key) ////TODO
+  template
+  P
+  MetaFactory::operator() (EntryID elementIdentity)
+  {
+    UNIMPLEMENTED ("Meta-Factory");
+  }
+  
+  
+  /** Generic factory method for specialising Metadata.
+   *  @param  prototype Descriptor of a special kind of metadata,
+   *          to be augmented and further specialised. Can indeed
+   *          be an existing asset::Meta instance
+   *  @param  EntryID specifying the type and a human readable name-ID
+   *  @return an Meta smart ptr linked to the internally registered smart ptr
+   *          created as a side effect of calling the concrete Meta subclass ctor.
+   */
+  template
+  P
+  MetaFactory::operator() (Descriptor const& prototype, EntryID elementIdentity)
   {
     UNIMPLEMENTED ("Meta-Factory");
   }
   
   
   
+} // namespace asset
+
+
+
+
+   /**************************************************/
+   /* explicit instantiations of the factory methods */
+   /**************************************************/
+
+#include "proc/asset/meta/time-grid.hpp"
+//#include "proc/asset/procpatt.hpp"
+//#include "proc/asset/timeline.hpp"
+//#include "proc/asset/sequence.hpp"
+
+
+namespace asset {
+  
+  using meta::Descriptor;
+  using meta::TimeGrid;
+  
+  template P  MetaFactory::operator() (EntryID);
+  
+  template P  MetaFactory::operator() (Descriptor const&, EntryID);
+  
 } // namespace asset
diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp
index cd94b26e5..cc0f569a2 100644
--- a/src/proc/asset/meta.hpp
+++ b/src/proc/asset/meta.hpp
@@ -41,6 +41,7 @@
 #include "pre_a.hpp"
 
 #include "proc/asset.hpp"
+#include "proc/asset/entry-id.hpp"
 #include "lib/factory.hpp"
 
 
@@ -59,13 +60,27 @@ namespace asset {
       ID (const Meta&);
     };
   
+  namespace meta {
+    
+    /**
+     * Interface: the unspecific, reflective base of meta assets.
+     * Based on descriptors, meta assets form a self referential structure.
+     */
+    class Descriptor
+      {
+      public:
+        virtual ~Descriptor();  ///< this is an ABC
+      };
+  }
   
   
   /**
-   * key abstraction: metadata and organisational asset
+   * key abstraction: metadata, parametrisation, customisation and similar organisational traits.
    * @todo just a stub, still have to figure out the distinctive properties of asset::Meta
    */
-  class Meta : public Asset
+  class Meta
+    : public Asset
+    , public meta::Descriptor
     {
     public:
       static MetaFactory create;
@@ -76,13 +91,14 @@ namespace asset {
         }
       
     protected:
-      Meta (const Asset::Ident& idi) : Asset(idi) {}  //////////////TODO
-      friend class MetaFactory;
+      Meta (Asset::Ident const& idi) : Asset(idi) {}  //////////////TODO
+      
+//    friend class MetaFactory;  ///////////////////////////////////TODO still necessary?
     };
     
     
     // definition of ID ctors is possible now,
-   //  after providing full definition of class Proc
+   //  after providing full definition of class Meta
   
   inline ID::ID(HashVal id)       : ID (id)           {};
   inline ID::ID(const Meta& meta) : ID (meta.getID()) {};
@@ -98,8 +114,12 @@ namespace asset {
     {
     public:
       typedef P PType;
-       
-      PType operator() (Asset::Ident& key);      ////////////TODO define actual operation 
+      
+      template
+      P operator() (EntryID elementIdentity); 
+      
+      template
+      P operator() (meta::Descriptor const& prototype, EntryID elementIdentity); 
       
     };
   
diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp
new file mode 100644
index 000000000..e969bf645
--- /dev/null
+++ b/src/proc/asset/meta/time-grid.cpp
@@ -0,0 +1,51 @@
+/*
+  TimeGrid  -  reference scale for quantised time
+
+  Copyright (C)         Lumiera.org
+    2010,               Hermann Vosseler 
+
+  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 "proc/asset/meta/time-grid.hpp"
+#include "proc/asset/entry-id.hpp"
+//#include "lib/util.hpp"
+//#include "include/logging.h"
+
+
+namespace asset {
+namespace meta {
+  
+  namespace {
+    
+    typedef TimeGrid::Builder Builder;
+    
+    Asset::Ident
+    deriveAssetIdentity (Builder const& setup)
+    {
+      EntryID wtf("wtf**ck");       //////////////////////TODO some real meat here
+      return wtf.getIdent();
+    }
+  }
+  
+ 
+  /** */
+  TimeGrid::TimeGrid (Builder const& setup)
+    : Meta (deriveAssetIdentity (setup))
+    { }
+  
+}} // namespace asset::meta
diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp
new file mode 100644
index 000000000..50e6d382f
--- /dev/null
+++ b/src/proc/asset/meta/time-grid.hpp
@@ -0,0 +1,77 @@
+/*
+  TIME-GRID.hpp  -  reference scale for quantised time
+
+  Copyright (C)         Lumiera.org
+    2010,               Hermann Vosseler 
+
+  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 time-grid.hpp
+ ** Establishing a reference scale for quantised time values.
+ ** Contrary to the continuous Time values (Lumiera internal time scale),
+ ** quantised time can only take on some discrete values corresponding
+ ** to blocks or frames, according to the respective \em timecode format.
+ ** The act of quantising continuous time values requires the definition
+ ** of a time grid of such allowed time values. At least, the specification
+ ** of a time origin and a frame spacing is required, but there might be
+ ** way more elaborate specifications, e.g. a grid varying over time.
+ ** 
+ ** Time grid specifications are integrated into Lumiera's framework
+ ** for meta assets, automation, configuration and similar metadata.
+ ** 
+ ** @see MetaFactory creating concrete asset::Meta instances
+ ** @see time::Quantiser
+ **
+ */
+
+#ifndef ASSET_META_TIME_GRID_H
+#define ASSET_META_TIME_GRID_H
+
+#include "proc/asset/meta.hpp"
+#include "lib/time/quantiser.hpp"
+
+
+
+namespace asset {
+namespace meta {
+  
+ 
+  /**
+   * Interface: a grid and scale for time quantisation.
+   * This meta-Asset describes a coordinate system or
+   * reference scale for quantised time values.
+   */ 
+  class TimeGrid
+    : public Meta
+    {
+      
+    public:
+      struct Builder
+        {
+        
+        };
+      
+    protected:
+      friend class Builder;
+      
+      TimeGrid (Builder const& setup); //////////////TODO
+    };
+  
+  
+}} // namespace asset::meta
+#endif
diff --git a/src/proc/asset/pipe.hpp b/src/proc/asset/pipe.hpp
index c60d310c4..c3e070206 100644
--- a/src/proc/asset/pipe.hpp
+++ b/src/proc/asset/pipe.hpp
@@ -84,7 +84,7 @@ namespace asset {
       
     protected:
       Pipe (Asset::Ident const&, string const& streamID, PProcPatt& wiring, string shortName ="", string longName ="") ;
-      friend class StructFactory;
+//    friend class StructFactory;
       friend class StructFactoryImpl;
       
     public:
diff --git a/src/proc/asset/struct.cpp b/src/proc/asset/struct.cpp
index 1eeece4a9..83083c53c 100644
--- a/src/proc/asset/struct.cpp
+++ b/src/proc/asset/struct.cpp
@@ -45,7 +45,7 @@ using util::contains;
 
 namespace asset {
   
-  /****** NOTE: not really implemented yet. What follows is partially a hack to build simple tests *******/
+  /****** NOTE: not fully implemented yet. What follows is partially a hack to build simple tests *******/
   
   
   
@@ -163,10 +163,8 @@ namespace asset {
    /* explicit instantiations of the factory methods */
    /**************************************************/
 
-#include "proc/asset/struct.hpp"
-#include "proc/asset/struct-scheme.hpp"
-#include "proc/asset/procpatt.hpp"
 #include "proc/asset/pipe.hpp"
+#include "proc/asset/procpatt.hpp"
 #include "proc/asset/timeline.hpp"
 #include "proc/asset/sequence.hpp"
 
diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp
index bfd175fae..4fd1b7f2b 100644
--- a/src/proc/asset/struct.hpp
+++ b/src/proc/asset/struct.hpp
@@ -118,7 +118,6 @@ namespace asset {
       
     protected:
       Struct (const Asset::Ident& idi) : Asset(idi) {}
-      friend class StructFactory;
     };
     
     
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 4704cf021..9b6fbed93 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -747,7 +747,7 @@ Even if the low-level memory manager(s) may use raw storage, we require that the
 &rarr; see MemoryManagement
 
-
+
Asset management is a subsystem on its own. Assets are "things" that can be loaded into a session, like Media, Clips, Effects, Transitions. It is the "bookkeeping view", while the Objects in the Session relate to the "manipulation and process view". Some Assets can be //loaded// and a collection of Assets is saved with each Session. Besides, there is a collection of basic Assets always available by default.
 
 The Assets are important reference points holding the information needed to access external resources. For example, an Clip asset can reference a Media asset, which in turn holds the external filename from which to get the media stream. For Effects, the situation is similar. Assets thus serve two quite distinct purposes. One is to load, list, group search and browse them, and to provide an entry point to create new or get at existing MObject in the Session, while the other purpose is to provide attribute and property informations to the inner parts of the engine, while at the same time isolating and decoupling them from environmental details. 
@@ -775,7 +775,7 @@ Some of the building blocks providing the framework for the objects placed into
 &rarr; StructAsset {{red{still a bit vague...}}}
 
 !Meta Asset
-Any resources related to the //reflective recurse of the application on itself,// including parametrisation and customisation aspects and similar metadata, are categorised and tracked apart of the primary entities. Examples being types, scales and quantisation grids, decision rules, control data stores (automation data), labels and annotations, inventory entities etc.
+Any resources related to the //reflective recurse of the application on itself,// including parametrisation and customisation aspects and similar metadata, are categorised and tracked apart of the primary entities. Examples being types, scales and quantisation grids, decision rules, control data stores (automation data), annotations attached to labels, inventory entities etc.
 * __outward interface operations__ include...
 * __inward interface operations__ include...
 &rarr; MetaAsset {{red{just emerging as of 12/2010}}}
@@ -1693,6 +1693,17 @@ When building the low-level model, the actual processing code is resolved and a
 Initially, only the parameter (descriptors) are present on the effect ~MObject, while the actual [[parameter providers|ParamProvider]] are created or wired (by the ConManager) on demand.
 
+
+
A general identification scheme, ombining a human readable symbolic name, unique within a //specifically typed context,// and machine readable hash ID (LUID). ~Entry-IDs allow for asset-like position accounting and for type safe binding between configuration rules and model obects. They allow for creating an entry with symbolic id and distinct type, combined with an derived hash value, without the overhead in storage and instance management imposed by using a full-fledged Asset.
+                                                                                      
+Similar to an Asset, an identification tuple is available (generated on the fly), as is an unique LUID and total ordering. The type information is attached as template parameter, but included into the hash calculation. All instantiations of the EntryID template share a common baseclass, usable for type erased common registration.
+
+~Entry-IDs as such do not provide any automatic registration or ensure uniqueness of any kind, but they can be used to that end. Especially, they're used within the TypedID registration framework for addressing individual entries {{red{planned as of 12/2010}}}
+&rarr; TypedLookup
+&rarr; TypeHandler
+&rarr; MetaAsset
+
+
The &raquo;Timeline&laquo; is a sequence of ~MObjects -- here clips -- together with an ExplicitPlacement, locating each clip at a given time and track. (Note: I simplified the time format and wrote frame numbers to make it more clear)
 [img[Example1: Objects in the Session/Fixture|uml/fig128773.png]]
@@ -2733,11 +2744,24 @@ For the case here in question this seems to be the ''resource allocation is cons
 And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the session or the sequences, which are created once and don't bear the danger of causing memory pressure. Besides, the ProcLayer code shouldn't issue "new" and "delete" when it comes in hand, rather it should use some centralized [[Factories]] for all allocation and freeing, so we can redirect these calls down to the backend, which may use pooling or special placement allocators or the like. The rationale is, for modern hardware/architectures, care has to be taken with heap allocations, esp. with many small objects and irregular usage patterns.
 
-
-
This category is comprised of the various aspects of the way the application controls and manages its own behaviour. They are more related to the way the application behaves, as oposed to the way the editied data is structured and organised.
+
+
This category is comprised of the various aspects of the way the application controls and manages its own behaviour. They are more related to the way the application behaves, as opposed to the way the edited data is structured and organised.
 * StreamType
 * ScaleGrid
 
+!accessing meta assets
+It turns out that all meta assets follow a distinct usage pattern: //they aren't built as individual entities.// Either, they are introduced into the system as part of a larger scale configuration activity, or they are //derived from category.// The latter is a prototype-like approach; mostly, the individual entry just serves to keep track of a categorisation, while at some point, such a link into a describing category may evolve into a local differentiation of some settings.
+
+Another distinct property of meta assets is to be just a generic front-end to some very specific data entry, which needs to be allocated and maintained and provided on demand. Consider for example configuration rules, which have a textual and AST representation and will be assembled and composed into an effective rule set. Another example would be the enormous amount of parameter data created by parameter automation in the session. While certainly the raw data needs to be stored and retrieved somehow, the purpose of the corresponding meta asset is to access and manipulate this data in a structured and specific fashion.
+
+!!!self referential structure
+These observation leads to a design relying on a self referential structure: each meta asset is a {{{meta::Descriptor}}}. In the most basic version -- as provided by the generic implementation by {{{asset::Meta}}}, this descriptor is just the link to another descriptor, which represents a category. Thus, meta assets are created or accessed by
+* just an EntryID, which implicitly also establishes a type, the intent being "get me yet another of this kind"
+* a descriptor and an EntryID, to get a new element with a more distinct characterisation.
+
+!!!mutating meta assets
+Meta assets are ''immutable'' -- but they can be //superseded.//
+For each meta asset instance, initially a //builder// is created for setting up the properties; when done, the builder will "drop off" the new meta asset instance. The same procedure is used augmenting or superseding an existing element.
 
@@ -6786,7 +6810,7 @@ Using transitions is a very basic task and thus needs viable support by the GUI. Because of this experience, ichthyo wants to support a more general case of transitions, which have N output connections, behave similar to their "simple" counterpart, but leave out the mixing step. As a plus, such transitions can be inserted at the source ports of N clips or between any intermediary or final output pipes as well. Any transition processor capable of handling this situation should provide some flag, in order to decide if he can be placed in such a manner. (wichin the builder, encountering a inconsistently placed transition is just an [[building error|BuildingError]])
-
+
//drafted service as of 4/10 &mdash; &rarr;[[implementation plans|TypedLookup]]//
 A registration service to associate object identities, symbolic identifiers and types.
 
@@ -6833,7 +6857,7 @@ Just an ''registration scheme'' should be implemented right now, working complet
 see [[implementation planning|TypedLookup]]
 
-
+
TypedID is a registration service to associate object identities, symbolic identifiers and types. It acts as frontend to the TypedLookup service within Proc-Layer, at the implementation level. While TypedID works within a strictly typed context, this type information is translated into an internal index on passing over to the implementation, which manages a set of tables containing base entries with an combined symbolic+hash ID, plus an opaque buffer. Thus, the strictly typed context is required to re-access the stored data. But the type information wasn't erased entirely, so this typed context can be re-gained with the help of an internal type index. All of this is considered implementation detail and may be subject to change without further notice; any access is assumed to happen through the TypedID frontend. Besides, there are two more specialised frontends.
 
 !Front-ends
@@ -6844,7 +6868,7 @@ see [[implementation planning|TypedLookup]]
 !Tables and index
 The Table consists of several registration groups, each of which contains a hashtable and deals with one specific type. Groups are created on demand, but there is initially one group holding the internal type index (translation table). There may be even sub-groups, usable to create clusterings within one group.
 
-__Individual entries__ are comprised of a ''~EntryID'' as key (actually a ~BareEntryID, without the typing) and a payload, which //doesn't store data,// but informations necessary to ''lookup and access'' the registered object. Obviously, this information is type specific, and thus the ~TypedLookup implementation can't know how to deal with it directly. Rather, we store a ''functor'' in the payload of the type index group. This functor is directly linked to the TypeHandler, i.e. any type wanting to be a primary type within Lumiera, so as to be directly usable within the ConfigRules, needs to provide a suitable functor implementation through its ~TypeHandler. These functors are then invoked by the ~TypedID frontend, when it comes to re-accessing an registered entity by ID
+__Individual entries__ are comprised of a EntryID as key (actually a ~BareEntryID, without the typing) and a payload, which //doesn't store data,// but informations necessary to ''lookup and access'' the registered object. Obviously, this information is type specific, and thus the ~TypedLookup implementation can't know how to deal with it directly. Rather, we store a ''functor'' in the payload of the type index group. This functor is directly linked to the TypeHandler, i.e. any type wanting to be a primary type within Lumiera, so as to be directly usable within the ConfigRules, needs to provide a suitable functor implementation through its ~TypeHandler. These functors are then invoked by the ~TypedID frontend, when it comes to re-accessing a registered entity by ID
 
 !link for automatic registration
 An entity can be linked with the TypedLookup system to be registered and deregistered automatically. This is achieved by mixing in the {{{TypedID::Link}}}. On creation, this will set up an EntryID for this individual instance and cause creation of an empty entry within the suitable registration group. As a side-effect, uniqueness of any symbolic-ID within one group (type) is enforced. Obviously, the dtor of this registration Link cares for de-registration automatically. Be forwarned though, by creating an unique identity, this mechanism will interfere with copying and cloning of the registered entity.
@@ -6863,8 +6887,8 @@ Obviously, the ~TypedLookup system is open for addition of completely separate a
 |~| Sequence| Asset|
 |~| StreamType| Asset|
 |1:n| Tag| Asset|
-|~| Clip|~MObject|
-|~| Effect|~MObejct|
+|~| Clip| ~MObject|
+|~| Effect| ~MObejct|
 |~| Transition| Asset(?)|
 
From 04db5655f13fd0dc3cf84b6fa39a3ae7f76c663b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 31 Dec 2010 05:59:53 +0100 Subject: [PATCH 018/140] Implementation: building a simple time grid --- src/lib/error.hpp | 3 +- src/lib/exception.cpp | 4 +- src/lib/time/display.hpp | 49 +++++++++++++++++ src/lib/time/lumitime.cpp | 22 ++++++++ src/lib/time/timevalue.hpp | 6 +++ src/proc/asset/meta.cpp | 13 ++--- src/proc/asset/meta.hpp | 45 ++++++++++++++-- src/proc/asset/meta/time-grid.cpp | 85 ++++++++++++++++++++++++++---- src/proc/asset/meta/time-grid.hpp | 58 ++++++++++++++++---- src/proc/assetmanager.cpp | 58 ++++++++++---------- tests/lib/time/time-value-test.cpp | 2 +- 11 files changed, 285 insertions(+), 60 deletions(-) create mode 100644 src/lib/time/display.hpp diff --git a/src/lib/error.hpp b/src/lib/error.hpp index af911ac7a..51fce33e0 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -125,7 +125,8 @@ namespace lumiera { LUMIERA_ERROR_DECLARE (WRONG_TYPE); ///< runtime type mismatch LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value - LUMIERA_ERROR_DECLARE (UNCONNECTED); ///< missing connection + LUMIERA_ERROR_DECLARE (UNCONNECTED); ///< missing connection + LUMIERA_ERROR_DECLARE (UNIMPLEMENTED);///< unimplemented feature diff --git a/src/lib/exception.cpp b/src/lib/exception.cpp index bd19d30f6..82e228687 100644 --- a/src/lib/exception.cpp +++ b/src/lib/exception.cpp @@ -82,7 +82,9 @@ namespace lumiera { LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch"); LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached"); LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value"); - LUMIERA_ERROR_DEFINE (UNCONNECTED, "missing connection"); + LUMIERA_ERROR_DEFINE (UNCONNECTED, "missing connection"); + LUMIERA_ERROR_DEFINE (UNIMPLEMENTED, "using a feature not yet implemented...."); + } // namespace error diff --git a/src/lib/time/display.hpp b/src/lib/time/display.hpp new file mode 100644 index 000000000..2c829d9fc --- /dev/null +++ b/src/lib/time/display.hpp @@ -0,0 +1,49 @@ +/* + DISPLAY.hpp - formatting of time values for display + + Copyright (C) Lumiera.org + 2010, Hermann Vosseler + + 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. + +*/ + + +#ifndef LIB_TIME_DISPLAY_H +#define LIB_TIME_DISPLAY_H + +#include "lib/time/timecode.hpp" + +#include +#include + + + +namespace lib { +namespace time { + + + /** writes time value, formatted as HH:MM:SS:mmm + * @see lumiera_tmpbuf_print_time */ + inline std::ostream& + operator<< (std::ostream& os, Time const& t) + { + return os << std::string(t); + } + + + +}} // lib::time +#endif diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index 8d9bba707..072165aa5 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -30,6 +30,7 @@ extern "C" { #include #include +#include using std::string; @@ -37,6 +38,18 @@ using std::string; namespace lib { namespace time { + namespace { + /** implementation detail: convert a rational number denoting fractionalSeconds + * into the internal time scale used by GAVL and Lumiera internal time values. + */ + inline gavl_time_t + rational2time (TimeFract const& fractionalSeconds) + { + return boost::rational_cast (GAVL_TIME_SCALE * fractionalSeconds); + } + } + + const Time Time::MAX ( TimeValue (+std::numeric_limits::max()) ); const Time Time::MIN ( TimeValue (-std::numeric_limits::max()) ); @@ -60,6 +73,15 @@ namespace time { { } + /** convenience constructor to build an Time value + * from a fraction of seconds, given as rational number. + * An example would be to the time unit of a framerate. + */ + Time::Time (TimeFract const& fractionalSeconds) + : TimeValue(rational2time (fractionalSeconds)) + { } + + /** displaying an internal Lumiera Time value * for diagnostic purposes or internal reporting. * @warning internal Lumiera time values refer to an diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 5e0e6f28a..9f2888588 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -25,6 +25,7 @@ #define LIB_TIME_TIMEVALUE_H #include +#include #include #include @@ -182,6 +183,9 @@ namespace time { /* ======= specific Time entities ==================== */ + /** rational representation of fractional seconds */ + typedef boost::rational TimeFract; + /** * Lumiera's internal time value datatype. * This is a TimeValue, but now more specifically denoting @@ -219,6 +223,8 @@ namespace time { : TimeValue(calcResult) { } + Time (TimeFract const& fractionalSeconds); + Time ( long millis , uint secs , uint mins =0 diff --git a/src/proc/asset/meta.cpp b/src/proc/asset/meta.cpp index 9ac77cac1..09a771068 100644 --- a/src/proc/asset/meta.cpp +++ b/src/proc/asset/meta.cpp @@ -38,7 +38,7 @@ namespace asset { namespace meta { - Descriptor::~Descriptor() { } // emit vtable here... + Descriptor::~Descriptor() { } // emit VTable here... } @@ -54,8 +54,8 @@ namespace asset { * created as a side effect of calling the concrete Meta subclass ctor. */ template - P - MetaFactory::operator() (EntryID elementIdentity) + meta::Builder + MetaFactory::operator () (EntryID elementIdentity) { UNIMPLEMENTED ("Meta-Factory"); } @@ -70,7 +70,7 @@ namespace asset { * created as a side effect of calling the concrete Meta subclass ctor. */ template - P + meta::Builder MetaFactory::operator() (Descriptor const& prototype, EntryID elementIdentity) { UNIMPLEMENTED ("Meta-Factory"); @@ -96,10 +96,11 @@ namespace asset { namespace asset { using meta::Descriptor; + using meta::Builder; using meta::TimeGrid; - template P MetaFactory::operator() (EntryID); + template Builder MetaFactory::operator() (EntryID); - template P MetaFactory::operator() (Descriptor const&, EntryID); + template Builder MetaFactory::operator() (Descriptor const&, EntryID); } // namespace asset diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp index cc0f569a2..21f029c14 100644 --- a/src/proc/asset/meta.hpp +++ b/src/proc/asset/meta.hpp @@ -22,14 +22,34 @@ /** @file meta.hpp - ** Internal and organisational metadata. Some internally created data elements, - ** like automation data sets, inventory of session contents, can be exposed and - ** treated as specific Kind of Asset. + ** Internal and organisational metadata. Some internally created data elements + ** rather serve the purpose of controlling the way the application behaves, as + ** opposed to organising the \link struct.hpp structure \endlink of the data the + ** user works with. Lumiera exposes this self-referential control and customisation + ** aspects as a special kind of Asset. Examples being types, scales and quantisation + ** grids, decision rules, control data stores (automation data), annotations attached + ** to labels, inventory entities etc. ** ** For the different Kinds of Assets, we use sub-interfaces inheriting ** from the general Asset interface. To be able to get asset::Meta instances ** directly from the AssetManager, we define a specialisation of the Asset ID. ** + ** \par using meta assets + ** The usage pattern of asset::Meta entities differs from the other assets, + ** insofar they aren't created as individual entries, rather added as part + ** of a larger scale configuration activity, or they are derived from category. + ** The latter fits in with a prototype-like approach; initially, the individual + ** entry just serves to keep track of a categorisation, while at some point, + ** such a link into a describing category may evolve into a local + ** differentiation of some settings (copy on modification). + ** + ** To cope with this special usage, the meta assets are defined to be immutable. + ** They are created from a descriptor, which stands for a category or sub-category + ** and can be another already existing asset::Meta (inheriting from meta::Descriptor) + ** Generally this yields a Builder object, which can be used for outfitting the new + ** or changed metadata entry, finally \em committing this builder to yield a new + ** asset::Meta (which, in case of a mutation, might superseede an existin one). + ** ** @see asset.hpp explanation of assets in general ** @see MetaFactory creating concrete asset::Meta instances ** @@ -71,6 +91,21 @@ namespace asset { public: virtual ~Descriptor(); ///< this is an ABC }; + + /** + * Building and configuring a meta asset. + * The finished elements are defined to be immutable, + * thus, on creation or when changing / superseding a + * meta asset, the client gets a special builder instance, + * which is a value object for configuring the specific details + * to set. When done, the client invokes a \c commit() function, + * which yields a smart-ptr to the new meta asset. + * Individual meta asset subclasses are bound to define a + * specialisation of this Builder template, which will then be + * instantiated and provided by the MetaFactory. + */ + template + struct Builder; } @@ -116,10 +151,10 @@ namespace asset { typedef P PType; template - P operator() (EntryID elementIdentity); + meta::Builder operator() (EntryID elementIdentity); template - P operator() (meta::Descriptor const& prototype, EntryID elementIdentity); + meta::Builder operator() (meta::Descriptor const& prototype, EntryID elementIdentity); }; diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index e969bf645..f7e1e6d5e 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -23,29 +23,94 @@ #include "proc/asset/meta/time-grid.hpp" #include "proc/asset/entry-id.hpp" +#include "proc/assetmanager.hpp" +//#include "lib/time/quantiser.hpp" +#include "lib/time/timevalue.hpp" +#include "lib/time/display.hpp" //#include "lib/util.hpp" //#include "include/logging.h" +#include +#include + +using boost::format; +using boost::str; +using std::string; + namespace asset { namespace meta { + namespace error = lumiera::error; + namespace { - typedef TimeGrid::Builder Builder; - - Asset::Ident - deriveAssetIdentity (Builder const& setup) - { - EntryID wtf("wtf**ck"); //////////////////////TODO some real meat here - return wtf.getIdent(); - } + // Implementation details (still required???) } /** */ - TimeGrid::TimeGrid (Builder const& setup) - : Meta (deriveAssetIdentity (setup)) + TimeGrid::TimeGrid (EntryID const& nameID) + : Meta (nameID.getIdent()) { } + + using lib::time::Time; + using lib::time::TimeValue; + using lib::time::TimeSpan; + using lib::time::Duration; + using lib::time::Offset; + + /** + * TimeGrid implementation: a trivial time grid, + * starting at a given point in time and using a + * constant grid spacing + */ + class SimpleTimeGrid + : public TimeGrid + { + TimeSpan timeBase_; + + // TODO implement the TimeGrid API here + + public: + SimpleTimeGrid (Time start, Duration gridSpacing, EntryID const& name) + : TimeGrid (name) + , timeBase_(start,gridSpacing) + { } + }; + + + + /** Setup of a TimeGrid: validate the settings configured into this builder instance, + * then decide on the implementation strategy for the time grid. Convert the given + * frames per second into an appropriate gridSpacing time and build a suitable + * name-ID to denote the TimeGrid-meta-Asset to be built. + * @return shared_ptr holding onto the new asset::Meta, which has already been + * registered with the AssetManager. + * @throw error::Config in case of invalid frames-per-second. The AssetManager + * might raise further exception when asset registration fails. + * @todo currently (12/2010) the AssetManager is unable to detect duplicate assets. + * Later on the intention is that in such cases, instead of creating a new grid + * we'll silently return the already registered exisiting and equivalent grid. + */ + P + Builder::commit() + { + if (predecessor_) + throw error::Invalid("compound and variable time grids are a planned feature" + , error::LUMIERA_ERROR_UNIMPLEMENTED); + if (!fps_) + throw error::Config ("attempt to build a TimeGrid with 0 frames per second"); + + Time spacing = 1 / fps_; // seconds per frame + + format gridIdFormat("grid_%f_%s"); + string name = str(gridIdFormat % fps_ % origin_); + EntryID nameID (name); + TimeGrid& newGrid (*new SimpleTimeGrid(origin_, Offset(spacing), nameID)); + + return AssetManager::instance().wrap (newGrid); + } + }} // namespace asset::meta diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 50e6d382f..3dcf8d6aa 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -34,6 +34,11 @@ ** Time grid specifications are integrated into Lumiera's framework ** for meta assets, automation, configuration and similar metadata. ** + ** \par using time grids + ** TimeGrid is an interface (ABC), but provides some actual factory + ** functions, which can be used as a convenience shortcut to fabricate + ** the kind of simple time grid used most often. + ** ** @see MetaFactory creating concrete asset::Meta instances ** @see time::Quantiser ** @@ -43,14 +48,14 @@ #define ASSET_META_TIME_GRID_H #include "proc/asset/meta.hpp" -#include "lib/time/quantiser.hpp" +#include "lib/time/timevalue.hpp" namespace asset { namespace meta { - + /** * Interface: a grid and scale for time quantisation. * This meta-Asset describes a coordinate system or @@ -61,17 +66,52 @@ namespace meta { { public: - struct Builder - { - - }; + + // TODO define the TimeGrid API here + protected: - friend class Builder; - - TimeGrid (Builder const& setup); //////////////TODO + TimeGrid (EntryID const&); }; + + + + using lib::time::Time; + using lib::time::TimeValue; + using lib::time::TimeFract; + + + template<> + struct Builder + { + TimeFract fps_; + Time origin_; + + /** when building a compound or variable grid, + * the predecessor is the grid active \em before + * the origin of this (local) grid. + * @todo currently not supported (as of 12/2010) + */ + P predecessor_; + + /** + * initialise to blank (zero). + * You need at least to set the framerate, + * in order to create a usable TimeGrid + */ + Builder() + : fps_(0) + , origin_(TimeValue(0)) + , predecessor_() + { } + + /** create a time grid + * based on settings within this builder + */ + P commit(); + + }; }} // namespace asset::meta #endif diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 0cf1a989c..6677b0cca 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -52,7 +52,7 @@ namespace asset { IDErr (const char* eID, format fmt) : lumiera::error::Invalid(fmt.str(),eID) {} }; - + // ------pre-defined-common-error-cases--------------- // @@ -88,8 +88,8 @@ namespace asset { AssetManager::AssetManager () : registry (Singleton() ()) { } - - + + /** provide the unique ID for given Asset::Ident tuple */ ID @@ -117,7 +117,7 @@ namespace asset { DB::Lock guard(®istry); TODO ("handle duplicate Registrations"); P smart_ptr (obj, &destroy); - + registry.put (asset_id, smart_ptr); return asset_id; } @@ -155,9 +155,9 @@ namespace asset { return static_pointer_cast (instance().registry.get (asset.id)); } - - - + + + /** * @return true if the given id is registered in the internal asset DB */ @@ -167,10 +167,10 @@ namespace asset { return ( registry.get (ID(id)) ); } // query most general Asset ID-kind and use implicit // conversion from smart-ptr to bool (test if empty) - - + + /** - * @return true if the given id is registered with the given Category + * @return true if the given id is registered with the given Category */ bool AssetManager::known (IDA id, const Category& cat) @@ -178,23 +178,23 @@ namespace asset { PAsset pA = registry.get (id); return ( pA && pA->ident.category.isWithin(cat)); } - + namespace { // details implementing AssetManager::remove - + void recursive_call (AssetManager* instance, PAsset& pA) { instance->remove (pA->getID()); } - + function - detach_child_recursively () ///< @return a functor recursively invoking remove(child) + detach_child_recursively () ///< @return a functor recursively invoking remove(child) { return bind( &recursive_call, &AssetManager::instance(), _1 ); } } - + /** * remove the given asset from the internal DB * together with all its dependents @@ -203,11 +203,11 @@ namespace asset { AssetManager::remove (IDA id) { PAsset asset = getAsset (id); - for_each (asset->dependants, detach_child_recursively()); ///// + for_each (asset->dependants, detach_child_recursively()); asset->unlink(); registry.del(id); } - + list @@ -218,7 +218,7 @@ namespace asset { res.sort(); return res; } - + } // namespace asset @@ -229,15 +229,16 @@ namespace asset { /* explicit template instantiations for various Asset Kinds */ /************************************************************/ -#include "proc/asset/media.hpp" -#include "proc/asset/clip.hpp" -#include "proc/asset/proc.hpp" -#include "proc/asset/struct.hpp" -#include "proc/asset/pipe.hpp" -#include "proc/asset/meta.hpp" -#include "proc/asset/procpatt.hpp" -#include "proc/asset/timeline.hpp" -#include "proc/asset/sequence.hpp" +#include "proc/asset/media.hpp" +#include "proc/asset/clip.hpp" +#include "proc/asset/proc.hpp" +#include "proc/asset/struct.hpp" +#include "proc/asset/pipe.hpp" +#include "proc/asset/meta.hpp" +#include "proc/asset/procpatt.hpp" +#include "proc/asset/timeline.hpp" +#include "proc/asset/sequence.hpp" +#include "proc/asset/meta/time-grid.hpp" namespace asset { @@ -260,5 +261,8 @@ namespace asset { template P AssetManager::wrap (const Timeline& asset); template P AssetManager::wrap (const Sequence& asset); + using meta::TimeGrid; + template P AssetManager::wrap (const TimeGrid& asset); + } // namespace asset diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index a56d0cd93..ee7e207f7 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -201,7 +201,7 @@ namespace test{ TimeValue zero; TimeValue five(5); - TimeSpan interval (Time(org), Duration(Offset (org,five))); + TimeSpan interval (Time(org), Duration(Offset (org,five))); /////////////TODO need more convenient constructors // the time span behaves like a time CHECK (org == interval); From f798428428e5adfcd4ff0990cf5b35af89902628 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 31 Dec 2010 23:04:13 +0100 Subject: [PATCH 019/140] WIP start a unit test to cover simple time grids --- src/proc/asset/meta/time-grid.hpp | 2 + tests/41asset.tests | 5 + tests/components/Makefile.am | 1 + .../proc/asset/meta/time-grid-basics-test.cpp | 129 ++++++++++++++++++ wiki/renderengine.html | 6 +- 5 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 tests/components/proc/asset/meta/time-grid-basics-test.cpp diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 3dcf8d6aa..9c0b9ee3b 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -74,6 +74,8 @@ namespace meta { TimeGrid (EntryID const&); }; + typedef lumiera::P PGrid; + diff --git a/tests/41asset.tests b/tests/41asset.tests index 332de28aa..c218522fa 100644 --- a/tests/41asset.tests +++ b/tests/41asset.tests @@ -68,3 +68,8 @@ END TEST "OrderingOfAssets_test" OrderingOfAssets_test < + + 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 "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" + +#include "proc/asset/meta.hpp" +#include "proc/asset/meta/time-grid.hpp" +//#include "proc/asset/entry-id.hpp" +//#include "lib/p.hpp" +//#include "proc/assetmanager.hpp" +//#include "proc/asset/inventory.hpp" +//#include "proc/mobject/session/clip.hpp" +//#include "proc/mobject/session/track.hpp" +//#include "lib/meta/trait-special.hpp" +//#include "lib/util-foreach.hpp" +//#include "lib/symbol.hpp" + +//#include +//#include +//#include + +//using lib::test::showSizeof; +//using lib::test::randStr; +//using util::isSameObject; +//using util::and_all; +//using util::for_each; +//using util::isnil; +using lib::test::randStr; +//using lib::Literal; +//using lib::Symbol; +//using lumiera::P; +//using std::string; +//using std::cout; +//using std::endl; + + + +namespace asset { +namespace meta { +namespace test { + + typedef Builder GridBuilder; + typedef EntryID GridID; + + namespace { // Test definitions... + + Time testOrigin (12,23); + TimeFract testFps (5,4); + + } + + + + /*************************************************************************** + * @test build some simple time grids and verify their behaviour + * for quantising (grid aligning) time values. + * + * @todo WIP-WIP-WIP ////////////////////TICKET #736 + * + * @see asset::meta::TimeGrid + * @see time-quantisation-test.cpp usage context + */ + class TimeGridBasics_test : public Test + { + + virtual void + run (Arg) + { + createGrid_fullProcedure(); + createGrid_simplified(); + } + + + void + createGrid_fullProcedure() + { + GridID myGrID (randStr(8)); + GridBuilder spec = asset::Meta::create (myGrID); + + CHECK (!spec.fps_); + CHECK (spec.origin_ == Time(0)); + CHECK (!spec.predecessor_); + + spec.fps_ = testFps; + spec.origin_ = testOrigin; + + PGrid myGrid = spec.commit(); + CHECK (myGrid); + + UNIMPLEMENTED ("the basic Grid API, so we can actually check anything"); + } + + + void + createGrid_simplified() + { + } + + + }; + + + + /** Register this test class... */ + LAUNCHER (TimeGridBasics_test, "unit asset"); + + +}}} // namespace asset::meta::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 9b6fbed93..5f0185605 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2744,15 +2744,15 @@ For the case here in question this seems to be the ''resource allocation is cons And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the session or the sequences, which are created once and don't bear the danger of causing memory pressure. Besides, the ProcLayer code shouldn't issue "new" and "delete" when it comes in hand, rather it should use some centralized [[Factories]] for all allocation and freeing, so we can redirect these calls down to the backend, which may use pooling or special placement allocators or the like. The rationale is, for modern hardware/architectures, care has to be taken with heap allocations, esp. with many small objects and irregular usage patterns.
-
+
This category is comprised of the various aspects of the way the application controls and manages its own behaviour. They are more related to the way the application behaves, as opposed to the way the edited data is structured and organised.
 * StreamType
 * ScaleGrid
 
 !accessing meta assets
-It turns out that all meta assets follow a distinct usage pattern: //they aren't built as individual entities.// Either, they are introduced into the system as part of a larger scale configuration activity, or they are //derived from category.// The latter is a prototype-like approach; mostly, the individual entry just serves to keep track of a categorisation, while at some point, such a link into a describing category may evolve into a local differentiation of some settings.
+It turns out that all meta assets follow a distinct usage pattern: //they aren't built as individual entities.// Either, they are introduced into the system as part of a larger scale configuration activity, or they are //derived from category.// The latter fits in with a prototype-like approach; initially, the individual entry just serves to keep track of a categorisation, while at some point, such a link into a describing category may evolve into a local differentiation of some settings.
 
-Another distinct property of meta assets is to be just a generic front-end to some very specific data entry, which needs to be allocated and maintained and provided on demand. Consider for example configuration rules, which have a textual and AST representation and will be assembled and composed into an effective rule set. Another example would be the enormous amount of parameter data created by parameter automation in the session. While certainly the raw data needs to be stored and retrieved somehow, the purpose of the corresponding meta asset is to access and manipulate this data in a structured and specific fashion.
+Another distinct property of meta assets is to be just a generic front-end to some very specific data entry, which needs to be allocated and maintained and provided on demand. Consider for example configuration rules, which have both a textual and an AST representation and will be assembled and composed into an effective rule set, depending on usage scope. Another example would be the enormous amount of parameter data created by parameter automation in the session. While certainly the raw data needs to be stored and retrieved somehow, the purpose of the corresponding meta asset is to access and manipulate this data in a structured and specific fashion.
 
 !!!self referential structure
 These observation leads to a design relying on a self referential structure: each meta asset is a {{{meta::Descriptor}}}. In the most basic version -- as provided by the generic implementation by {{{asset::Meta}}}, this descriptor is just the link to another descriptor, which represents a category. Thus, meta assets are created or accessed by

From f832e817b815534e4796f5507507f0985e16ad5e Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 1 Jan 2011 03:56:31 +0100
Subject: [PATCH 020/140] WIP: test-driven brainstorming how quantisation could
 be used...

---
 tests/lib/time/time-quantisation-test.cpp | 82 ++++++++++++++++++++---
 tests/lib/time/time-value-test.cpp        |  1 +
 2 files changed, 73 insertions(+), 10 deletions(-)

diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp
index e490661dd..a770d2f78 100644
--- a/tests/lib/time/time-quantisation-test.cpp
+++ b/tests/lib/time/time-quantisation-test.cpp
@@ -22,18 +22,22 @@
 
 
 #include "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
 #include "lib/time/quantiser.hpp"
 #include "lib/util.hpp"
 
 #include 
-//#include 
+#include 
+#include 
 //#include 
 
 using boost::lexical_cast;
 using util::isnil;
 //using std::rand;
-//using std::cout;
-//using std::endl;
+using std::cout;
+using std::endl;
+
+using boost::algorithm::join;
 
 
 namespace lib {
@@ -42,7 +46,7 @@ namespace test{
   
   
   /********************************************************
-   * @test verify handling of time values, time intervals.
+   * @test verify handling of quantised time values.
    *       - creating times and time intervals
    *       - comparisons
    *       - time arithmetics
@@ -55,30 +59,88 @@ namespace test{
           long refval= isnil(arg)?  1 : lexical_cast (arg[1]);
           
           TimeValue ref (refval);
+          CHECK (Time(0) < ref);
           
-          checkBasics (ref);
-          checkComparisons (ref);
-          checkComponentAccess();
+          checkUsage (ref);
+          check_theFullStory (ref);
+          checkMultipleGrids (ref);
         } 
       
       
       void
-      checkBasics (TimeValue ref)
+      checkUsage (TimeValue org)
         {
+          TimeGrid::build("my_simple_grid", 25);    // "someone" has defined a time grid
+          
+          QuTime qVal (org, "my_simple_grid");      // create time quantised to this grid
+          
+          FrameNr count(qVal);                      // materialise this quantised time into..
+          int n = count;                            // frame count, accessible as plain number
+          
+          CHECK (TimeFract(n-1, 25) < org);         // verify quantisation: the original time
+          CHECK (org < TimeFract(n+1, 25));         // is properly bracketed by (n-1, n+2)
         }
       
       
       void
-      checkComparisons (TimeValue ref)
+      check_theFullStory (TimeValue org)
         {
+          FixedFrameQuantiser fixQ(25);
+          QuTime qVal (org, fixQ);
+          
+          CHECK (contains (qVal.supportedFormats, format::FRAMES));
+          CHECK (contains (qVal.supportedFormats, format::SMPTE));
+          
+          TCode smpteTCode = qVal.formatAs();
+          showTimeCode (smpteTCode);
+          
+          TCode frameTCode = qVal.formatAs();
+          showTimeCode (frameTCode);
+          
+          FrameNr count(frameTCode);
+          CHECK (string(count) == *(frameTCode.components()));
+        }
+      
+      template
+      void
+      showTimeCode (TC timecodeValue)
+        {
+          cout << timecodeValue.describe() << " = " << join (timecodeValue.components(), ":") << endl;
         }
       
       
       void
-      checkComponentAccess()
+      checkMultipleGrids (TimeValue org)
         {
+          TimeGrid::build("my_alternate_grid", TimeFract(30000,1001));
+          
+          QuTime palVal (org, "my_simple_grid");
+          QuTime ntscVal (org, "my_alternate_grid");
+          
+          CHECK (org == palVal);
+          CHECK (org == ntscVal);
+          
+          FrameNr palNr (palVal);
+          FrameNr ntscNr(ntscVal);
+          CHECK (palNr < ntscNr);
         }
       
+      
+      void
+      checkGridLateBinding (TimeValue org)
+        {
+          QuTime funny (org, "special_funny_grid");      // refer a not yet existing grid
+          CHECK (org == funny);                          // no problem, unless we request quantisation
+          
+          VERIFY_ERROR (UNKNOWN_GRID, funny.formatAs() );
+          
+          TimeGrid::build("special_funny_grid", 1);      // provide the grid's definition (1 frame per second)
+          
+          int cnt = funny.formatAs();    // and now performing quantisation is OK 
+          SmpteTC smpte (funny);                         // also converting into SMPTE (which implies frame quantisation)
+          CHECK (0 == smpte.getFrames());                // we have 1fps, thus the frame part is always zero!
+          CHECK (cnt % 60 == smpte.getSecs());           // and the seconds part will be in sync with the frame count
+        }
     };
   
   
diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp
index ee7e207f7..7511fb0ff 100644
--- a/tests/lib/time/time-value-test.cpp
+++ b/tests/lib/time/time-value-test.cpp
@@ -185,6 +185,7 @@ namespace test{
           CHECK (distance > zero);
           CHECK (distance == backwards.abs());
           
+          // assign to variable for calculations
           point = backwards;
           point *= 2;
           CHECK (point < zero);

From 15214cc0693205788508836fe60cb1d5ab07d00b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 2 Jan 2011 01:21:39 +0100
Subject: [PATCH 021/140] WIP start stubbing and defining time quantisation and
 timecode entities

---
 .gitignore                                |   1 +
 doc/devel/uml/fig142725.png               | Bin 14152 -> 18325 bytes
 src/lib/Makefile.am                       |   1 +
 src/lib/time/formats.hpp                  | 124 ++++++++++++++++++
 src/lib/time/lumitime.cpp                 |   2 +-
 src/lib/time/quantiser.hpp                |  20 +++
 src/lib/time/timecode.cpp                 |  44 +++++++
 src/lib/time/timecode.hpp                 |  25 +++-
 src/lib/time/timequant.hpp                |  58 +++++++++
 src/proc/asset/meta/time-grid.cpp         |  17 +++
 src/proc/asset/meta/time-grid.hpp         |  19 ++-
 tests/lib/time/time-quantisation-test.cpp |  14 +-
 uml/lumiera/128517                        | 149 ++++++++++++++++++++--
 uml/lumiera/142725.diagram                | 135 ++++++++++++++------
 uml/lumiera/lumiera.prj                   |   2 +-
 15 files changed, 546 insertions(+), 65 deletions(-)
 create mode 100644 src/lib/time/formats.hpp
 create mode 100644 src/lib/time/timecode.cpp
 create mode 100644 src/lib/time/timequant.hpp

diff --git a/.gitignore b/.gitignore
index 8ba9734d1..c5b3efe27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,5 @@ autom4te.cache
 semantic.cache
 tests/,*
 wiki/backups/*
+doc/devel/uml/fig128309.png
 m4/*
diff --git a/doc/devel/uml/fig142725.png b/doc/devel/uml/fig142725.png
index e69ceab0e28613de3f80e6fd2e6fcd910b196690..08a0e46ef17a0ab88e58084bb711fac4ea176a8d 100644
GIT binary patch
literal 18325
zcmc(H1zc3?`tB%-D4-x9(gM;FlF|(l64Kq$-8m>A2vSngDIncQhjfid3_a4_3?27l
z@3YU@cb|R!_x^73({VAbS+mx+zVCgX_j#UoeNd2-#Jop%4+4Q;O1%_&4T0Pg2Y;2(
zZi08BW7~5fkS7o+F=1u5q|IqhZCvEs)h;Y3?Mb$d>~Ot)a7M2B40Do-%-{>%hui+a
zR9U%cpQMA+Od|E&Zbye1EHUz#n{&x5#L>{nx18i9;c@Why7wNKnFoJs63jltLhmv$
zcq4e`A>eiVIXOA`qHln8`@(C9EmcvLItl`5?il$|3(iCRhC3=G;{Vx;aQ~VYo48`$
z{$@eDF{`=GEl9JsvSrF$a@lN@k@K*aELzmU5D4U(-~`bP26_%BB~Wu+;W>`yMvPP!GyPxvqN2w1bcO&dwc?q_&kfV_^NVv#^*uadu`+BCVWEWl=d*!&@v_zqRSz9q|_Y($^U5!jYvx)?+j1<5cHB=9*w;5f)j4mx4(m?
z5XbGh!G5C#QwPVhz
z$k7{+0+}ZFH>9#zJGm5K%xw?@h$=>UHn!#CUA9*V>{udB+gsxm=6)mjSB(yf7|x{x
zY^v>@@@$Xm;l)>W+cSNw`GqRIifEAur>kFN)MPYhAk9@P)eWWUGO@R~d3oL4Ziz|Q
zn8Ax`{6sOy{WEC6C9=fJW=aqYw`a>C6WL6syCM~x*K5(p<&sYa>)$W+6Wu49p|q)nbL6OG@PNdJ7GiGF(ZWaMmNj2H_(KZ7rNOXzWkK`*G}`lzU<#G;y5{j
zRWy9{4~R7yga8|r_yMq<;^|-M&D&L9&@s6+|)~j4FPTLuL!di#6pvLX`{k?M7Ve)gMkyyr5
zbn?ADrp2X{@yd0T-@fK#uQDj8jh@Lt@qaC9j$NwTlim0c>$Cri$>xm6*1vwQCpwbS
zJC-SV?{r;hC94wJ?yypMKMr*i9iLZde!gv{1dRGRu6^@Bb_8Lg>*#p%^P>z6
zmHT5|UMh+Nv8pZ=e346@K=rF``NinJGTJ|+zf+`#K%OEKw;Su^(b83KQpNc2W
z%gaCJVgETY!o}`nQo6W3S#6Xl@c?=}t41AV4oeZ8?|gtIVl{DKzCMbRl$7G&NQ6yP
z*Iqwx8+}wh;lRV$#yp1bh25{FOC&RSTNnxNn}MVngYWMLl6j9dC#yvR@9!_eiWhnc
zzlB50K(uq&Tc}ClpG+h>Tw8RWYttZady4`R!1%arJdMx!uC$1Vrs3QMCK1Jbk{)c4
zXIfR`W_`Uq(!2ZX+%{96-o5iQH<$6py7QoD@Qso5()^J9U`Dg^x(g=JC@+skSwd&*
zqN`o*a}TE`e$Pf8hlMjXQ>IW7UXVIWVd*6-S4m03K|!U8ibVbE^-imr`oDzwIlCur
z-n?05i(v8}t9Rc=XRf!KYx{s)heJ;mDs`0
zr_<$&$PF+Rx28Q0H^$*bI_p2uMT>PC9zJ|X>a`;*DcQMJBUiI=mP>dKCGF`c|5|@C
zFZGKTmwStrGc$!{WpZup)`$}Fj4>+Psc-I^6U=9a?b}oFrB>rhU{L-h0V%AzEW`B7
zhgW9Tro+xyT#ad`GF!^dMS`}b`4c&GKR-1!!~LZYXHO^W%m
zK^d8uBj%Pmq*)_X(OL9WgG*fpw^8&zki|qq-0$>5RT$}Fdznipmv`d_(H@2a#ZQQ*
zXo|3=7@@ve7hBiImna*7jZDg)%(2wttJ<@_MrI$*o8&+q4ymPdJDuq_AmPmH*5ODz
z=X{gXcQQG*tM!{
zkb8SQ{rw}gPap^H_`avQd3wsnvwpFJ7xgD{!E2v=A>j8oC^hP=V7d(<8HAs(nHAYg
z*E%ls4c6iwE&uwJ=8O$lK}UiFh0%5WaxgMOs|0%!UnNesfh%Ol(^K9GZ|-PoGgJT6
z6J@S-P@wJSTcQN18Jt1;2kw5W{Jo0x9%J5p1QSSgAj1a|i7Qqxp*?M@M*krwwbyq(
z8Si($Aj}qDAIG?>oB~sr*hyd9s}7=Nu`8<%bBS
z=BhEfxI$J^1uIvSbaL?D{iU;f%vy1yk!4ZLsh<1^DK7hZ27bS%t0baN_6BkDU#7=h`P?Z$T7X
zJiew9gt`YCD&YE+?>9Gkn|fVcgyX4q$1uR>=c*FnZx$(TLY!oo-Ls|T<=wU$J|}RH
zh={7G{#uM<)(Z)Gi7UeQJGj@XbAmuFeyXJqV9faV@JD@G^4ja>YPxXAvEz_Y#E>vb
z;W2G_hm4|bX#5Kje?;!z1dYbZkWc-w4!3PQ+Y%se8_dk{L?&#_v3B)Z{>0bwqPfhi
z4e&2;`}LMQQ-U$uRRGc@tmmF>G1%H~%&JrGR+)91NvHPF;}>=y?SKHv&HWV{+bA=G
z^B(!npRs7<_er&lbRBx4xk#b)yIhE;p^pb$`#!JKxuOrBB=hj6L|`Sv#zw}uehsch
zg(#35jFBw_k}=T+|16pH6Fj#Q#&vz``1+@nWM=~S&7e0HCU|*lN#2&sd(vLC))l!8
zFT&j4bFdvWJZC7bcf35ubM|@Jr9j>mO=mk*>s)uQ8jQ8hfr=on-4joMy(1#r^bj_c
zmb@c*i?sePUtKtz>{$okJ31B6`G0kY{H
zH@pt2AxW(&D`KpjJootM=pY=L0hdj}dP{f<32%nws8ep0^$mzJjrin!?BI+Q>-zka
z6*K3Ab(q5E472mjn`us`d8s!}v#R8VBrsP9t}ENOq-R_=Plb~)9+t!u+fyYTbhD*B
zUTT8wlRaLB=?~psxtz3Ah?c&Li8O6a&6SdHx-kMXW>um7Spj21z5iY=!q|6-9>;uQ
zIezAJ-BRb4GFNZ5ID|0IF3UXC)K@h(;>M|R=}StOzU%d-+S}_*NwCS8RUavlBJ88w
z1tC%?0`ERyqc`>~Wbn)ap$!UbD2I37?ppE7Jlw^4xZP3NEx≪
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

diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index a00b4bdc2..8374eab38 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -62,6 +62,7 @@ liblumiera_la_SOURCES =					\
 	$(liblumiera_la_srcdir)/time.c			\
 	$(liblumiera_la_srcdir)/time/lumitime.cpp	\
 	$(liblumiera_la_srcdir)/time/quantiser.cpp	\
+	$(liblumiera_la_srcdir)/time/timecode.cpp	\
 	$(liblumiera_la_srcdir)/tmpbuf.c		\
 	$(liblumiera_la_srcdir)/util.cpp
 
diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp
new file mode 100644
index 000000000..5136dcf64
--- /dev/null
+++ b/src/lib/time/formats.hpp
@@ -0,0 +1,124 @@
+/*
+  FORMATS.hpp  -  formats for displaying and specifying time
+
+  Copyright (C)         Lumiera.org
+    2010,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+#ifndef LIB_TIME_FORMATS_H
+#define LIB_TIME_FORMATS_H
+
+#include "lib/time/timevalue.hpp"
+
+//#include 
+#include 
+
+
+namespace lib {
+namespace time {
+  
+  
+  /**
+   * descriptor for a time representation format (ABC).
+   * A time format describes how to specify time; it allows
+   * to format a time value and to parse a textual representation.
+   * @note while Format is an generic descriptor, the actual
+   *       TCode (timecode) values are time values, which \em
+   *       use a specific format, given as template parameter
+   * 
+   * @todo WIP-WIP-WIP
+   */
+  class Format
+    {
+      
+    public:
+      virtual ~Format();
+    };
+  
+  namespace format {
+    
+    /** 
+     * Frame count as timecode format.
+     * An integral number used to count frames
+     * can be used as a simple from of time code.
+     * Indeed the Lumiera backend mostly relies on
+     * these frame counts. As with any timecode, the
+     * underlying framerate/quantisation remains implicit.
+     */
+    class Frames
+      : public Format
+      {
+        
+      };
+    
+    
+    /**
+     * Widely used standard media timecode format.
+     * A SMPTE timestamp addresses individual frames,
+     * by specifying time as hour-minute-second plus the
+     * frame number within the actual second.
+     */
+    class Smpte
+      : public Format
+      {
+        
+      };
+    
+    
+    /**
+     * The informal hours-minutes-seconds-millisecond timecode.
+     * As such, this timecode is quantisation agnostic, but usually
+     * it is used to address some frame or block or otherwise quantised
+     * entity in time. HMS-Timecode is similar to SMPTE, but uses a
+     * floating point milliseconds value instead of the frame count
+     */
+    class Hms
+      : public Format
+      {
+        
+      };
+    
+    
+    /**
+     * Simple timecode specification as fractional seconds.
+     * Similar to HMS, a specification of seconds is quantisation agnostic,
+     * but usually some implicit quantisation is used anyway, be it on actual
+     * data frames, audio frames, or just on some smaller time interval, e.g.
+     * full milliseconds.
+     * @note Seconds is implemented as rational number and thus uses
+     *       decimal format, not the usual sexagesimal time format
+     */
+    class Seconds
+      : public Format
+      {
+        TimeFract secs_;
+      };
+    
+    
+    
+    /* ==== predefined constants for specifying the format to use ==== */
+      
+    static const Seconds SECONDS;
+    static const Frames  FRAMES;
+    static const Smpte   SMPTE;
+    static const Hms     HMS;
+  
+  
+}}} // lib::time::format
+#endif
diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp
index 072165aa5..0aeacf9fb 100644
--- a/src/lib/time/lumitime.cpp
+++ b/src/lib/time/lumitime.cpp
@@ -56,7 +56,7 @@ namespace time {
   
   /** convenience constructor to build an
    *  internal Lumiera Time value from the usual parts
-   *  of an hexagesimal time specification. Arbitrary integral
+   *  of an sexagesimal time specification. Arbitrary integral
    *  values are acceptable and will be summed up accordingly.
    *  The minute and hour part can be omitted.
    * @warning internal Lumiera time values refer to an
diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp
index 9db7d88c2..29ab6d9b8 100644
--- a/src/lib/time/quantiser.hpp
+++ b/src/lib/time/quantiser.hpp
@@ -25,6 +25,7 @@
 #define LIB_TIME_QUANTISER_H
 
 #include "lib/time/timevalue.hpp"
+#include "lib/time/formats.hpp"
 
 //#include 
 #include 
@@ -47,5 +48,24 @@ namespace time {
   
   
   
+  /**
+   * Simple stand-alone Quantiser implementation for debugging and test.
+   * This is a self-contained quantiser implementation without any implicit
+   * referral to the Lumiera session. It is mainly intended for simplified unit testing.
+   * @warning real GUI and Proc-Layer code should always prefer to build a real quantiser,
+   * which referres some TimeGrid definition within the session. Basically, the overall
+   * purpose of the time-quantisation framework is to enforce such a link to a specific
+   * time and quantisation scale and to prevent "wild and uncoordinated" rounding attempts.
+   */
+  class FixedFrameQuantiser
+    : public Quantiser
+    {
+      
+    public:
+      FixedFrameQuantiser (TimeFract frames_per_second);
+    };
+  
+  
+  
 }} // lib::time
 #endif
diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp
new file mode 100644
index 000000000..b739f411f
--- /dev/null
+++ b/src/lib/time/timecode.cpp
@@ -0,0 +1,44 @@
+/*
+  Timecode  -  implementation of fixed grid aligned time specifications
+
+  Copyright (C)         Lumiera.org
+    2010,               Hermann Vosseler 
+
+  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 "lib/lumitime.hpp"
+#include "lib/time/timecode.hpp"
+#include "lib/time/timequant.hpp"
+#include "lib/time/formats.hpp"
+
+using std::string;
+
+
+namespace lib {
+namespace time {
+  
+  
+  Format::~Format() { }  // emit VTable here....
+  
+  
+  /** */
+  
+  
+  
+}} // lib::time
+  
diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp
index d4aebf8c1..54866dd7f 100644
--- a/src/lib/time/timecode.hpp
+++ b/src/lib/time/timecode.hpp
@@ -25,6 +25,7 @@
 #define LIB_TIME_TIMECODE_H
 
 #include "lib/time/timevalue.hpp"
+#include "lib/time/formats.hpp"
 
 //#include 
 #include 
@@ -36,9 +37,11 @@ namespace time {
   
   /**
    * fixed format time specification.
-   * 
+   * @param FMT the actual timecode format to use
+   * @see time::Format
    * @todo WIP-WIP-WIP
    */
+  template
   class TCode
     {
       
@@ -46,6 +49,26 @@ namespace time {
     };
   
   
+  class QuTime;
+  
+  /**
+   * A frame counting timecode value.
+   * This is an hard-coded representation of
+   * TCode, with additional convenience
+   * constructors and conversions, which basically make
+   * FrameNr values interchangeable with integral numbers. 
+   */
+  class FrameNr
+    : public TCode
+    {
+      
+    public:
+      FrameNr (QuTime const& quantisedTime);
+      
+      operator long()  const;
+    };
+  
+  
   
 }} // lib::time
 #endif
diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp
new file mode 100644
index 000000000..dff03e8ac
--- /dev/null
+++ b/src/lib/time/timequant.hpp
@@ -0,0 +1,58 @@
+/*
+  TIMEQUANT.hpp  -  quantised (grid aligned) time values
+
+  Copyright (C)         Lumiera.org
+    2010,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+#ifndef LIB_TIME_TIMEQUQNT_H
+#define LIB_TIME_TIMEQUQNT_H
+
+#include "lib/time/timevalue.hpp"
+#include "lib/time/quantiser.hpp"
+#include "lib/time/timecode.hpp"
+#include "lib/symbol.hpp"
+
+//#include 
+#include 
+
+
+namespace lib {
+namespace time {
+  
+  using lib::Symbol;
+  
+  
+  /**
+   * fixed format time specification.
+   * 
+   * @todo WIP-WIP-WIP
+   */
+  class QuTime
+    {
+      
+    public:
+      QuTime (TimeValue raw, Symbol gridID);
+      QuTime (TimeValue raw, Quantiser const& quantisation_to_use);
+    };
+  
+  
+  
+}} // lib::time
+#endif
diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp
index f7e1e6d5e..15745ec0c 100644
--- a/src/proc/asset/meta/time-grid.cpp
+++ b/src/proc/asset/meta/time-grid.cpp
@@ -113,4 +113,21 @@ namespace meta {
     return AssetManager::instance().wrap (newGrid);
   }
   
+  
+  /* === TimeGrid shortcut builder functions === */
+  
+  PGrid
+  TimeGrid::build (Symbol gridID, TimeFract frames_per_second)
+  {
+    return build (gridID,frames_per_second, Time(0));
+  }
+  
+  
+  PGrid
+  TimeGrid::build (Symbol gridID, TimeFract frames_per_second, Time origin)
+  {
+    UNIMPLEMENTED ("build a trivial time grid");
+  }
+  
+  
 }} // namespace asset::meta
diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp
index 9c0b9ee3b..cc28e8806 100644
--- a/src/proc/asset/meta/time-grid.hpp
+++ b/src/proc/asset/meta/time-grid.hpp
@@ -49,12 +49,22 @@
 
 #include "proc/asset/meta.hpp"
 #include "lib/time/timevalue.hpp"
+#include "lib/symbol.hpp"
 
 
 
 namespace asset {
 namespace meta {
   
+  using lib::Symbol;
+  using lib::time::Time;
+  using lib::time::TimeValue;
+  using lib::time::TimeFract;
+  
+  
+  class TimeGrid;
+  typedef lumiera::P PGrid;
+  
   
   /**
    * Interface: a grid and scale for time quantisation.
@@ -69,19 +79,16 @@ namespace meta {
       
       // TODO define the TimeGrid API here
       
+      /* === shortcut builder functions === */
+      static PGrid build (Symbol gridID, TimeFract frames_per_second);
+      static PGrid build (Symbol gridID, TimeFract frames_per_second, Time origin);
       
     protected:
       TimeGrid (EntryID const&);
     };
-  
-  typedef lumiera::P PGrid;
-  
     
   
   
-  using lib::time::Time;
-  using lib::time::TimeValue;
-  using lib::time::TimeFract;
   
   
   template<>
diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp
index a770d2f78..1513ab8fa 100644
--- a/tests/lib/time/time-quantisation-test.cpp
+++ b/tests/lib/time/time-quantisation-test.cpp
@@ -23,7 +23,8 @@
 
 #include "lib/test/run.hpp"
 #include "lib/test/test-helper.hpp"
-#include "lib/time/quantiser.hpp"
+#include "lib/time/timequant.hpp"
+#include "proc/asset/meta/time-grid.hpp"
 #include "lib/util.hpp"
 
 #include 
@@ -33,6 +34,7 @@
 
 using boost::lexical_cast;
 using util::isnil;
+using util::contains;
 //using std::rand;
 using std::cout;
 using std::endl;
@@ -44,6 +46,8 @@ namespace lib {
 namespace time{
 namespace test{
   
+  using asset::meta::TimeGrid;
+  
   
   /********************************************************
    * @test verify handling of quantised time values.
@@ -61,14 +65,14 @@ namespace test{
           TimeValue ref (refval);
           CHECK (Time(0) < ref);
           
-          checkUsage (ref);
+          checkSimpleUsage (ref);
           check_theFullStory (ref);
           checkMultipleGrids (ref);
         } 
       
       
       void
-      checkUsage (TimeValue org)
+      checkSimpleUsage (TimeValue org)
         {
           TimeGrid::build("my_simple_grid", 25);    // "someone" has defined a time grid
           
@@ -88,8 +92,8 @@ namespace test{
           FixedFrameQuantiser fixQ(25);
           QuTime qVal (org, fixQ);
           
-          CHECK (contains (qVal.supportedFormats, format::FRAMES));
-          CHECK (contains (qVal.supportedFormats, format::SMPTE));
+          CHECK (contains (qVal.supportedFormats(), format::FRAMES));
+          CHECK (contains (qVal.supportedFormats(), format::SMPTE));
           
           TCode smpteTCode = qVal.formatAs();
           showTimeCode (smpteTCode);
diff --git a/uml/lumiera/128517 b/uml/lumiera/128517
index eef0810c5..dcd28563e 100644
--- a/uml/lumiera/128517
+++ b/uml/lumiera/128517
@@ -1,6 +1,6 @@
 format 58
 "CommonLib" // CommonLib
-  revision 22
+  revision 23
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -870,21 +870,21 @@ ${inlines}
       idl_decl ""
       explicit_switch_type ""
       
-      classrelation 206341 // 
-	relation 195461 ---|>
+      classrelation 210693 // 
+	relation 199813 ---|>
 	  a public
 	    cpp default "${type}"
-	    classrelation_ref 206341 // 
-	  b parent class_ref 168837 // Duration
+	    classrelation_ref 210693 // 
+	  b parent class_ref 134917 // Time
       end
 
-      classrelation 206469 // start ()
-	relation 195589 --->
-	  a role_name "start" protected
+      classrelation 210821 // dur_ ()
+	relation 199941 --->
+	  a role_name "dur_" multiplicity "1" protected
 	    cpp default "    ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value};
 "
-	    classrelation_ref 206469 // start ()
-	  b parent class_ref 134917 // Time
+	    classrelation_ref 210821 // dur_ ()
+	  b parent class_ref 168837 // Duration
       end
     end
 
@@ -916,12 +916,12 @@ ${inlines}
       idl_decl ""
       explicit_switch_type ""
       
-      classrelation 206213 // 
-	relation 195333 ---|>
+      classrelation 210437 // 
+	relation 199557 ---|>
 	  a public
 	    cpp default "${type}"
-	    classrelation_ref 206213 // 
-	  b parent class_ref 168709 // TimeValue
+	    classrelation_ref 210437 // 
+	  b parent class_ref 172165 // Offset
       end
     end
 
@@ -1034,6 +1034,14 @@ ${inlines}
 	python_decl ""
 	idl_decl ""
       end
+
+      classrelation 210949 // 
+	relation 200069 ---|>
+	  a public
+	    cpp default "${type}"
+	    classrelation_ref 210949 // 
+	  b parent class_ref 137093 // Meta
+      end
     end
 
     class 170373 "TimeVar"
@@ -1183,6 +1191,119 @@ ${inlines}
 	  b parent class_ref 169221 // TimeGrid
       end
     end
+
+    class 172165 "Offset"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      php_decl ""
+      python_2_2 python_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+      classrelation 210565 // 
+	relation 199685 ---|>
+	  a public
+	    cpp default "${type}"
+	    classrelation_ref 210565 // 
+	  b parent class_ref 168709 // TimeValue
+      end
+    end
+
+    class 172293 "Frames"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      php_decl ""
+      python_2_2 python_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+      classrelation 211077 // 
+	relation 200197 ---|>
+	  a public
+	    cpp default "${type}"
+	    classrelation_ref 211077 // 
+	  b parent class_ref 170757 // Format
+      end
+    end
+
+    class 172421 "Smpte"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      php_decl ""
+      python_2_2 python_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+      classrelation 211205 // 
+	relation 200325 ---|>
+	  a public
+	    cpp default "${type}"
+	    classrelation_ref 211205 // 
+	  b parent class_ref 170757 // Format
+      end
+    end
+
+    class 172549 "Hms"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      php_decl ""
+      python_2_2 python_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+      classrelation 211333 // 
+	relation 200453 ---|>
+	  a public
+	    cpp default "${type}"
+	    classrelation_ref 211333 // 
+	  b parent class_ref 170757 // Format
+      end
+    end
+
+    class 172677 "SmpteTC"
+      visibility package 
+      nactuals 1
+      actual class class_ref 170629 // TCode
+        rank 0 explicit_value ""
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      php_decl ""
+      python_2_2 python_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
+      classrelation 211461 // 
+	relation 200581 ---|>
+	  a public
+	    cpp default "${type}"
+	    classrelation_ref 211461 // 
+	  b parent class_ref 170629 // TCode
+      end
+    end
   end
 
   package_ref 131077 // ConfigQuery
diff --git a/uml/lumiera/142725.diagram b/uml/lumiera/142725.diagram
index c38a9266c..70a003dcd 100644
--- a/uml/lumiera/142725.diagram
+++ b/uml/lumiera/142725.diagram
@@ -6,7 +6,7 @@ classcanvas 128005 class_ref 134917 // Time
 end
 classcanvas 128133 class_ref 168581 // TimeSpan
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 38 218 2000
+  xyz 110 267 2000
 end
 classcanvas 128261 class_ref 168709 // TimeValue
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
@@ -14,19 +14,19 @@ classcanvas 128261 class_ref 168709 // TimeValue
 end
 classcanvas 128389 class_ref 168837 // Duration
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 42 118 2000
+  xyz 42 182 2000
 end
 classcanvas 129669 class_ref 168965 // QuTime
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 245 324 2000
+  xyz 245 361 2000
 end
 classcanvas 130181 class_ref 169093 // Quantiser
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 393 324 2000
+  xyz 393 361 2000
 end
 classcanvas 130565 class_ref 169221 // TimeGrid
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 508 324 2000
+  xyz 508 361 2000
 end
 classcanvas 130949 class_ref 170373 // TimeVar
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
@@ -34,23 +34,49 @@ classcanvas 130949 class_ref 170373 // TimeVar
 end
 classcanvas 131589 class_ref 170501 // QuTimeSpan
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 103 324 2000
+  xyz 103 361 2000
 end
 classcanvas 131973 class_ref 170629 // TCode
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 248 413 2000
+  xyz 248 450 2000
 end
 classcanvas 132101 class_ref 170757 // Format
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 393 397 2000
+  xyz 393 434 2000
 end
 classcanvas 132997 class_ref 170885 // FrameNr
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 238 496 2000
+  xyz 243 545 2000
 end
 classcanvas 133253 class_ref 171013 // CompoundGrid
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 480 439 2000
+  xyz 480 476 2000
+end
+classcanvas 133509 class_ref 172165 // Offset
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
+  xyz 46 118 2000
+end
+classcanvas 135045 class_ref 137093 // Meta
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
+  xyz 514 237 2000
+end
+note 135429 "Asset System"
+  xyzwh 504 197 2000 66 47
+classcanvas 135557 class_ref 172293 // Frames
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
+  xyz 392 507 1995
+end
+classcanvas 135685 class_ref 172421 // Smpte
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
+  xyz 446 533 2010
+end
+classcanvas 136069 class_ref 172549 // Hms
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
+  xyz 421 523 2005
+end
+classcanvas 136325 class_ref 172677 // SmpteTC
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
+  xyz 308 546 2000
 end
 relationcanvas 128517 relation_ref 195205 // 
   geometry VHV unfixed
@@ -60,31 +86,16 @@ relationcanvas 128517 relation_ref 195205 // 
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
-relationcanvas 128901 relation_ref 195333 // 
-  from ref 128389 z 1999 to ref 128261
-  no_role_a no_role_b
-  no_multiplicity_a no_multiplicity_b
-end
-relationcanvas 129285 relation_ref 195461 // 
-  from ref 128133 z 1999 to ref 128389
-  no_role_a no_role_b
-  no_multiplicity_a no_multiplicity_b
-end
-relationcanvas 129413 relation_ref 195589 // 
-  from ref 128133 z 1999 to ref 128005
-  role_a_pos 111 214 3000 no_role_b
-  no_multiplicity_a no_multiplicity_b
-end
 relationcanvas 130437 relation_ref 195973 // 
-  from ref 129669 z 1999 stereotype "<>" xyz 309 330 3000 to ref 130181
+  from ref 129669 z 1999 stereotype "<>" xyz 309 367 3000 to ref 130181
   no_role_a no_role_b
-  multiplicity_a_pos 376 349 3000 multiplicity_b_pos 305 349 3000
+  multiplicity_a_pos 376 386 3000 multiplicity_b_pos 305 386 3000
 end
 relationcanvas 130693 relation_ref 196101 // 
   decenter_end 353
-  from ref 130181 z 1999 stereotype "<>" xyz 457 333 3000 to ref 130565
+  from ref 130181 z 1999 stereotype "<>" xyz 457 370 3000 to ref 130565
   no_role_a no_role_b
-  multiplicity_a_pos 491 352 3000 no_multiplicity_b
+  multiplicity_a_pos 491 389 3000 no_multiplicity_b
 end
 relationcanvas 130821 relation_ref 197253 // 
   from ref 129669 z 1999 to ref 128005
@@ -101,34 +112,32 @@ relationcanvas 131077 relation_ref 197381 // 
 end
 relationcanvas 131461 relation_ref 197509 // 
   from ref 130949 z 1999 to ref 128005
-  role_a_pos 175 124 3000 no_role_b
-  multiplicity_a_pos 230 140 3000 multiplicity_b_pos 176 143 3000
+  role_a_pos 225 120 3000 no_role_b
+  multiplicity_a_pos 232 143 3000 multiplicity_b_pos 176 143 3000
 end
 relationcanvas 131717 relation_ref 197637 // 
-  from ref 131589 z 1999 to point 65 290
-  line 131845 z 1999 to ref 128133
+  from ref 131589 z 1999 to ref 128133
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
 relationcanvas 132485 relation_ref 197893 // 
-  from ref 131973 z 1999 to point 290 413
+  from ref 131973 z 1999 to point 290 450
   line 132869 z 1999 to ref 132101
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
 relationcanvas 132613 relation_ref 198021 // 
-  from ref 129669 z 1999 stereotype "<>" xyz 275 378 3000 to ref 131973
+  from ref 129669 z 1999 stereotype "<>" xyz 275 415 3000 to ref 131973
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
 relationcanvas 132741 relation_ref 198149 // 
   decenter_end 595
-  from ref 130181 z 1999 stereotype "<>" xyz 360 377 3000 to ref 132101
+  from ref 130181 z 1999 stereotype "<>" xyz 360 414 3000 to ref 132101
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
 relationcanvas 133125 relation_ref 198277 // 
-  decenter_begin 581
   from ref 132997 z 1999 to ref 131973
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
@@ -139,4 +148,56 @@ relationcanvas 133381 relation_ref 198405 // 
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
+relationcanvas 133637 relation_ref 199557 // 
+  from ref 128389 z 1999 to ref 133509
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 133765 relation_ref 199685 // 
+  from ref 133509 z 1999 to ref 128261
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 134277 relation_ref 199813 // 
+  from ref 128133 z 1999 to point 137 248
+  line 134533 z 1999 to point 268 194
+  line 134405 z 1999 to ref 128005
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 134661 relation_ref 199941 // 
+  geometry VHr
+  from ref 128133 z 1999 to point 66 284
+  line 134789 z 1999 to ref 128389
+  role_a_pos 75 269 3000 no_role_b
+  multiplicity_a_pos 52 226 3000 no_multiplicity_b
+end
+relationcanvas 135301 relation_ref 200069 // 
+  from ref 130565 z 1999 to ref 135045
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 135813 relation_ref 200197 // 
+  from ref 135557 z 1994 to ref 132101
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 135941 relation_ref 200325 // 
+  from ref 135685 z 1999 to ref 132101
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 136197 relation_ref 200453 // 
+  from ref 136069 z 1999 to ref 132101
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 136453 relation_ref 200581 // 
+  geometry VHV
+  from ref 136325 z 1999 to point 334 522
+  line 136581 z 1999 to point 268 522
+  line 136709 z 1999 to ref 131973
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
 end
diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj
index d6e97d779..1380666d0 100644
--- a/uml/lumiera/lumiera.prj
+++ b/uml/lumiera/lumiera.prj
@@ -1,6 +1,6 @@
 format 58
 "lumiera"
-  revision 66
+  revision 67
   modified_by 5 "hiv"
   cpp_root_dir "../../src/"
 

From c40ba74bc652791f40239270306aceef490b900c Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 2 Jan 2011 18:56:44 +0100
Subject: [PATCH 022/140] WIP clarify ambiguity with fractional seconds They
 are *not* intended to stand-in for gavl_time_t Indeed, Time values should be
 handled as opaque

---
 src/lib/time/formats.hpp                                  | 2 +-
 src/lib/time/lumitime.cpp                                 | 4 ++--
 src/lib/time/quantiser.hpp                                | 2 +-
 src/lib/time/timevalue.hpp                                | 7 ++++---
 src/proc/asset/meta/time-grid.cpp                         | 4 ++--
 src/proc/asset/meta/time-grid.hpp                         | 8 ++++----
 .../components/proc/asset/meta/time-grid-basics-test.cpp  | 2 +-
 tests/lib/time/time-quantisation-test.cpp                 | 6 +++---
 8 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp
index 5136dcf64..daea02508 100644
--- a/src/lib/time/formats.hpp
+++ b/src/lib/time/formats.hpp
@@ -107,7 +107,7 @@ namespace time {
     class Seconds
       : public Format
       {
-        TimeFract secs_;
+        
       };
     
     
diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp
index 0aeacf9fb..c1837667b 100644
--- a/src/lib/time/lumitime.cpp
+++ b/src/lib/time/lumitime.cpp
@@ -43,7 +43,7 @@ namespace time {
      *  into the internal time scale used by GAVL and Lumiera internal time values.
      */
     inline gavl_time_t
-    rational2time (TimeFract const& fractionalSeconds)
+    rational2time (FSecs const& fractionalSeconds)
     {
       return boost::rational_cast (GAVL_TIME_SCALE * fractionalSeconds);
     }
@@ -77,7 +77,7 @@ namespace time {
    *  from a fraction of seconds, given as rational number.
    *  An example would be to the time unit of a framerate.
    */
-  Time::Time (TimeFract const& fractionalSeconds)
+  Time::Time (FSecs const& fractionalSeconds)
     : TimeValue(rational2time (fractionalSeconds))
     { }
   
diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp
index 29ab6d9b8..1d7ea45e9 100644
--- a/src/lib/time/quantiser.hpp
+++ b/src/lib/time/quantiser.hpp
@@ -62,7 +62,7 @@ namespace time {
     {
       
     public:
-      FixedFrameQuantiser (TimeFract frames_per_second);
+      FixedFrameQuantiser (FSecs frames_per_second);
     };
   
   
diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp
index 9f2888588..674952ace 100644
--- a/src/lib/time/timevalue.hpp
+++ b/src/lib/time/timevalue.hpp
@@ -183,8 +183,9 @@ namespace time {
   
   /* ======= specific Time entities ==================== */
   
-  /** rational representation of fractional seconds */
-  typedef boost::rational TimeFract;
+  /** rational representation of fractional seconds
+   * @warning do not mix up gavl_time_t and FSecs */
+  typedef boost::rational FSecs;
 
   /**
    * Lumiera's internal time value datatype.
@@ -223,7 +224,7 @@ namespace time {
         : TimeValue(calcResult)
         { }
       
-      Time (TimeFract const& fractionalSeconds);
+      Time (FSecs const& fractionalSeconds);
       
       Time ( long millis
            , uint secs 
diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp
index 15745ec0c..c1ab270e8 100644
--- a/src/proc/asset/meta/time-grid.cpp
+++ b/src/proc/asset/meta/time-grid.cpp
@@ -117,14 +117,14 @@ namespace meta {
   /* === TimeGrid shortcut builder functions === */
   
   PGrid
-  TimeGrid::build (Symbol gridID, TimeFract frames_per_second)
+  TimeGrid::build (Symbol gridID, FSecs frames_per_second)
   {
     return build (gridID,frames_per_second, Time(0));
   }
   
   
   PGrid
-  TimeGrid::build (Symbol gridID, TimeFract frames_per_second, Time origin)
+  TimeGrid::build (Symbol gridID, FSecs frames_per_second, Time origin)
   {
     UNIMPLEMENTED ("build a trivial time grid");
   }
diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp
index cc28e8806..1e449a372 100644
--- a/src/proc/asset/meta/time-grid.hpp
+++ b/src/proc/asset/meta/time-grid.hpp
@@ -59,7 +59,7 @@ namespace meta {
   using lib::Symbol;
   using lib::time::Time;
   using lib::time::TimeValue;
-  using lib::time::TimeFract;
+  using lib::time::FSecs;
   
   
   class TimeGrid;
@@ -80,8 +80,8 @@ namespace meta {
       // TODO define the TimeGrid API here
       
       /* === shortcut builder functions === */
-      static PGrid build (Symbol gridID, TimeFract frames_per_second);
-      static PGrid build (Symbol gridID, TimeFract frames_per_second, Time origin);
+      static PGrid build (Symbol gridID, FSecs frames_per_second);
+      static PGrid build (Symbol gridID, FSecs frames_per_second, Time origin);
       
     protected:
       TimeGrid (EntryID const&);
@@ -94,7 +94,7 @@ namespace meta {
   template<>
   struct Builder
     {
-      TimeFract fps_;
+      FSecs fps_;
       Time      origin_;
       
       /** when building a compound or variable grid,
diff --git a/tests/components/proc/asset/meta/time-grid-basics-test.cpp b/tests/components/proc/asset/meta/time-grid-basics-test.cpp
index 36f5e8c62..c85fe2b95 100644
--- a/tests/components/proc/asset/meta/time-grid-basics-test.cpp
+++ b/tests/components/proc/asset/meta/time-grid-basics-test.cpp
@@ -66,7 +66,7 @@ namespace test {
   namespace { // Test definitions...
     
     Time testOrigin (12,23);
-    TimeFract testFps (5,4);
+    FSecs testFps (5,4);
     
   }
   
diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp
index 1513ab8fa..22325c37e 100644
--- a/tests/lib/time/time-quantisation-test.cpp
+++ b/tests/lib/time/time-quantisation-test.cpp
@@ -81,8 +81,8 @@ namespace test{
           FrameNr count(qVal);                      // materialise this quantised time into..
           int n = count;                            // frame count, accessible as plain number
           
-          CHECK (TimeFract(n-1, 25) < org);         // verify quantisation: the original time
-          CHECK (org < TimeFract(n+1, 25));         // is properly bracketed by (n-1, n+2)
+          CHECK (Time(FSecs(n-1, 25)) < org);       // verify quantisation: the original time
+          CHECK (org < Time(FSecs(n+1, 25)));       // is properly bracketed by (n-1, n+2)
         }
       
       
@@ -116,7 +116,7 @@ namespace test{
       void
       checkMultipleGrids (TimeValue org)
         {
-          TimeGrid::build("my_alternate_grid", TimeFract(30000,1001));
+          TimeGrid::build("my_alternate_grid", FSecs(30000,1001));
           
           QuTime palVal (org, "my_simple_grid");
           QuTime ntscVal (org, "my_alternate_grid");

From 62f9e84f2d4127cea0e11e0d783076d58cdb8a23 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 3 Jan 2011 22:08:28 +0100
Subject: [PATCH 023/140] reduce the prerequisites of trait.hpp the
 AssignableRefWrapper is not in use anymore... by not pulling in wrapper.hpp,
 we avoid 

---
 src/lib/meta/trait.hpp   | 5 -----
 src/lib/util-foreach.hpp | 1 +
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/lib/meta/trait.hpp b/src/lib/meta/trait.hpp
index 84250866f..ee77db289 100644
--- a/src/lib/meta/trait.hpp
+++ b/src/lib/meta/trait.hpp
@@ -27,7 +27,6 @@
 
 #include "lib/meta/util.hpp"
 #include "lib/meta/duck-detector.hpp"
-#include "lib/wrapper.hpp"     ////////////////////////TODO only because of AssignableRefWrapper -- can we get rid of this import?
 
 #include 
 #include 
@@ -196,7 +195,6 @@ namespace meta {
       typedef TY* pointer;
       typedef TY& reference;
       typedef TY  value_type;
-      typedef value_type member_type;
     };
   
   template
@@ -205,7 +203,6 @@ namespace meta {
       typedef TY* pointer;
       typedef TY& reference;
       typedef TY  value_type;
-      typedef pointer member_type;
     };
   
   template
@@ -214,9 +211,7 @@ namespace meta {
       typedef TY* pointer;
       typedef TY& reference;
       typedef TY  value_type;
-      typedef lib::wrapper::AssignableRefWrapper member_type;
     };
-  //////////////////////////////////////////TODO: member_type not needed anymore 12/09 -- obsolete? useful? keep it?
   
   
   
diff --git a/src/lib/util-foreach.hpp b/src/lib/util-foreach.hpp
index 9270c3a06..8c0791c65 100644
--- a/src/lib/util-foreach.hpp
+++ b/src/lib/util-foreach.hpp
@@ -55,6 +55,7 @@
 #include "lib/meta/trait.hpp"
 
 #include 
+#include 
 #include 
 
 

From 607c8a23389c4fe38b554bfcd2477b0c440025e1 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 4 Jan 2011 01:45:11 +0100
Subject: [PATCH 024/140] WIP enough stubbing to get it through the Compiler...
 ...but NOT yet the linker

---
 src/lib/time/quantiser.cpp                |  6 +++++-
 src/lib/time/quantiser.hpp                | 13 ++++++++++++
 src/lib/time/timecode.hpp                 | 26 +++++++++++++++++++++++
 src/lib/time/timequant.hpp                |  7 ++++++
 tests/lib/time/time-quantisation-test.cpp | 11 +++++-----
 5 files changed, 57 insertions(+), 6 deletions(-)

diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp
index 48b24979c..c4e12b274 100644
--- a/src/lib/time/quantiser.cpp
+++ b/src/lib/time/quantiser.cpp
@@ -21,7 +21,7 @@
 * *****************************************************/
 
 
-#include "lib/lumitime.hpp"
+#include "lib/time/quantiser.hpp"
 #include "lib/time/timevalue.hpp"
 
 using std::string;
@@ -35,5 +35,9 @@ namespace time {
   
   
   
+  LUMIERA_ERROR_DEFINE (UNKNOWN_GRID, "referring to an undefined grid or scale in value quantisation");
+  
+  
+  
 }} // lib::time
   
diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp
index 1d7ea45e9..61b9e99d0 100644
--- a/src/lib/time/quantiser.hpp
+++ b/src/lib/time/quantiser.hpp
@@ -24,16 +24,20 @@
 #ifndef LIB_TIME_QUANTISER_H
 #define LIB_TIME_QUANTISER_H
 
+#include "lib/error.hpp"
 #include "lib/time/timevalue.hpp"
 #include "lib/time/formats.hpp"
+#include "lib/iter-adapter.hpp"
 
 //#include 
+#include 
 #include 
 
 
 namespace lib {
 namespace time {
   
+  LUMIERA_ERROR_DECLARE (UNKNOWN_GRID); ///< referring to an undefined grid or scale in value quantisation
   
   /**
    * Facility to create grid-aligned time values.
@@ -42,8 +46,17 @@ namespace time {
    */
   class Quantiser
     {
+      typedef std::vector         _FormatTable;
+      typedef _FormatTable::const_iterator _SrcIter;
+      typedef lib::PtrDerefIter<_SrcIter>  _Iter;
       
     public:
+      template
+      bool supports()  const;
+      
+      typedef _Iter iterator;
+      iterator getSupportedFormats()  const;
+      
     };
   
   
diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp
index 54866dd7f..18d57e52d 100644
--- a/src/lib/time/timecode.hpp
+++ b/src/lib/time/timecode.hpp
@@ -34,6 +34,8 @@
 namespace lib {
 namespace time {
   
+  using std::string;
+  
   
   /**
    * fixed format time specification.
@@ -46,6 +48,10 @@ namespace time {
     {
       
     public:
+      
+      string describe()  const;
+      
+      
     };
   
   
@@ -64,11 +70,31 @@ namespace time {
       
     public:
       FrameNr (QuTime const& quantisedTime);
+      FrameNr (TCode const&);
       
       operator long()  const;
     };
   
   
   
+  /**
+   * 
+   */
+  class SmpteTC
+    : public TCode
+    {
+      
+    public:
+      SmpteTC (QuTime const& quantisedTime);
+      SmpteTC (TCode const&);
+      
+      int getSecs   () const; 
+      int getMins   () const; 
+      int getHours  () const; 
+      int getFrames () const;
+    };
+  
+  
+  
 }} // lib::time
 #endif
diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp
index dff03e8ac..499c5ae43 100644
--- a/src/lib/time/timequant.hpp
+++ b/src/lib/time/timequant.hpp
@@ -45,11 +45,18 @@ namespace time {
    * @todo WIP-WIP-WIP
    */
   class QuTime
+    : public Time
     {
       
     public:
       QuTime (TimeValue raw, Symbol gridID);
       QuTime (TimeValue raw, Quantiser const& quantisation_to_use);
+      
+      template
+      bool supports()  const;
+      
+      template
+      TCode formatAs()  const;
     };
   
   
diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp
index 22325c37e..eb5acf171 100644
--- a/tests/lib/time/time-quantisation-test.cpp
+++ b/tests/lib/time/time-quantisation-test.cpp
@@ -92,8 +92,8 @@ namespace test{
           FixedFrameQuantiser fixQ(25);
           QuTime qVal (org, fixQ);
           
-          CHECK (contains (qVal.supportedFormats(), format::FRAMES));
-          CHECK (contains (qVal.supportedFormats(), format::SMPTE));
+          CHECK ( qVal.supports());
+          CHECK ( qVal.supports());
           
           TCode smpteTCode = qVal.formatAs();
           showTimeCode (smpteTCode);
@@ -102,14 +102,14 @@ namespace test{
           showTimeCode (frameTCode);
           
           FrameNr count(frameTCode);
-          CHECK (string(count) == *(frameTCode.components()));
+//        CHECK (string(count) == frameTCode.part[0]));
         }
       
       template
       void
       showTimeCode (TC timecodeValue)
         {
-          cout << timecodeValue.describe() << " = " << join (timecodeValue.components(), ":") << endl;
+//        cout << timecodeValue.describe() << " = " << join (timecodeValue.part(), ":") << endl;
         }
       
       
@@ -140,7 +140,8 @@ namespace test{
           
           TimeGrid::build("special_funny_grid", 1);      // provide the grid's definition (1 frame per second)
           
-          int cnt = funny.formatAs();    // and now performing quantisation is OK 
+          int cnt = 0;//////funny.formatAs().part["count"];
+                                                         // and now performing quantisation is OK 
           SmpteTC smpte (funny);                         // also converting into SMPTE (which implies frame quantisation)
           CHECK (0 == smpte.getFrames());                // we have 1fps, thus the frame part is always zero!
           CHECK (cnt % 60 == smpte.getSecs());           // and the seconds part will be in sync with the frame count

From 6638120ec2acb5103ad6bd05942703777a468957 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 4 Jan 2011 03:50:33 +0100
Subject: [PATCH 025/140] WIP test-driven dreaming... inventing a "Digxel"
 entity

---
 src/lib/time/digxel.cpp        |  41 +++++++
 src/lib/time/digxel.hpp        | 106 +++++++++++++++++
 tests/lib/time/digxel-test.cpp | 204 +++++++++++++++++++++++++++++++++
 3 files changed, 351 insertions(+)
 create mode 100644 src/lib/time/digxel.cpp
 create mode 100644 src/lib/time/digxel.hpp
 create mode 100644 tests/lib/time/digxel-test.cpp

diff --git a/src/lib/time/digxel.cpp b/src/lib/time/digxel.cpp
new file mode 100644
index 000000000..3eea7f927
--- /dev/null
+++ b/src/lib/time/digxel.cpp
@@ -0,0 +1,41 @@
+/*
+  Timecode  -  implementation of fixed grid aligned time specifications
+
+  Copyright (C)         Lumiera.org
+    2010,               Hermann Vosseler 
+
+  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 "lib/lumitime.hpp"
+#include "lib/time/timecode.hpp"
+#include "lib/time/timequant.hpp"
+#include "lib/time/formats.hpp"
+
+using std::string;
+
+
+namespace lib {
+namespace time {
+  
+  
+  /** */
+  
+  
+  
+}} // lib::time
+  
diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp
new file mode 100644
index 000000000..af451cbb2
--- /dev/null
+++ b/src/lib/time/digxel.hpp
@@ -0,0 +1,106 @@
+/*
+  TIMECODE.hpp  -  grid aligned and fixed format time specifications
+
+  Copyright (C)         Lumiera.org
+    2010,               Hermann Vosseler 
+
+  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 digxel.hpp
+ ** A self-contained numeric element for building structured numeric displays.
+ ** Within the GUI of an editing application, we got to build several display widgets
+ ** to show numeric values in a structured fashion, like Colours or Timecode. While the
+ ** actual formats of such display elements vary largely, the common property is that
+ ** they use an underlying \em format to build the numbers out of individual numeric elements.
+ ** For a timecode display these are for example the sexagesimal (base 60) "digits" of the
+ ** common human readable time representation. A "Digxel" is an abstract element with specific
+ ** properties to support building such display widgets. It doesn't contain any GUI code, but
+ ** can be wrapped up to build a custom widget.
+ ** 
+ ** \par properties of a "Digxel"
+ ** 
+ ** Semantically, it's a number or number component. It holds an internal numeric representation
+ ** and is implicitly convertible both to integrals and floating point numbers. This implicit
+ ** conversion is a compromise to support generic processing.
+ ** 
+ ** But at the same time, a Digxel has a definite textual format and the ability to present
+ ** its numeric value formatted accordingly. To this end, the contract \em requires that
+ ** numeric data pushed to the Digxel be kept within such limits to prevent exceeding the
+ ** embedded formatting buffer. There is an assertion in debug mode, and a range check,
+ ** but the result will be just truncated, so this is clearly the caller's responsibility.
+ ** Digxel might be considered an implementation support class, and performance is important
+ ** to some limited degree; especially, formatted values will be cached.
+ ** 
+ ** To support in-place modification, the digxel stores a mutation signal (functor) and exposes
+ ** a special \c mutate(newVal) function, which invokes this stored functor, if defined. Usually
+ ** this should invoke some internal recalculations, resulting in a new value being pushed to
+ ** the Digxel for display.
+ ** 
+ ** \par configuration
+ ** the Digxel template can be configured to some degree to adjust the stored numeric data
+ ** and the actual format to be applied
+ ** 
+ ** @see timecode.hpp typical usage example
+ **
+ */
+
+#ifndef LIB_TIME_DIGXEL_H
+#define LIB_TIME_DIGXEL_H
+
+#include "lib/error.hpp"
+
+//#include 
+#include   ////////////////////TODO
+#include 
+
+
+namespace lib {
+namespace time {
+  
+  using std::string;
+  
+  
+  /**
+   * A number element for building structured numeric displays.
+   * The purpose is to represent parts of a numeric format, like
+   * e.g. the sexagesimal "digits" of a timecode display. Digxel
+   * - is customised by template parameters to a specific number format
+   * - requires that any number set must not overflow the format buffer
+   * - can receive new numbers by assignment
+   * - will then format these numbers and cache the formatted representation
+   * - can store and invoke a mutation functor
+   *  
+   * @param TODO
+   * @see lib::time::TCode
+   * @todo WIP-WIP-WIP
+   */
+  template
+  class Digxel
+    {
+      
+    public:
+      
+      string describe()  const;
+      
+      
+    };
+  
+  
+  
+}} // lib::time
+#endif
diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp
new file mode 100644
index 000000000..9707e490a
--- /dev/null
+++ b/tests/lib/time/digxel-test.cpp
@@ -0,0 +1,204 @@
+/*
+  Digxel(Test)  -  cover behaviour of a generic number-element holder
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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 "lib/test/run.hpp"
+#include "lib/test/test-helper.hpp"
+#include "lib/time/digxel.hpp"
+#include "lib/util.hpp"
+
+//#include 
+//#include 
+#include 
+#include   //TODO
+#include 
+
+//using boost::lexical_cast;
+//using util::isnil;
+//using util::contains;
+using util::isSameObject;
+using std::rand;
+using std::cout;/////////TODO
+using std::endl;
+
+//using boost::algorithm::join;
+
+
+namespace lib {
+namespace time{
+namespace test{
+  
+  namespace { // Test data
+    
+    // Placeholder to mark the function argument in a "lambda-expression"
+    boost::lambda::placeholder1_type _1_;
+    
+    const uint REPEAT = 40;
+    const uint RAND_RANGE = 100;
+    const uint RAND_DENOM = 3;
+    
+    inline double
+    randomFrac()
+      {
+        double arbitrary = (rand() % RAND_RANGE);
+        arbitrary /= (1 + rand() % RAND_DENOM);
+        return arbitrary;
+      }
+
+
+  }
+  
+  
+  /***********************************************************************
+   * @test verify correct behaviour of an display "Digxel":
+   *       A self-contained numeric element to support building displays.
+   *       - build a Digxel
+   *       - set a value
+   *       - retrieve formatted display
+   *       - assign to invoke the setter-functor
+   */
+  class Digxel_test : public Test
+    {
+      virtual void
+      run (Arg arg) 
+        {
+          checkSimpleUsage (ref);
+        } 
+      
+      
+      void
+      checkSimpleUsage ()
+        {
+          TestDigxel digi;
+          CHECK (0 == digi);
+          CHECK ("## 0 ##" == digi.show());
+          
+          digi = 88;
+          CHECK (88 == digi);
+          CHECK ("## 88.0 ##" == digi.show());
+        }
+      
+      
+      void
+      checkMutation ()
+        {
+          TestDigxel digi;
+          
+          uint sum(0), checksum(0);
+          
+          // configure what the Digxel does on "mutation"
+          digi.mutator = ( var(sum) += _1_ );
+          
+          CHECK (0 == digi);
+          for (uint i=0; i < REPEAT; ++i)
+            {
+              double arbitrary = randomFrac();
+              checksum += arbitrary;
+              
+              digi.mutate (arbitrary);  // invoke the mutation functor 
+              
+              CHECK (sum == checksum, "divergence after adding %f in iteration %d", arbitrary, i);
+              CHECK (0 == digi);     // the value itself is not touched
+            }
+        }
+      
+      
+      void
+      checkDefaultMutator ()
+        {
+          TestDigxel digi;
+          
+          CHECK (0 == digi);
+          digi.mutate(12.3);
+          CHECK (12.3 == digi);
+        }
+      
+      
+      void
+      checkCopy ()
+        {
+          TestDigxel d1;
+          
+          double someValue = randomFrac();
+          
+          d1 = someValue;
+          CHECK (d1 == someValue);
+          
+          TextDigxel d2(d1);
+          CHECK (d2 == someValue);
+          CHECK (!isSameObject (d1, d2));
+          
+          d1 = randomFrac();
+          CHECK (d1 != d2);
+          CHECK (d2 == someValue);
+        }
+      
+      
+      void
+      checkDisplayOverrun ()
+        {
+          TestDigxel digi;
+          digi = 123456789.12345678;
+          
+          string formatted (digi.show());  
+          CHECK (formatted.length() <= digi.maxlen());
+        }
+      
+      
+      void
+      verifyDisplayCaching ()
+        {
+          TestDigxel digi;
+          digi = 1;
+          
+          clock_t start, stop;
+          start = clock();
+          for (uint i=0; i < TIMING_CNT; ++i)
+            {
+              val odd = i % 2;
+              digi = 1;
+            }
+          stop = clock();
+          uint without_reformatting = stop - start;
+          
+          
+          start = clock();
+          for (uint i=0; i < TIMING_CNT; ++i)
+            {
+              val odd = i % 2;
+              digi = odd;
+            }
+          stop = clock();
+          uint with_reformatting = stop - start;
+          
+          cout << "without = "<< without_reformatting << endl;
+          cout << "with    = "<< with_reformatting << endl;
+        }
+    };
+  
+  
+  /** Register this test class... */
+  LAUNCHER (Digxel_test, "unit common");
+  
+  
+  
+}}} // namespace lib::time::test

From b19fd1e634e519ce9a01d8a102e18f6b45f947a7 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 4 Jan 2011 20:56:47 +0100
Subject: [PATCH 026/140] WIP rework and adjust "Digxel" draft

---
 src/lib/time/digxel.cpp        |  4 ++
 src/lib/time/digxel.hpp        | 74 ++++++++++++++++++++++++++++++----
 tests/lib/time/digxel-test.cpp | 49 ++++++++++++++++++----
 3 files changed, 112 insertions(+), 15 deletions(-)

diff --git a/src/lib/time/digxel.cpp b/src/lib/time/digxel.cpp
index 3eea7f927..5c8046de7 100644
--- a/src/lib/time/digxel.cpp
+++ b/src/lib/time/digxel.cpp
@@ -33,6 +33,10 @@ namespace lib {
 namespace time {
   
   
+  Digxel::~Digxel () { }
+  // A hint for the compiler to emit VTables and magic stuff here
+
+  
   /** */
   
   
diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp
index af451cbb2..6cdc3888e 100644
--- a/src/lib/time/digxel.hpp
+++ b/src/lib/time/digxel.hpp
@@ -63,6 +63,7 @@
 #define LIB_TIME_DIGXEL_H
 
 #include "lib/error.hpp"
+//#include "lib/symbol.hpp"
 
 //#include 
 #include   ////////////////////TODO
@@ -72,8 +73,7 @@
 namespace lib {
 namespace time {
   
-  using std::string;
-  
+//  using std::string;
   
   /**
    * A number element for building structured numeric displays.
@@ -89,17 +89,77 @@ namespace time {
    * @see lib::time::TCode
    * @todo WIP-WIP-WIP
    */
-  template
   class Digxel
     {
       
     public:
-      
-      string describe()  const;
-      
-      
+      virtual ~Digxel ();  ///< this is an ABC
+        
     };
   
+  namespace digxel {
+    
+//  using lib::Literal;
+    
+    template
+    struct PrintfFormatter
+      {
+        enum{ len = 6
+            , bufsiz = len+1
+        };
+        
+        char printbuffer_[bufsiz];
+        
+        static void
+        show (NUM val)
+          {
+            size_t space = std::snprintf (printbuffer_, bufsiz, "%5d", val);
+            REQUIRE (space <= bufsiz, "Digxel value exceeded available buffer size. "
+                                      "For showing %d, %d chars instead of just %d would be required."
+                                    , val, space, bufsiz);
+          }
+      };
+    
+    template
+    struct Formatter;
+    
+    template<>
+    struct Formatter
+      : PrintfFormatter
+      {
+        enum{ len = 6
+            , bufsiz = len+1
+            };
+        
+      };
+  
+    /**
+     * The outward hull of a concrete Digxel implementation.
+     * Inheriting from the Digxel interface, it embodies a concrete
+     * Formatter specialised to yield the desired behaviour.
+     *  
+     * @param TODO
+     * @todo WIP-WIP-WIP
+     */
+    template< typename NUM
+            , class FMT  = digxel::Formatter
+            >
+    class Holder
+      : public Digxel
+      {
+        FMT buffer_;
+        NUM value_;
+        
+      public:
+        Holder ()
+          : buffer_()
+          , value_()
+          { }
+          
+      };
+    
+  }
+  
   
   
 }} // lib::time
diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp
index 9707e490a..e5fffccc2 100644
--- a/tests/lib/time/digxel-test.cpp
+++ b/tests/lib/time/digxel-test.cpp
@@ -55,6 +55,7 @@ namespace test{
     const uint REPEAT = 40;
     const uint RAND_RANGE = 100;
     const uint RAND_DENOM = 3;
+    const uint TIMING_CNT = 10000;
     
     inline double
     randomFrac()
@@ -63,7 +64,25 @@ namespace test{
         arbitrary /= (1 + rand() % RAND_DENOM);
         return arbitrary;
       }
-
+    
+    
+    /* === special Digxel configuration for this test === */
+    
+    double
+    limitingMutator (double value2set)
+    {
+      return (+1 < value2set) ? 1.0
+           : (-1 > value2set) ? -1.0
+                              : value2set;
+    }
+    
+    
+    struct VerySpecialFormat
+      {
+        
+      };
+    
+    typedef digxel::Holder TestDigxel;
 
   }
   
@@ -79,9 +98,14 @@ namespace test{
   class Digxel_test : public Test
     {
       virtual void
-      run (Arg arg) 
+      run (Arg) 
         {
-          checkSimpleUsage (ref);
+          checkSimpleUsage ();
+          checkMutation ();
+          verifyMutatorInfluence ();
+          checkCopy ();
+          checkDisplayOverrun ();
+          verifyDisplayCaching ();
         } 
       
       
@@ -114,21 +138,30 @@ namespace test{
               double arbitrary = randomFrac();
               checksum += arbitrary;
               
-              digi.mutate (arbitrary);  // invoke the mutation functor 
+              digi = arbitrary;         // invoke the mutation functor 
               
               CHECK (sum == checksum, "divergence after adding %f in iteration %d", arbitrary, i);
-              CHECK (0 == digi);     // the value itself is not touched
+              CHECK (digi == abitrary);
             }
         }
       
       
       void
-      checkDefaultMutator ()
+      verifyMutatorInfluence ()
         {
           TestDigxel digi;
           
+          // using the default mutator
           CHECK (0 == digi);
-          digi.mutate(12.3);
+          digi = 12.3;
+          CHECK (12.3 == digi);
+          
+          digi.mutator = limitingMutator;
+          CHECK (12.3 == digi);
+          digi = 12.3;
+          CHECK (1 == digi);
+          
+          digi.setValueRaw(12.3);
           CHECK (12.3 == digi);
         }
       
@@ -159,7 +192,7 @@ namespace test{
           TestDigxel digi;
           digi = 123456789.12345678;
           
-          string formatted (digi.show());  
+          string formatted (digi.show());              // should trigger assertion
           CHECK (formatted.length() <= digi.maxlen());
         }
       

From 336264a6beeba7d250f6915ec3629a23ad1e4653 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 5 Jan 2011 01:05:13 +0100
Subject: [PATCH 027/140] WIP first implementation draft for Digxel --
 problematic

This draft highlights problems with poliferation of
generated virtual methods (code bloat). Also it's
unnecessarily complex and especially the automatic
conversion to double *and* int creates a whole
shitload of problems....
---
 src/lib/time/digxel.cpp        |   7 +-
 src/lib/time/digxel.hpp        | 137 ++++++++++++++++++++++++++++-----
 tests/lib/time/digxel-test.cpp |   2 +-
 3 files changed, 121 insertions(+), 25 deletions(-)

diff --git a/src/lib/time/digxel.cpp b/src/lib/time/digxel.cpp
index 5c8046de7..ddf441561 100644
--- a/src/lib/time/digxel.cpp
+++ b/src/lib/time/digxel.cpp
@@ -21,12 +21,7 @@
 * *****************************************************/
 
 
-#include "lib/lumitime.hpp"
-#include "lib/time/timecode.hpp"
-#include "lib/time/timequant.hpp"
-#include "lib/time/formats.hpp"
-
-using std::string;
+#include "lib/time/digxel.hpp"
 
 
 namespace lib {
diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp
index 6cdc3888e..7d4e37d3a 100644
--- a/src/lib/time/digxel.hpp
+++ b/src/lib/time/digxel.hpp
@@ -63,10 +63,12 @@
 #define LIB_TIME_DIGXEL_H
 
 #include "lib/error.hpp"
-//#include "lib/symbol.hpp"
+#include "lib/symbol.hpp"
 
-//#include 
-#include   ////////////////////TODO
+#include 
+#include 
+#include   ///////////TODO
+#include 
 #include 
 
 
@@ -85,38 +87,97 @@ namespace time {
    * - will then format these numbers and cache the formatted representation
    * - can store and invoke a mutation functor
    *  
-   * @param TODO
+   * @note comparisons are assumed to be not performance relevant
    * @see lib::time::TCode
    * @todo WIP-WIP-WIP
    */
   class Digxel
+    : public boost::totally_ordered > >
     {
+      typedef const char* CBuf;
       
     public:
       virtual ~Digxel ();  ///< this is an ABC
-        
+      
+      operator int()     const { return getIntValue(); }
+      operator double()  const { return getDoubleValue(); }
+      
+      CBuf     show()          { return getFormatted(); }
+      void operator= (int i)   { return changeTo(i); }
+      void operator= (double d){ return changeTo(d); }
+      
+      
+      // Supporting totally_ordered
+      bool operator<  (Digxel const& o)  const { return double(*this) <  double(o); }
+      bool operator== (Digxel const& o)  const { return double(*this) == double(o); }
+      bool operator== (int    i)         const { return    int(*this) ==        i ; }
+      bool operator<  (int    i)         const { return    int(*this) <         i ; }
+      bool operator>  (int    i)         const { return    int(*this) >         i ; }
+      bool operator== (double d)         const { return double(*this) ==        d ; }
+      bool operator<  (double d)         const { return double(*this) <         d ; }
+      bool operator>  (double d)         const { return double(*this) >         d ; }
+      
+    protected:
+      virtual int    getIntValue()    const   =0;
+      virtual double getDoubleValue() const   =0;
+      virtual CBuf   getFormatted()           =0;
+      virtual void   changeTo (int i)         =0;
+      virtual void   changeTo (double d)      =0;
     };
   
   namespace digxel {
     
-//  using lib::Literal;
+    using std::string;
+    using lib::Literal;
+    using boost::lexical_cast;
+    
+    
     
     template
-    struct PrintfFormatter
+    struct ValTrait;
+    
+    template<>
+    struct ValTrait
       {
-        enum{ len = 6
-            , bufsiz = len+1
-        };
+        static int    asInt    (int val) { return val; }
+        static double asDouble (int val) { return val; }
+      };
+    
+    template<>
+    struct ValTrait
+      {
+        static int    asInt    (double val) { return std::floor(0.5+val); }  ///< in accordance with Lumiera's time handling RfC
+        static double asDouble (double val) { return val; }
+      };
+    
+    
+    
+    template
+    class PrintfFormatter
+      {
+        enum{ bufsiz = len+1 };
         
         char printbuffer_[bufsiz];
+        Literal formatSpec_;
         
-        static void
+      public:
+        PrintfFormatter (Literal fmt)
+          : printbuffer_()
+          , formatSpec_(fmt)
+          { 
+            printbuffer_[0] = '\0';
+          }
+        
+        void
         show (NUM val)
           {
             size_t space = std::snprintf (printbuffer_, bufsiz, "%5d", val);
             REQUIRE (space <= bufsiz, "Digxel value exceeded available buffer size. "
-                                      "For showing %d, %d chars instead of just %d would be required."
-                                    , val, space, bufsiz);
+                                      "For showing %s, %d chars instead of just %d would be required."
+                                    , lexical_cast(val), space, bufsiz);
           }
       };
     
@@ -125,11 +186,17 @@ namespace time {
     
     template<>
     struct Formatter
-      : PrintfFormatter
+      : PrintfFormatter
       {
-        enum{ len = 6
-            , bufsiz = len+1
-            };
+        Formatter() : PrintfFormatter("%5d") { }
+        
+      };
+    
+    template<>
+    struct Formatter
+      : PrintfFormatter
+      {
+        Formatter() : PrintfFormatter("%06.3f") { }
         
       };
   
@@ -150,12 +217,46 @@ namespace time {
         FMT buffer_;
         NUM value_;
         
+        
+        /* === Digxel implementation === */
+        int
+        getIntValue()  const
+          {
+            return ValTrait::asInt (value_);
+          }
+        
+        double
+        getDoubleValue()  const
+          {
+            return ValTrait::asDouble (value_);
+          }
+        
+        CBuf
+        getFormatted()
+          {
+            UNIMPLEMENTED("call formatting or cache");
+          }
+        
+        void
+        changeTo (int i)
+          {
+            UNIMPLEMENTED("mutate INT");
+          }
+        
+        void
+        changeTo (double d)
+          {
+            UNIMPLEMENTED("mutate FLOAT");
+          }
+
+        
       public:
         Holder ()
           : buffer_()
           , value_()
           { }
-          
+        
+        using Digxel::operator=;
       };
     
   }
diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp
index e5fffccc2..5a76553f3 100644
--- a/tests/lib/time/digxel-test.cpp
+++ b/tests/lib/time/digxel-test.cpp
@@ -141,7 +141,7 @@ namespace test{
               digi = arbitrary;         // invoke the mutation functor 
               
               CHECK (sum == checksum, "divergence after adding %f in iteration %d", arbitrary, i);
-              CHECK (digi == abitrary);
+              CHECK (digi == arbitrary);
             }
         }
       

From e7f5ce9e3360c8f6a6f0f62fefe5c2b4b47bf7f8 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 5 Jan 2011 02:43:29 +0100
Subject: [PATCH 028/140] WIP rework timecode format hierarchy

second try: eliminate base class,
work with concrete formats allways...
---
 doc/devel/uml/fig142725.png               | Bin 18325 -> 19348 bytes
 src/lib/time/digxel.hpp                   |  62 +++++++++-------------
 src/lib/time/formats.hpp                  |  39 ++++++++++++++
 src/lib/time/timecode.cpp                 |   1 +
 src/lib/time/timecode.hpp                 |  50 +++++++++++++----
 src/lib/time/timequant.hpp                |   3 +-
 tests/lib/time/time-quantisation-test.cpp |  18 ++++---
 uml/lumiera/128517                        |  59 ++++++++++++--------
 uml/lumiera/142725.diagram                |  54 +++++++++++--------
 uml/lumiera/5.session                     |   2 +-
 uml/lumiera/lumiera.prj                   |   2 +-
 wiki/renderengine.html                    |   4 +-
 12 files changed, 195 insertions(+), 99 deletions(-)

diff --git a/doc/devel/uml/fig142725.png b/doc/devel/uml/fig142725.png
index 08a0e46ef17a0ab88e58084bb711fac4ea176a8d..6e8c570d81b164a4ab1fc290b50e592f5e7dca80 100644
GIT binary patch
literal 19348
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
W>J+|Q0G_r5c_}6*S|F_d{{H~`be&KD

literal 18325
zcmc(H1zc3?`tB%-D4-x9(gM;FlF|(l64Kq$-8m>A2vSngDIncQhjfid3_a4_3?27l
z@3YU@cb|R!_x^73({VAbS+mx+zVCgX_j#UoeNd2-#Jop%4+4Q;O1%_&4T0Pg2Y;2(
zZi08BW7~5fkS7o+F=1u5q|IqhZCvEs)h;Y3?Mb$d>~Ot)a7M2B40Do-%-{>%hui+a
zR9U%cpQMA+Od|E&Zbye1EHUz#n{&x5#L>{nx18i9;c@Why7wNKnFoJs63jltLhmv$
zcq4e`A>eiVIXOA`qHln8`@(C9EmcvLItl`5?il$|3(iCRhC3=G;{Vx;aQ~VYo48`$
z{$@eDF{`=GEl9JsvSrF$a@lN@k@K*aELzmU5D4U(-~`bP26_%BB~Wu+;W>`yMvPP!GyPxvqN2w1bcO&dwc?q_&kfV_^NVv#^*uadu`+BCVWEWl=d*!&@v_zqRSz9q|_Y($^U5!jYvx)?+j1<5cHB=9*w;5f)j4mx4(m?
z5XbGh!G5C#QwPVhz
z$k7{+0+}ZFH>9#zJGm5K%xw?@h$=>UHn!#CUA9*V>{udB+gsxm=6)mjSB(yf7|x{x
zY^v>@@@$Xm;l)>W+cSNw`GqRIifEAur>kFN)MPYhAk9@P)eWWUGO@R~d3oL4Ziz|Q
zn8Ax`{6sOy{WEC6C9=fJW=aqYw`a>C6WL6syCM~x*K5(p<&sYa>)$W+6Wu49p|q)nbL6OG@PNdJ7GiGF(ZWaMmNj2H_(KZ7rNOXzWkK`*G}`lzU<#G;y5{j
zRWy9{4~R7yga8|r_yMq<;^|-M&D&L9&@s6+|)~j4FPTLuL!di#6pvLX`{k?M7Ve)gMkyyr5
zbn?ADrp2X{@yd0T-@fK#uQDj8jh@Lt@qaC9j$NwTlim0c>$Cri$>xm6*1vwQCpwbS
zJC-SV?{r;hC94wJ?yypMKMr*i9iLZde!gv{1dRGRu6^@Bb_8Lg>*#p%^P>z6
zmHT5|UMh+Nv8pZ=e346@K=rF``NinJGTJ|+zf+`#K%OEKw;Su^(b83KQpNc2W
z%gaCJVgETY!o}`nQo6W3S#6Xl@c?=}t41AV4oeZ8?|gtIVl{DKzCMbRl$7G&NQ6yP
z*Iqwx8+}wh;lRV$#yp1bh25{FOC&RSTNnxNn}MVngYWMLl6j9dC#yvR@9!_eiWhnc
zzlB50K(uq&Tc}ClpG+h>Tw8RWYttZady4`R!1%arJdMx!uC$1Vrs3QMCK1Jbk{)c4
zXIfR`W_`Uq(!2ZX+%{96-o5iQH<$6py7QoD@Qso5()^J9U`Dg^x(g=JC@+skSwd&*
zqN`o*a}TE`e$Pf8hlMjXQ>IW7UXVIWVd*6-S4m03K|!U8ibVbE^-imr`oDzwIlCur
z-n?05i(v8}t9Rc=XRf!KYx{s)heJ;mDs`0
zr_<$&$PF+Rx28Q0H^$*bI_p2uMT>PC9zJ|X>a`;*DcQMJBUiI=mP>dKCGF`c|5|@C
zFZGKTmwStrGc$!{WpZup)`$}Fj4>+Psc-I^6U=9a?b}oFrB>rhU{L-h0V%AzEW`B7
zhgW9Tro+xyT#ad`GF!^dMS`}b`4c&GKR-1!!~LZYXHO^W%m
zK^d8uBj%Pmq*)_X(OL9WgG*fpw^8&zki|qq-0$>5RT$}Fdznipmv`d_(H@2a#ZQQ*
zXo|3=7@@ve7hBiImna*7jZDg)%(2wttJ<@_MrI$*o8&+q4ymPdJDuq_AmPmH*5ODz
z=X{gXcQQG*tM!{
zkb8SQ{rw}gPap^H_`avQd3wsnvwpFJ7xgD{!E2v=A>j8oC^hP=V7d(<8HAs(nHAYg
z*E%ls4c6iwE&uwJ=8O$lK}UiFh0%5WaxgMOs|0%!UnNesfh%Ol(^K9GZ|-PoGgJT6
z6J@S-P@wJSTcQN18Jt1;2kw5W{Jo0x9%J5p1QSSgAj1a|i7Qqxp*?M@M*krwwbyq(
z8Si($Aj}qDAIG?>oB~sr*hyd9s}7=Nu`8<%bBS
z=BhEfxI$J^1uIvSbaL?D{iU;f%vy1yk!4ZLsh<1^DK7hZ27bS%t0baN_6BkDU#7=h`P?Z$T7X
zJiew9gt`YCD&YE+?>9Gkn|fVcgyX4q$1uR>=c*FnZx$(TLY!oo-Ls|T<=wU$J|}RH
zh={7G{#uM<)(Z)Gi7UeQJGj@XbAmuFeyXJqV9faV@JD@G^4ja>YPxXAvEz_Y#E>vb
z;W2G_hm4|bX#5Kje?;!z1dYbZkWc-w4!3PQ+Y%se8_dk{L?&#_v3B)Z{>0bwqPfhi
z4e&2;`}LMQQ-U$uRRGc@tmmF>G1%H~%&JrGR+)91NvHPF;}>=y?SKHv&HWV{+bA=G
z^B(!npRs7<_er&lbRBx4xk#b)yIhE;p^pb$`#!JKxuOrBB=hj6L|`Sv#zw}uehsch
zg(#35jFBw_k}=T+|16pH6Fj#Q#&vz``1+@nWM=~S&7e0HCU|*lN#2&sd(vLC))l!8
zFT&j4bFdvWJZC7bcf35ubM|@Jr9j>mO=mk*>s)uQ8jQ8hfr=on-4joMy(1#r^bj_c
zmb@c*i?sePUtKtz>{$okJ31B6`G0kY{H
zH@pt2AxW(&D`KpjJootM=pY=L0hdj}dP{f<32%nws8ep0^$mzJjrin!?BI+Q>-zka
z6*K3Ab(q5E472mjn`us`d8s!}v#R8VBrsP9t}ENOq-R_=Plb~)9+t!u+fyYTbhD*B
zUTT8wlRaLB=?~psxtz3Ah?c&Li8O6a&6SdHx-kMXW>um7Spj21z5iY=!q|6-9>;uQ
zIezAJ-BRb4GFNZ5ID|0IF3UXC)K@h(;>M|R=}StOzU%d-+S}_*NwCS8RUavlBJ88w
z1tC%?0`ERyqc`>~Wbn)ap$!UbD2I37?ppE7Jlw^4xZP3NEx≪
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 > >
+//  : public boost::totally_ordered > >
     {
       typedef const char* CBuf;
       
@@ -103,29 +103,23 @@ namespace time {
       virtual ~Digxel ();  ///< this is an ABC
       
       operator int()     const { return getIntValue(); }
-      operator double()  const { return getDoubleValue(); }
       
       CBuf     show()          { return getFormatted(); }
-      void operator= (int i)   { return changeTo(i); }
-      void operator= (double d){ return changeTo(d); }
       
       
-      // Supporting totally_ordered
-      bool operator<  (Digxel const& o)  const { return double(*this) <  double(o); }
-      bool operator== (Digxel const& o)  const { return double(*this) == double(o); }
-      bool operator== (int    i)         const { return    int(*this) ==        i ; }
-      bool operator<  (int    i)         const { return    int(*this) <         i ; }
-      bool operator>  (int    i)         const { return    int(*this) >         i ; }
-      bool operator== (double d)         const { return double(*this) ==        d ; }
-      bool operator<  (double d)         const { return double(*this) <         d ; }
-      bool operator>  (double d)         const { return double(*this) >         d ; }
+//    // Supporting totally_ordered
+//    bool operator<  (Digxel const& o)  const { return double(*this) <  double(o); }
+//    bool operator== (Digxel const& o)  const { return double(*this) == double(o); }
+//    bool operator== (int    i)         const { return    int(*this) ==        i ; }
+//    bool operator<  (int    i)         const { return    int(*this) <         i ; }
+//    bool operator>  (int    i)         const { return    int(*this) >         i ; }
+//    bool operator== (double d)         const { return double(*this) ==        d ; }
+//    bool operator<  (double d)         const { return double(*this) <         d ; }
+//    bool operator>  (double d)         const { return double(*this) >         d ; }
       
     protected:
       virtual int    getIntValue()    const   =0;
-      virtual double getDoubleValue() const   =0;
       virtual CBuf   getFormatted()           =0;
-      virtual void   changeTo (int i)         =0;
-      virtual void   changeTo (double d)      =0;
     };
   
   namespace digxel {
@@ -225,29 +219,23 @@ namespace time {
             return ValTrait::asInt (value_);
           }
         
-        double
-        getDoubleValue()  const
-          {
-            return ValTrait::asDouble (value_);
-          }
-        
         CBuf
         getFormatted()
           {
             UNIMPLEMENTED("call formatting or cache");
           }
-        
-        void
-        changeTo (int i)
-          {
-            UNIMPLEMENTED("mutate INT");
-          }
-        
-        void
-        changeTo (double d)
-          {
-            UNIMPLEMENTED("mutate FLOAT");
-          }
+//      
+//      void
+//      changeTo (int i)
+//        {
+//          UNIMPLEMENTED("mutate INT");
+//        }
+//      
+//      void
+//      changeTo (double d)
+//        {
+//          UNIMPLEMENTED("mutate FLOAT");
+//        }
 
         
       public:
diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp
index daea02508..ffb46a868 100644
--- a/src/lib/time/formats.hpp
+++ b/src/lib/time/formats.hpp
@@ -118,7 +118,46 @@ namespace time {
     static const Frames  FRAMES;
     static const Smpte   SMPTE;
     static const Hms     HMS;
+    
+  }
+  // ====== forward declarationss of concrete Timecode types
+  
+  class FrameNr;
+  class SmpteTC;
+  class HmsTC;
+  class Secs;
   
   
+  namespace format {
+    
+    template
+    struct Traits;
+    
+    template<>
+    struct Traits
+      {
+        typedef FrameNr TimeCode;
+      };
+    
+    template<>
+    struct Traits
+      {
+        typedef SmpteTC TimeCode;
+      };
+    
+    template<>
+    struct Traits
+      {
+        typedef HmsTC TimeCode;
+      };
+    
+    template<>
+    struct Traits
+      {
+        typedef Secs TimeCode;
+      };
+    
+    
+    
 }}} // lib::time::format
 #endif
diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp
index b739f411f..24c00de5b 100644
--- a/src/lib/time/timecode.cpp
+++ b/src/lib/time/timecode.cpp
@@ -34,6 +34,7 @@ namespace time {
   
   
   Format::~Format() { }  // emit VTable here....
+  TCode::~TCode()   { }
   
   
   /** */
diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp
index 18d57e52d..f536dac2e 100644
--- a/src/lib/time/timecode.hpp
+++ b/src/lib/time/timecode.hpp
@@ -38,20 +38,19 @@ namespace time {
   
   
   /**
-   * fixed format time specification.
-   * @param FMT the actual timecode format to use
+   * Interface: fixed format timecode specification.
    * @see time::Format
    * @todo WIP-WIP-WIP
    */
-  template
   class TCode
     {
       
     public:
+      virtual ~TCode();
       
-      string describe()  const;
-      
-      
+      virtual string show()      const   =0;
+      virtual string describe()  const   =0;
+      virtual Time   getTime()   const   =0;
     };
   
   
@@ -65,12 +64,11 @@ namespace time {
    * FrameNr values interchangeable with integral numbers. 
    */
   class FrameNr
-    : public TCode
+    : public TCode
     {
       
     public:
       FrameNr (QuTime const& quantisedTime);
-      FrameNr (TCode const&);
       
       operator long()  const;
     };
@@ -81,12 +79,11 @@ namespace time {
    * 
    */
   class SmpteTC
-    : public TCode
+    : public TCode
     {
       
     public:
       SmpteTC (QuTime const& quantisedTime);
-      SmpteTC (TCode const&);
       
       int getSecs   () const; 
       int getMins   () const; 
@@ -96,5 +93,38 @@ namespace time {
   
   
   
+  /**
+   * 
+   */
+  class HmsTC
+    : public TCode
+    {
+      
+    public:
+      HmsTC (QuTime const& quantisedTime);
+      
+      double getMillis () const;
+      int getSecs      () const; 
+      int getMins      () const; 
+      int getHours     () const; 
+    };
+  
+  
+  
+  /**
+   * 
+   */
+  class Secs
+    : public TCode
+    {
+      
+    public:
+      Secs (QuTime const& quantisedTime);
+      
+      operator FSecs()  const;
+    };
+  
+  
+  
 }} // lib::time
 #endif
diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp
index 499c5ae43..25cd9b40f 100644
--- a/src/lib/time/timequant.hpp
+++ b/src/lib/time/timequant.hpp
@@ -56,7 +56,8 @@ namespace time {
       bool supports()  const;
       
       template
-      TCode formatAs()  const;
+      typename format::Traits::TimeCode
+      formatAs()  const;
     };
   
   
diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp
index eb5acf171..8ef868412 100644
--- a/tests/lib/time/time-quantisation-test.cpp
+++ b/tests/lib/time/time-quantisation-test.cpp
@@ -95,21 +95,27 @@ namespace test{
           CHECK ( qVal.supports());
           CHECK ( qVal.supports());
           
-          TCode smpteTCode = qVal.formatAs();
+          SmpteTC smpteTCode = qVal.formatAs();
           showTimeCode (smpteTCode);
           
-          TCode frameTCode = qVal.formatAs();
+          HmsTC pureTimeCode = qVal.formatAs();
+          showTimeCode (pureTimeCode);
+          
+          FrameNr frameTCode = qVal.formatAs();
           showTimeCode (frameTCode);
           
-          FrameNr count(frameTCode);
-//        CHECK (string(count) == frameTCode.part[0]));
+          Secs seconds  = qVal.formatAs();
+          showTimeCode (seconds);
         }
       
       template
       void
       showTimeCode (TC timecodeValue)
         {
-//        cout << timecodeValue.describe() << " = " << join (timecodeValue.part(), ":") << endl;
+          cout << timecodeValue.describe() 
+               << " time = "<< timecodeValue.getTime() 
+               << " code = "<< timecodeValue.show()
+               << endl;
         }
       
       
@@ -140,7 +146,7 @@ namespace test{
           
           TimeGrid::build("special_funny_grid", 1);      // provide the grid's definition (1 frame per second)
           
-          int cnt = 0;//////funny.formatAs().part["count"];
+          int cnt = funny.formatAs();
                                                          // and now performing quantisation is OK 
           SmpteTC smpte (funny);                         // also converting into SMPTE (which implies frame quantisation)
           CHECK (0 == smpte.getFrames());                // we have 1fps, thus the frame part is always zero!
diff --git a/uml/lumiera/128517 b/uml/lumiera/128517
index dcd28563e..21aba95a2 100644
--- a/uml/lumiera/128517
+++ b/uml/lumiera/128517
@@ -1,6 +1,6 @@
 format 58
 "CommonLib" // CommonLib
-  revision 23
+  revision 24
   modified_by 5 "hiv"
   // class settings
   //class diagram settings
@@ -956,14 +956,23 @@ ${inlines}
 	  b parent class_ref 134917 // Time
       end
 
-      classrelation 208901 // 
-	relation 198021 --->
+      classrelation 212613 // 
+	relation 201733 --->
+	  a role_name "" protected
+	    cpp default "    ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value};
+"
+	    classrelation_ref 212613 // 
+	  b parent class_ref 170885 // FrameNr
+      end
+
+      classrelation 212741 // 
+	relation 201861 --->
 	  stereotype "yield"
 	  a role_name "" protected
 	    cpp default "    ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value};
 "
-	    classrelation_ref 208901 // 
-	  b parent class_ref 170629 // TCode
+	    classrelation_ref 212741 // 
+	  b parent class_ref 172677 // SmpteTC
       end
     end
 
@@ -1099,9 +1108,6 @@ ${inlines}
 
     class 170629 "TCode"
       visibility package 
-      nformals 1
-      formal name "F" type "class" explicit_default_value ""
-        explicit_extends ""
       cpp_decl "${comment}${template}class ${name}${inherit}
   {
 ${members}  };
@@ -1121,13 +1127,6 @@ ${inlines}
 	  b parent class_ref 170629 // TCode
       end
 
-      classrelation 208773 // 
-	relation 197893 -_->
-	  a default
-	    cpp default "#include in source"
-	    classrelation_ref 208773 // 
-	  b parent class_ref 170757 // Format
-      end
     end
 
     class 170757 "Format"
@@ -1147,9 +1146,6 @@ ${inlines}
 
     class 170885 "FrameNr"
       visibility package 
-      nactuals 1
-      actual class class_ref 170629 // TCode
-        rank 0 explicit_value ""
       cpp_decl "${comment}${template}class ${name}${inherit}
   {
 ${members}  };
@@ -1282,9 +1278,6 @@ ${inlines}
 
     class 172677 "SmpteTC"
       visibility package 
-      nactuals 1
-      actual class class_ref 170629 // TCode
-        rank 0 explicit_value ""
       cpp_decl "${comment}${template}class ${name}${inherit}
   {
 ${members}  };
@@ -1303,6 +1296,30 @@ ${inlines}
 	    classrelation_ref 211461 // 
 	  b parent class_ref 170629 // TCode
       end
+
+      classrelation 212869 // parts ()
+	relation 201989 *-->
+	  a role_name "parts" multiplicity "4" protected
+	    cpp default "    ${comment}${static}${mutable}${volatile}${const}${type} ${name}${value};
+"
+	    classrelation_ref 212869 // parts ()
+	  b parent class_ref 173829 // Digxel
+      end
+    end
+
+    class 173829 "Digxel"
+      visibility package 
+      cpp_decl "${comment}${template}class ${name}${inherit}
+  {
+${members}  };
+${inlines}
+"
+      java_decl ""
+      php_decl ""
+      python_2_2 python_decl ""
+      idl_decl ""
+      explicit_switch_type ""
+      
     end
   end
 
diff --git a/uml/lumiera/142725.diagram b/uml/lumiera/142725.diagram
index 70a003dcd..18a84f06e 100644
--- a/uml/lumiera/142725.diagram
+++ b/uml/lumiera/142725.diagram
@@ -38,7 +38,7 @@ classcanvas 131589 class_ref 170501 // QuTimeSpan
 end
 classcanvas 131973 class_ref 170629 // TCode
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 248 450 2000
+  xyz 319 434 2000
 end
 classcanvas 132101 class_ref 170757 // Format
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
@@ -46,7 +46,7 @@ classcanvas 132101 class_ref 170757 // Format
 end
 classcanvas 132997 class_ref 170885 // FrameNr
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 243 545 2000
+  xyz 314 507 2000
 end
 classcanvas 133253 class_ref 171013 // CompoundGrid
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
@@ -58,25 +58,29 @@ classcanvas 133509 class_ref 172165 // Offset
 end
 classcanvas 135045 class_ref 137093 // Meta
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 514 237 2000
+  xyz 514 237 2005
 end
 note 135429 "Asset System"
   xyzwh 504 197 2000 66 47
 classcanvas 135557 class_ref 172293 // Frames
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 392 507 1995
+  xyz 392 507 1979
 end
 classcanvas 135685 class_ref 172421 // Smpte
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 446 533 2010
+  xyz 446 533 1994
 end
 classcanvas 136069 class_ref 172549 // Hms
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 421 523 2005
+  xyz 421 523 1984
 end
 classcanvas 136325 class_ref 172677 // SmpteTC
   draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
-  xyz 308 546 2000
+  xyz 242 507 2000
+end
+classcanvas 137349 class_ref 173829 // Digxel
+  draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_members_multiplicity default show_members_initialization default member_max_width 0 show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default show_stereotype_properties default
+  xyz 191 548 2000
 end
 relationcanvas 128517 relation_ref 195205 // 
   geometry VHV unfixed
@@ -120,20 +124,9 @@ relationcanvas 131717 relation_ref 197637 // 
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
-relationcanvas 132485 relation_ref 197893 // 
-  from ref 131973 z 1999 to point 290 450
-  line 132869 z 1999 to ref 132101
-  no_role_a no_role_b
-  no_multiplicity_a no_multiplicity_b
-end
-relationcanvas 132613 relation_ref 198021 // 
-  from ref 129669 z 1999 stereotype "<>" xyz 275 415 3000 to ref 131973
-  no_role_a no_role_b
-  no_multiplicity_a no_multiplicity_b
-end
 relationcanvas 132741 relation_ref 198149 // 
   decenter_end 595
-  from ref 130181 z 1999 stereotype "<>" xyz 360 414 3000 to ref 132101
+  from ref 130181 z 1999 stereotype "<>" xyz 360 413 3000 to ref 132101
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
@@ -194,10 +187,29 @@ relationcanvas 136197 relation_ref 200453 // 
 end
 relationcanvas 136453 relation_ref 200581 // 
   geometry VHV
-  from ref 136325 z 1999 to point 334 522
-  line 136581 z 1999 to point 268 522
+  from ref 136325 z 1999 to point 268 487
+  line 136581 z 1999 to point 339 487
   line 136709 z 1999 to ref 131973
   no_role_a no_role_b
   no_multiplicity_a no_multiplicity_b
 end
+relationcanvas 136837 relation_ref 201733 // 
+  from ref 129669 z 1999 to ref 132997
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 136965 relation_ref 201861 // 
+  decenter_end 754
+  from ref 129669 z 1999 stereotype "<>" xyz 244 416 3000 to point 295 437
+  line 137093 z 1999 to ref 136325
+  no_role_a no_role_b
+  no_multiplicity_a no_multiplicity_b
+end
+relationcanvas 137477 relation_ref 201989 // 
+  geometry HVr
+  from ref 136325 z 1999 to point 268 565
+  line 137605 z 1999 to ref 137349
+  role_a_pos 245 554 3000 no_role_b
+  multiplicity_a_pos 245 573 3000 no_multiplicity_b
+end
 end
diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session
index a3d4a7eab..09a1aa0c4 100644
--- a/uml/lumiera/5.session
+++ b/uml/lumiera/5.session
@@ -5,7 +5,7 @@ diagrams
   objectdiagram_ref 138885 // ModelAssetRelations
     730 488 100 4 0 0
   active  classdiagram_ref 142725 // Time flavours
-    595 629 100 4 0 0
+    595 646 100 4 0 0
 end
 show_stereotypes
 selected 
diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj
index 1380666d0..f1dc5de9f 100644
--- a/uml/lumiera/lumiera.prj
+++ b/uml/lumiera/lumiera.prj
@@ -1,6 +1,6 @@
 format 58
 "lumiera"
-  revision 67
+  revision 68
   modified_by 5 "hiv"
   cpp_root_dir "../../src/"
 
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 5f0185605..63faf288a 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -6561,7 +6561,7 @@ Thus no server and no network connection is needed. Simply open the file in your
  * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
 
-
+
The term &raquo;Time&laquo; spans a variety of vastly different entities. Within a NLE we get to deal with various //flavours of time values.//
 ;continuous time
 :without any additional assumptions, ''points in time'' can be specified with arbitrary precision.
@@ -6616,6 +6616,8 @@ __delayed quantisation__: with this approach, the information loss is delayed as
 
 !!!discussion
 For Lumiera, the static typing approach is of limited value -- it excels when values belonging to different scales are actually treated differently. There are such cases, but rather on the data handling level, e.g. sound samples are always handled block wise. But regarding time values, the unifying aspect is more important, which leads to prefering a dynamic (run time typed) approach, while //erasing// the special differences most of the time. Yet the dynamic and open nature of the Lumiera high-level model favours the delayed quantisation pattern; the same values may require different quantisation depending on the larger model context an object is encountered in. This solution might be to general and heavy weight at times though. Thus, for important special cases, the accessors should return tagged values, preferably even with differing static type. Time codes can be integrated this way, but most notably the ''frame numbers'' used for addressing throughout the backend, can be implemented as such specifically typed tagged values; the tag here denotes the quantiser and thus the underlying grid -- it should be implemented as hash-ID for smooth integration with code written in plain C.
+
+At the level of individual timecode formats, we're lacking a common denominator; thus it is preferrable to work with different concrete timecode classes through //generic programming.// This way, each timecode format can expose operations specific only to the given format. Especially, different timecode formats expose different //component fields,// modelled by the generic ''Digxel'' concept. There is a common baseclass ~TCode though, which can be used for //type erasure.//
 &rarr; more on [[usage situations|TimeUsage]]
 
From 25d7af449e8173c4ca8b26e6c7c3e7a613b36bb1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 5 Jan 2011 03:07:42 +0100 Subject: [PATCH 029/140] WIP adapt / rewrite Digxel implementation --- src/lib/time/digxel.cpp | 2 - src/lib/time/digxel.hpp | 101 ++++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/lib/time/digxel.cpp b/src/lib/time/digxel.cpp index ddf441561..b841acf0e 100644 --- a/src/lib/time/digxel.cpp +++ b/src/lib/time/digxel.cpp @@ -28,8 +28,6 @@ namespace lib { namespace time { - Digxel::~Digxel () { } - // A hint for the compiler to emit VTables and magic stuff here /** */ diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index a1fd73b15..7d63770f9 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -77,58 +77,14 @@ namespace time { // using std::string; - /** - * A number element for building structured numeric displays. - * The purpose is to represent parts of a numeric format, like - * e.g. the sexagesimal "digits" of a timecode display. Digxel - * - is customised by template parameters to a specific number format - * - requires that any number set must not overflow the format buffer - * - can receive new numbers by assignment - * - will then format these numbers and cache the formatted representation - * - can store and invoke a mutation functor - * - * @note comparisons are assumed to be not performance relevant - * @see lib::time::TCode - * @todo WIP-WIP-WIP - */ - class Digxel -// : public boost::totally_ordered > > - { - typedef const char* CBuf; - - public: - virtual ~Digxel (); ///< this is an ABC - - operator int() const { return getIntValue(); } - - CBuf show() { return getFormatted(); } - - -// // Supporting totally_ordered -// bool operator< (Digxel const& o) const { return double(*this) < double(o); } -// bool operator== (Digxel const& o) const { return double(*this) == double(o); } -// bool operator== (int i) const { return int(*this) == i ; } -// bool operator< (int i) const { return int(*this) < i ; } -// bool operator> (int i) const { return int(*this) > i ; } -// bool operator== (double d) const { return double(*this) == d ; } -// bool operator< (double d) const { return double(*this) < d ; } -// bool operator> (double d) const { return double(*this) > d ; } - - protected: - virtual int getIntValue() const =0; - virtual CBuf getFormatted() =0; - }; - namespace digxel { using std::string; using lib::Literal; using boost::lexical_cast; - + typedef const char* CBuf; + template struct ValTrait; @@ -203,11 +159,11 @@ namespace time { * @todo WIP-WIP-WIP */ template< typename NUM - , class FMT = digxel::Formatter + , class FMT > class Holder - : public Digxel { + protected: FMT buffer_; NUM value_; @@ -244,10 +200,55 @@ namespace time { , value_() { } - using Digxel::operator=; }; - } + } //(End) digxel configuration namespace + + /** + * A number element for building structured numeric displays. + * The purpose is to represent parts of a numeric format, like + * e.g. the sexagesimal "digits" of a timecode display. Digxel + * - is customised by template parameters to a specific number format + * - requires that any number set must not overflow the format buffer + * - can receive new numbers by assignment + * - will then format these numbers and cache the formatted representation + * - can store and invoke a mutation functor + * + * @note comparisons are assumed to be not performance relevant + * @see lib::time::TCode + * @todo WIP-WIP-WIP + */ + template< typename NUM + , class FMT = digxel::Formatter + > + class Digxel + : public digxel::Holder + , public boost::totally_ordered, + boost::totally_ordered, NUM + > > + { + typedef digxel::Holder _Holder; + + typedef const char* CBuf; + + public: + + operator int() const { return getIntValue(); } + + CBuf show() { return getFormatted(); } + + + //---Supporting-totally_ordered--------- + bool operator< (Digxel const& o) const { return value_ < NUM(o); } + bool operator== (Digxel const& o) const { return value_ == NUM(o); } + bool operator== (NUM n) const { return value_ == n ; } + bool operator< (NUM n) const { return value_ < n ; } + bool operator> (NUM n) const { return value_ > n ; } + + protected: + virtual int getIntValue() const =0; + virtual CBuf getFormatted() =0; + }; From 2edaae5c5b50402cf5252310f16608905890c428 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 5 Jan 2011 04:19:43 +0100 Subject: [PATCH 030/140] simplify util::isnil implementation doh! --- src/lib/util.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/util.hpp b/src/lib/util.hpp index 1383e6244..cc1d18909 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -26,7 +26,6 @@ #include #include -#include #include @@ -84,7 +83,7 @@ namespace util { inline bool isnil (const char* pCStr) { - return !pCStr || 0 == std::strlen(pCStr); + return !pCStr || !(*pCStr); } From e66bed65e4a3d0c387c928132e7fa22dff2955d8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 5 Jan 2011 05:14:10 +0100 Subject: [PATCH 031/140] WIP stubs for timecode string representation --- src/lib/time/display.hpp | 14 +++++----- src/lib/time/timecode.hpp | 31 ++++++++++++++++++++--- tests/lib/time/time-quantisation-test.cpp | 5 ++-- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/lib/time/display.hpp b/src/lib/time/display.hpp index 2c829d9fc..27c202da5 100644 --- a/src/lib/time/display.hpp +++ b/src/lib/time/display.hpp @@ -34,14 +34,12 @@ namespace lib { namespace time { - - /** writes time value, formatted as HH:MM:SS:mmm - * @see lumiera_tmpbuf_print_time */ - inline std::ostream& - operator<< (std::ostream& os, Time const& t) - { - return os << std::string(t); - } + + /* === shortcuts for diagnostic output === */ + + /** writes time value, formatted as HH:MM:SS:mmm */ + inline std::ostream& operator<< (std::ostream& os, Time const& t) { return os << string(t); } + inline std::ostream& operator<< (std::ostream& os, TCode const& t) { return os << string(t); } diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index f536dac2e..6cc526d79 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -27,6 +27,7 @@ #include "lib/time/timevalue.hpp" #include "lib/time/formats.hpp" +//#include //#include #include @@ -48,9 +49,14 @@ namespace time { public: virtual ~TCode(); - virtual string show() const =0; - virtual string describe() const =0; - virtual Time getTime() const =0; + operator string() const { return show(); } + string describe() const { return tcID(); } + Time getTime() const { return Time(calc()); } + + protected: + virtual string show() const =0; + virtual string tcID() const =0; + virtual TimeValue calc() const =0; }; @@ -66,6 +72,10 @@ namespace time { class FrameNr : public TCode { + + virtual string show() const ; + virtual string tcID() const ; + virtual TimeValue calc() const ; public: FrameNr (QuTime const& quantisedTime); @@ -82,6 +92,10 @@ namespace time { : public TCode { + virtual string show() const ; + virtual string tcID() const ; + virtual TimeValue calc() const ; + public: SmpteTC (QuTime const& quantisedTime); @@ -100,6 +114,10 @@ namespace time { : public TCode { + virtual string show() const ; + virtual string tcID() const ; + virtual TimeValue calc() const ; + public: HmsTC (QuTime const& quantisedTime); @@ -118,13 +136,20 @@ namespace time { : public TCode { + virtual string show() const ; + virtual string tcID() const ; + virtual TimeValue calc() const ; + public: Secs (QuTime const& quantisedTime); operator FSecs() const; }; + + /** writes time value, formatted as HH:MM:SS:mmm */ + }} // lib::time #endif diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index 8ef868412..50d9d9127 100644 --- a/tests/lib/time/time-quantisation-test.cpp +++ b/tests/lib/time/time-quantisation-test.cpp @@ -23,8 +23,9 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" -#include "lib/time/timequant.hpp" #include "proc/asset/meta/time-grid.hpp" +#include "lib/time/timequant.hpp" +#include "lib/time/display.hpp" #include "lib/util.hpp" #include @@ -114,7 +115,7 @@ namespace test{ { cout << timecodeValue.describe() << " time = "<< timecodeValue.getTime() - << " code = "<< timecodeValue.show() + << " code = "<< timecodeValue << endl; } From 031f61f31da0158c422623fce2a9aa3490392f10 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 5 Jan 2011 05:14:28 +0100 Subject: [PATCH 032/140] WIP prototypical Digxel implementation complete --- src/lib/time/digxel.hpp | 157 +++++++++++++++++---------------- tests/lib/time/digxel-test.cpp | 43 +++++---- 2 files changed, 109 insertions(+), 91 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 7d63770f9..2d99aa7fd 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -64,9 +64,11 @@ #include "lib/error.hpp" #include "lib/symbol.hpp" +#include "lib/util.hpp" #include #include +#include #include ///////////TODO #include #include @@ -75,11 +77,11 @@ namespace lib { namespace time { -// using std::string; + using std::string; namespace digxel { - using std::string; + using util::cStr; using lib::Literal; using boost::lexical_cast; @@ -117,17 +119,31 @@ namespace time { PrintfFormatter (Literal fmt) : printbuffer_() , formatSpec_(fmt) - { - printbuffer_[0] = '\0'; + { + clear(); } - void + void clear() { printbuffer_[0] = '\0'; } + bool empty() { return bool(*printbuffer_); } + + size_t + maxlen() const + { + return len; + } + + CBuf show (NUM val) { - size_t space = std::snprintf (printbuffer_, bufsiz, "%5d", val); - REQUIRE (space <= bufsiz, "Digxel value exceeded available buffer size. " - "For showing %s, %d chars instead of just %d would be required." - , lexical_cast(val), space, bufsiz); + if (empty()) + { + size_t space = std::snprintf (printbuffer_, bufsiz, formatSpec_, val); + REQUIRE (space < bufsiz, "Digxel value exceeded available buffer size. " + "For showing %s, %lu chars instead of just %d would be required." + , cStr(lexical_cast(val)), space, bufsiz); ///////////TICKET #197 + } + ENSURE (!empty()); + return printbuffer_; } }; @@ -149,61 +165,10 @@ namespace time { Formatter() : PrintfFormatter("%06.3f") { } }; - - /** - * The outward hull of a concrete Digxel implementation. - * Inheriting from the Digxel interface, it embodies a concrete - * Formatter specialised to yield the desired behaviour. - * - * @param TODO - * @todo WIP-WIP-WIP - */ - template< typename NUM - , class FMT - > - class Holder - { - protected: - FMT buffer_; - NUM value_; - - - /* === Digxel implementation === */ - int - getIntValue() const - { - return ValTrait::asInt (value_); - } - - CBuf - getFormatted() - { - UNIMPLEMENTED("call formatting or cache"); - } -// -// void -// changeTo (int i) -// { -// UNIMPLEMENTED("mutate INT"); -// } -// -// void -// changeTo (double d) -// { -// UNIMPLEMENTED("mutate FLOAT"); -// } - - - public: - Holder () - : buffer_() - , value_() - { } - - }; } //(End) digxel configuration namespace + /** * A number element for building structured numeric displays. * The purpose is to represent parts of a numeric format, like @@ -222,32 +187,74 @@ namespace time { , class FMT = digxel::Formatter > class Digxel - : public digxel::Holder - , public boost::totally_ordered, - boost::totally_ordered, NUM - > > + : public boost::totally_ordered > +// , +// boost::totally_ordered, NUM +// > > { - typedef digxel::Holder _Holder; + mutable + FMT buffer_; + NUM value_; - typedef const char* CBuf; + static NUM use_newValue_as_is (NUM n) { return n; } + typedef std::tr1::function _Mutator; public: + /** a functor to be applied on any new digxel value. + * This allows individual instances to limit the possible digxel values, + * or to update an compound value (e.g. a time comprised of hour, minute + * and second digxel elements). By default, new values can be set without + * any restrictions or side effects. + */ + _Mutator mutator; - operator int() const { return getIntValue(); } - CBuf show() { return getFormatted(); } + Digxel () + : buffer_() + , value_() + , mutator(use_newValue_as_is) + { } + + // using the standard copy operations + + operator NUM() const { return value_; } + operator string() const { return show(); } + + size_t maxlen() const { return buffer_.maxlen(); } + + digxel::CBuf + show() const + { + return buffer_.show (value_); + } + + + void + operator= (NUM n) + { + NUM changedValue = mutator(n); + this->setValueRaw (changedValue); + } + + void + setValueRaw (NUM newVal) + { + if (newVal != value_) + { + value_ = newVal; + buffer_.clear(); + } + } + //---Supporting-totally_ordered--------- bool operator< (Digxel const& o) const { return value_ < NUM(o); } bool operator== (Digxel const& o) const { return value_ == NUM(o); } - bool operator== (NUM n) const { return value_ == n ; } - bool operator< (NUM n) const { return value_ < n ; } - bool operator> (NUM n) const { return value_ > n ; } +// bool operator== (NUM n) const { return value_ == n ; } +// bool operator< (NUM n) const { return value_ < n ; } +// bool operator> (NUM n) const { return value_ > n ; } - protected: - virtual int getIntValue() const =0; - virtual CBuf getFormatted() =0; }; diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp index 5a76553f3..16c94d21f 100644 --- a/tests/lib/time/digxel-test.cpp +++ b/tests/lib/time/digxel-test.cpp @@ -28,7 +28,7 @@ //#include //#include -#include +//#include #include //TODO #include @@ -49,9 +49,6 @@ namespace test{ namespace { // Test data - // Placeholder to mark the function argument in a "lambda-expression" - boost::lambda::placeholder1_type _1_; - const uint REPEAT = 40; const uint RAND_RANGE = 100; const uint RAND_DENOM = 3; @@ -65,6 +62,21 @@ namespace test{ return arbitrary; } + inline uint + isOdd (uint i) + { + return i % 2; + } + + double sum(0), + checksum(0); + + double + sideeffectSum (double val) + { + sum += val; + return val; + } /* === special Digxel configuration for this test === */ @@ -78,11 +90,12 @@ namespace test{ struct VerySpecialFormat + : digxel::PrintfFormatter { - + VerySpecialFormat() : digxel::PrintfFormatter("## %4.1f ##") { } }; - typedef digxel::Holder TestDigxel; + typedef Digxel TestDigxel; } @@ -114,11 +127,11 @@ namespace test{ { TestDigxel digi; CHECK (0 == digi); - CHECK ("## 0 ##" == digi.show()); + CHECK ("## 0 ##" == string(digi)); digi = 88; CHECK (88 == digi); - CHECK ("## 88.0 ##" == digi.show()); + CHECK ("## 88.0 ##" == string(digi)); } @@ -127,10 +140,9 @@ namespace test{ { TestDigxel digi; - uint sum(0), checksum(0); - // configure what the Digxel does on "mutation" - digi.mutator = ( var(sum) += _1_ ); + digi.mutator = sideeffectSum; + sum = checksum = 0; CHECK (0 == digi); for (uint i=0; i < REPEAT; ++i) @@ -176,7 +188,7 @@ namespace test{ d1 = someValue; CHECK (d1 == someValue); - TextDigxel d2(d1); + TestDigxel d2(d1); CHECK (d2 == someValue); CHECK (!isSameObject (d1, d2)); @@ -192,7 +204,7 @@ namespace test{ TestDigxel digi; digi = 123456789.12345678; - string formatted (digi.show()); // should trigger assertion + string formatted (digi); // should trigger assertion CHECK (formatted.length() <= digi.maxlen()); } @@ -207,8 +219,8 @@ namespace test{ start = clock(); for (uint i=0; i < TIMING_CNT; ++i) { - val odd = i % 2; digi = 1; + isOdd (i); } stop = clock(); uint without_reformatting = stop - start; @@ -217,8 +229,7 @@ namespace test{ start = clock(); for (uint i=0; i < TIMING_CNT; ++i) { - val odd = i % 2; - digi = odd; + digi = isOdd (i); } stop = clock(); uint with_reformatting = stop - start; From 159d3928d8fac1b06285ef0f3b3177253b157fa7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 5 Jan 2011 18:15:44 +0100 Subject: [PATCH 033/140] Timecode stub implementation... now passes Compiler and Linker again --- src/lib/time/lumitime.cpp | 5 +++ src/lib/time/quantiser.cpp | 19 ++++++++ src/lib/time/timecode.cpp | 90 ++++++++++++++++++++++++++++++++++++++ src/lib/time/timecode.hpp | 51 ++++++++++++--------- src/lib/time/timequant.hpp | 18 ++++++++ src/lib/time/timevalue.hpp | 4 +- 6 files changed, 166 insertions(+), 21 deletions(-) diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index c1837667b..d2daba3c2 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -94,6 +94,11 @@ namespace time { return string (lumiera_tmpbuf_print_time (t_)); } + TimeVar::operator string() const + { + return string (lumiera_tmpbuf_print_time (t_)); + } + }} // namespace lib::Time diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index c4e12b274..cf384822b 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -23,6 +23,7 @@ #include "lib/time/quantiser.hpp" #include "lib/time/timevalue.hpp" +#include "lib/time/timequant.hpp" using std::string; @@ -32,6 +33,24 @@ namespace time { /** */ + QuTime::QuTime (TimeValue raw, Symbol gridID) + : Time(raw) /////////////////////////////////////////////////TODO fetch quantiser + { } + + + /** */ + QuTime::QuTime (TimeValue raw, Quantiser const& quantisation_to_use) + : Time(raw) /////////////////////////////////////////////////TODO fetch quantiser + { } + + + + /** */ + FixedFrameQuantiser::FixedFrameQuantiser (FSecs frames_per_second) + : Quantiser() /////////////////////////////////////////////////TODO we ought to do something + { } + + diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 24c00de5b..3bb80e697 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -23,9 +23,15 @@ #include "lib/lumitime.hpp" #include "lib/time/timecode.hpp" +#include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" #include "lib/time/formats.hpp" +extern "C" { +#include "lib/time.h" +} + + using std::string; @@ -38,6 +44,90 @@ namespace time { /** */ + FrameNr::FrameNr (QuTime const& quantisedTime) + : FACTOR_TODO (1,25) + , nr_(TimeVar(quantisedTime) / (25*GAVL_TIME_SCALE)) + { } /////////////////////////////TODO temporary bullshit (of course that is the job of the quantiser) + + + /** */ + SmpteTC::SmpteTC (QuTime const& quantisedTime) + : tpoint_(quantisedTime) /////////////////////////////TODO eternal bullshit + { } + + + /** */ + HmsTC::HmsTC (QuTime const& quantisedTime) + : tpoint_(quantisedTime) /////////////////////////////TODO bullshit + { } + + + /** */ + Secs::Secs (QuTime const& quantisedTime) + : sec_(TimeVar(quantisedTime) / GAVL_TIME_SCALE) /////////////TODO bullshit + { } + + + + /** */ + int + SmpteTC::getSecs() const + { + return lumiera_time_seconds (tpoint_); + } + + /** */ + int + SmpteTC::getMins() const + { + return lumiera_time_minutes (tpoint_); + } + + /** */ + int + SmpteTC::getHours() const + { + return lumiera_time_hours (tpoint_); + } + + /** */ + int + SmpteTC::getFrames() const + { + UNIMPLEMENTED ("Frame-Quantisation"); + } + + /** */ + int + HmsTC::getSecs() const + { + return lumiera_time_seconds (tpoint_); + } + + /** */ + int + HmsTC::getMins() const + { + return lumiera_time_minutes (tpoint_); + } + + /** */ + int + HmsTC::getHours() const + { + return lumiera_time_hours (tpoint_); + } + + /** */ + double + HmsTC::getMillis() const + { + TODO ("Frame-Quantisation"); + return lumiera_time_millis (tpoint_); + } + + /** */ + diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 6cc526d79..05b5fb3ff 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -26,9 +26,11 @@ #include "lib/time/timevalue.hpp" #include "lib/time/formats.hpp" +#include "lib/symbol.hpp" //#include //#include +#include ///////////////TODO #include @@ -36,6 +38,8 @@ namespace lib { namespace time { using std::string; + using lib::Literal; + using boost::lexical_cast; /////////TODO /** @@ -50,13 +54,13 @@ namespace time { virtual ~TCode(); operator string() const { return show(); } - string describe() const { return tcID(); } - Time getTime() const { return Time(calc()); } + string describe() const { return string(tcID()); } + Time getTime() const { return Time(value()); } protected: virtual string show() const =0; - virtual string tcID() const =0; - virtual TimeValue calc() const =0; + virtual Literal tcID() const =0; + virtual TimeValue value() const =0; }; @@ -72,15 +76,19 @@ namespace time { class FrameNr : public TCode { - - virtual string show() const ; - virtual string tcID() const ; - virtual TimeValue calc() const ; + FSecs FACTOR_TODO; /////////////////////////////TODO temporary bullshit (of course that is the job of the quantiser) + + long nr_; + + + string show() const { return lexical_cast(nr_)+"fr"; } + Literal tcID() const { return "Frame-count"; } + TimeValue value() const { return Time(FACTOR_TODO * nr_); } public: - FrameNr (QuTime const& quantisedTime); + FrameNr (QuTime const& quantisedTime); - operator long() const; + operator long() const { return nr_; } }; @@ -91,10 +99,11 @@ namespace time { class SmpteTC : public TCode { - - virtual string show() const ; - virtual string tcID() const ; - virtual TimeValue calc() const ; + TimeVar tpoint_; + + virtual string show() const { return string(tpoint_); } + virtual Literal tcID() const { return "SMPTE"; } + virtual TimeValue value() const { return tpoint_; } public: SmpteTC (QuTime const& quantisedTime); @@ -113,10 +122,11 @@ namespace time { class HmsTC : public TCode { + TimeVar tpoint_; - virtual string show() const ; - virtual string tcID() const ; - virtual TimeValue calc() const ; + virtual string show() const { return string(tpoint_); } + virtual Literal tcID() const { return "Timecode"; } + virtual TimeValue value() const { return tpoint_; } public: HmsTC (QuTime const& quantisedTime); @@ -135,10 +145,11 @@ namespace time { class Secs : public TCode { + FSecs sec_; - virtual string show() const ; - virtual string tcID() const ; - virtual TimeValue calc() const ; + virtual string show() const { return string(Time(sec_)); } + virtual Literal tcID() const { return "Seconds"; } + virtual TimeValue value() const { return Time(sec_); } public: Secs (QuTime const& quantisedTime); diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp index 25cd9b40f..89c5db63f 100644 --- a/src/lib/time/timequant.hpp +++ b/src/lib/time/timequant.hpp @@ -62,5 +62,23 @@ namespace time { + /* == implementation == */ + template + bool + QuTime::supports() const + { + return false; ////////////////TODO; + } + + + template + typename format::Traits::TimeCode + QuTime::formatAs() const + { + typedef typename format::Traits::TimeCode TimeCode; + return TimeCode(*this); + } + + }} // lib::time #endif diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 674952ace..8fee6aca6 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -107,7 +107,7 @@ namespace time { > > { public: - TimeVar (TimeValue time = TimeValue()) + TimeVar (TimeValue const& time = TimeValue()) : TimeValue(time) { } @@ -123,6 +123,8 @@ namespace time { return *this; } + /** @internal diagnostics */ + operator std::string () const; // Supporting mixing with plain long int arithmetics operator gavl_time_t () const { return t_; } From 8e90b3d5dcb3e20c72fd25d32b8eaa1530a81b7d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 6 Jan 2011 01:35:22 +0100 Subject: [PATCH 034/140] Digxel unit test pass --- src/lib/time/digxel.hpp | 9 +-- src/lib/time/display.hpp | 5 ++ tests/lib/time/digxel-test.cpp | 116 ++++++++++++++++++++++++--------- 3 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 2d99aa7fd..0d757be29 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -124,7 +124,7 @@ namespace time { } void clear() { printbuffer_[0] = '\0'; } - bool empty() { return bool(*printbuffer_); } + bool empty() { return ! bool(*printbuffer_); } size_t maxlen() const @@ -139,8 +139,8 @@ namespace time { { size_t space = std::snprintf (printbuffer_, bufsiz, formatSpec_, val); REQUIRE (space < bufsiz, "Digxel value exceeded available buffer size. " - "For showing %s, %lu chars instead of just %d would be required." - , cStr(lexical_cast(val)), space, bufsiz); ///////////TICKET #197 + "For showing %s, %lu+1 chars instead of just %lu+1 would be required." + , cStr(lexical_cast(val)), space, len); ///////////TICKET #197 } ENSURE (!empty()); return printbuffer_; @@ -188,9 +188,6 @@ namespace time { > class Digxel : public boost::totally_ordered > -// , -// boost::totally_ordered, NUM -// > > { mutable FMT buffer_; diff --git a/src/lib/time/display.hpp b/src/lib/time/display.hpp index 27c202da5..2fa093767 100644 --- a/src/lib/time/display.hpp +++ b/src/lib/time/display.hpp @@ -25,6 +25,7 @@ #define LIB_TIME_DISPLAY_H #include "lib/time/timecode.hpp" +#include "lib/time/digxel.hpp" #include #include @@ -41,6 +42,10 @@ namespace time { inline std::ostream& operator<< (std::ostream& os, Time const& t) { return os << string(t); } inline std::ostream& operator<< (std::ostream& os, TCode const& t) { return os << string(t); } + /** display a single timecode component */ + template< typename NUM, class FMT> + inline std::ostream& operator<< (std::ostream& os, Digxel const& d) { return os << string(d); } + }} // lib::time diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp index 16c94d21f..620cd13c3 100644 --- a/tests/lib/time/digxel-test.cpp +++ b/tests/lib/time/digxel-test.cpp @@ -23,36 +23,29 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" +#include "lib/time/display.hpp" #include "lib/time/digxel.hpp" #include "lib/util.hpp" -//#include -//#include -//#include -#include //TODO +#include #include -//using boost::lexical_cast; -//using util::isnil; -//using util::contains; using util::isSameObject; using std::rand; -using std::cout;/////////TODO +using std::cout; using std::endl; -//using boost::algorithm::join; - namespace lib { namespace time{ namespace test{ - namespace { // Test data + namespace { // Test data and setup - const uint REPEAT = 40; + const uint REPEAT = 40; const uint RAND_RANGE = 100; const uint RAND_DENOM = 3; - const uint TIMING_CNT = 10000; + const uint TIMING_CNT = 10000000; inline double randomFrac() @@ -68,9 +61,10 @@ namespace test{ return i % 2; } + double sum(0), checksum(0); - + double sideeffectSum (double val) { @@ -78,6 +72,7 @@ namespace test{ return val; } + /* === special Digxel configuration for this test === */ double @@ -92,12 +87,17 @@ namespace test{ struct VerySpecialFormat : digxel::PrintfFormatter { - VerySpecialFormat() : digxel::PrintfFormatter("## %4.1f ##") { } + VerySpecialFormat() : digxel::PrintfFormatter("##%+5.1f ##") { } }; typedef Digxel TestDigxel; - - } + + }//(End)Test setup + + + + + /*********************************************************************** @@ -106,7 +106,8 @@ namespace test{ * - build a Digxel * - set a value * - retrieve formatted display - * - assign to invoke the setter-functor + * - performing side-effects from the setter-functor + * - formatted value caching */ class Digxel_test : public Test { @@ -116,6 +117,7 @@ namespace test{ checkSimpleUsage (); checkMutation (); verifyMutatorInfluence (); + verifyComparisons (); checkCopy (); checkDisplayOverrun (); verifyDisplayCaching (); @@ -127,11 +129,13 @@ namespace test{ { TestDigxel digi; CHECK (0 == digi); - CHECK ("## 0 ##" == string(digi)); + CHECK ("## +0.0 ##" == string(digi)); + cout << "empty____" << digi << endl; - digi = 88; - CHECK (88 == digi); - CHECK ("## 88.0 ##" == string(digi)); + digi = -88.77; + CHECK (-88.77 == digi); + CHECK ("##-88.8 ##" == string(digi)); + cout << "value____" << digi << endl; } @@ -142,19 +146,20 @@ namespace test{ // configure what the Digxel does on "mutation" digi.mutator = sideeffectSum; - sum = checksum = 0; CHECK (0 == digi); + sum = checksum = 0; for (uint i=0; i < REPEAT; ++i) { double arbitrary = randomFrac(); - checksum += arbitrary; - - digi = arbitrary; // invoke the mutation functor + checksum += arbitrary; // for verification + // + digi = arbitrary; //...causes invocation of mutation functor CHECK (sum == checksum, "divergence after adding %f in iteration %d", arbitrary, i); CHECK (digi == arbitrary); } + CHECK (0 < sum); } @@ -168,16 +173,49 @@ namespace test{ digi = 12.3; CHECK (12.3 == digi); + // a special mutator to limit the value digi.mutator = limitingMutator; CHECK (12.3 == digi); digi = 12.3; CHECK (1 == digi); - digi.setValueRaw(12.3); + digi = 0.5; + CHECK (0.5 == digi); + digi = -0.678; + CHECK (-0.678 == digi); + digi = -9.1011; + CHECK (-1 == digi); + + digi.setValueRaw(12.3); // bypassing mutator CHECK (12.3 == digi); } + void + verifyComparisons () + { + TestDigxel d1; + TestDigxel d2; + + CHECK (d1 == d2); + + double someValue = randomFrac(); + d1 = someValue; + + CHECK (d1 == someValue); + CHECK (d1 != d2); + CHECK (d2 != d1); + + d2 = d1 + 22; + CHECK (d1 < d2); + CHECK (d1 <= d2); + + CHECK (!(d1 > d2)); + CHECK (!(d1 >= d2)); + CHECK (!(d1 == d2)); + } + + void checkCopy () { @@ -198,24 +236,38 @@ namespace test{ } + /** @test Digxel should be protected + * against display buffer overrun */ void checkDisplayOverrun () { TestDigxel digi; digi = 123456789.12345678; - string formatted (digi); // should trigger assertion + string formatted; +#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT + VERIFY_ERROR (ASSERTION, formatted = digi.show() ); // should trigger assertion + formatted = digi.show(); // second time doesn't reformat +#endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT + CHECK (formatted.length() <= digi.maxlen()); } + /** @test verify caching of formatted values. + * Digxel avoids reformatting unchanged values; + * to verify the effectivity of this measure, we + * take some timings. + * @warning the results of such tests could be unreliable, + * but in this case here I saw a significant difference, + * with values of 0.5sec / 0.8sec */ void verifyDisplayCaching () { TestDigxel digi; digi = 1; - clock_t start, stop; + clock_t start(0), stop(0); start = clock(); for (uint i=0; i < TIMING_CNT; ++i) { @@ -234,8 +286,10 @@ namespace test{ stop = clock(); uint with_reformatting = stop - start; - cout << "without = "<< without_reformatting << endl; - cout << "with = "<< with_reformatting << endl; + cout << "without reformatting = "<< double(without_reformatting)/CLOCKS_PER_SEC <<"sec"<< endl; + cout << "with reformatting = "<< double(with_reformatting )/CLOCKS_PER_SEC <<"sec"<< endl; + + CHECK (without_reformatting < with_reformatting); } }; From c85f7e0715dc5c4968c3fc73c5bd4cb2d8b6dfa1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 6 Jan 2011 03:45:29 +0100 Subject: [PATCH 035/140] Add more special formatters and Digxel testcases The Digxel implementation draft can be considered complete now --- src/lib/time/digxel.cpp | 38 ------ src/lib/time/digxel.hpp | 108 ++++++++++-------- src/lib/time/quantiser.hpp | 24 ++++ tests/40components.tests | 20 ++++ tests/lib/time/digxel-configurations-test.cpp | 105 +++++++++++++++++ tests/lib/time/digxel-test.cpp | 3 +- 6 files changed, 212 insertions(+), 86 deletions(-) delete mode 100644 src/lib/time/digxel.cpp create mode 100644 tests/lib/time/digxel-configurations-test.cpp diff --git a/src/lib/time/digxel.cpp b/src/lib/time/digxel.cpp deleted file mode 100644 index b841acf0e..000000000 --- a/src/lib/time/digxel.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - Timecode - implementation of fixed grid aligned time specifications - - Copyright (C) Lumiera.org - 2010, Hermann Vosseler - - 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 "lib/time/digxel.hpp" - - -namespace lib { -namespace time { - - - - - /** */ - - - -}} // lib::time - diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 0d757be29..ac5f5f8f8 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -35,21 +35,23 @@ ** \par properties of a "Digxel" ** ** Semantically, it's a number or number component. It holds an internal numeric representation - ** and is implicitly convertible both to integrals and floating point numbers. This implicit - ** conversion is a compromise to support generic processing. + ** and is implicitly convertible back to the underlying numeric type (usually int or double). ** ** But at the same time, a Digxel has a definite textual format and the ability to present ** its numeric value formatted accordingly. To this end, the contract \em requires that ** numeric data pushed to the Digxel be kept within such limits to prevent exceeding the ** embedded formatting buffer. There is an assertion in debug mode, and a range check, - ** but the result will be just truncated, so this is clearly the caller's responsibility. - ** Digxel might be considered an implementation support class, and performance is important - ** to some limited degree; especially, formatted values will be cached. + ** but the result will be just truncated, so passing only sane values is clearly the + ** caller's responsibility. Digxel might be considered an implementation support class, + ** and performance is important to some limited degree; + ** especially, formatted values will be \em cached. ** - ** To support in-place modification, the digxel stores a mutation signal (functor) and exposes - ** a special \c mutate(newVal) function, which invokes this stored functor, if defined. Usually - ** this should invoke some internal recalculations, resulting in a new value being pushed to - ** the Digxel for display. + ** To support in-place modification, the digxel stores a mutation signal (functor). This + ** functor will be invoked, whenever a new value gets assigned. The actual functor is free + ** to cause side effects; the value returned from this functor will be the new value to set. + ** If not configured, the default implementation just accepts the given value unaltered. Usually + ** this mutation functor should invoke some internal recalculations, maybe resulting in a new + ** value being pushed to the Digxel for display. ** ** \par configuration ** the Digxel template can be configured to some degree to adjust the stored numeric data @@ -69,16 +71,15 @@ #include #include #include -#include ///////////TODO -#include #include +#include + +using std::string; namespace lib { namespace time { - using std::string; - namespace digxel { using util::cStr; @@ -86,27 +87,13 @@ namespace time { using boost::lexical_cast; typedef const char* CBuf; - - - template - struct ValTrait; - - template<> - struct ValTrait - { - static int asInt (int val) { return val; } - static double asDouble (int val) { return val; } - }; - - template<> - struct ValTrait - { - static int asInt (double val) { return std::floor(0.5+val); } ///< in accordance with Lumiera's time handling RfC - static double asDouble (double val) { return val; } - }; - + /** + * Default / base implementation for Digxel formatting. + * This formatter holds an inline buffer of limited size, + * receiving and caching the textual representation + */ template class PrintfFormatter { @@ -124,7 +111,7 @@ namespace time { } void clear() { printbuffer_[0] = '\0'; } - bool empty() { return ! bool(*printbuffer_); } + bool empty() { return '\0' == *printbuffer_; } size_t maxlen() const @@ -147,6 +134,11 @@ namespace time { } }; + + /** + * default configured Formatter implementations + * for some of the basic numeric types + */ template struct Formatter; @@ -154,8 +146,7 @@ namespace time { struct Formatter : PrintfFormatter { - Formatter() : PrintfFormatter("%5d") { } - + Formatter() : PrintfFormatter("%3d") { } }; template<> @@ -163,25 +154,46 @@ namespace time { : PrintfFormatter { Formatter() : PrintfFormatter("%06.3f") { } - }; + /* == other specialised Formatters == */ + struct SexaFormatter + : PrintfFormatter + { + SexaFormatter() : PrintfFormatter("%02d") { } + }; + + struct HexaFormatter + : PrintfFormatter + { + HexaFormatter() : PrintfFormatter("%02X") { } + }; + + } //(End) digxel configuration namespace + + + /** * A number element for building structured numeric displays. * The purpose is to represent parts of a numeric format, like - * e.g. the sexagesimal "digits" of a timecode display. Digxel + * e.g. the sexagesimal "digits" of a timecode display. A Digxel * - is customised by template parameters to a specific number format - * - requires that any number set must not overflow the format buffer + * - requires that any given number must not overflow the format buffer * - can receive new numbers by assignment + * - stores and these given value numerically * - will then format these numbers and cache the formatted representation - * - can store and invoke a mutation functor - * + * - can store and invoke a mutation functor to pre-process values on setting + * * @note comparisons are assumed to be not performance relevant + * @param NUM numeric type to be used for the value + * @param FMT a formatter and buffer holder type + * @see digxel::Formatter default printf based formatter * @see lib::time::TCode - * @todo WIP-WIP-WIP + * @see Digxel_test + * */ template< typename NUM , class FMT = digxel::Formatter @@ -208,7 +220,7 @@ namespace time { Digxel () : buffer_() - , value_() + , value_ () , mutator(use_newValue_as_is) { } @@ -219,6 +231,7 @@ namespace time { size_t maxlen() const { return buffer_.maxlen(); } + digxel::CBuf show() const { @@ -229,7 +242,7 @@ namespace time { void operator= (NUM n) { - NUM changedValue = mutator(n); + NUM changedValue = mutator(n); this->setValueRaw (changedValue); } @@ -248,13 +261,14 @@ namespace time { //---Supporting-totally_ordered--------- bool operator< (Digxel const& o) const { return value_ < NUM(o); } bool operator== (Digxel const& o) const { return value_ == NUM(o); } -// bool operator== (NUM n) const { return value_ == n ; } -// bool operator< (NUM n) const { return value_ < n ; } -// bool operator> (NUM n) const { return value_ > n ; } - }; + /* == predefined Digxel configurations == */ + typedef Digxel< int, digxel::SexaFormatter> SexaDigit; ///< for displaying time components (sexagesimal) + typedef Digxel HexaDigit; ///< for displaying a hex byte + + }} // lib::time #endif diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 61b9e99d0..84881141a 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -32,6 +32,7 @@ //#include #include #include +#include namespace lib { @@ -39,6 +40,29 @@ namespace time { LUMIERA_ERROR_DECLARE (UNKNOWN_GRID); ///< referring to an undefined grid or scale in value quantisation + + namespace { // stashed here for later + + template + struct ValTrait; + + template<> + struct ValTrait + { + static int asInt (int val) { return val; } + static double asDouble (int val) { return val; } + }; + + template<> + struct ValTrait + { + static int asInt (double val) { return std::floor(0.5+val); } ///< in accordance with Lumiera's time handling RfC + static double asDouble (double val) { return val; } + }; + + } + + /** * Facility to create grid-aligned time values. * diff --git a/tests/40components.tests b/tests/40components.tests index b61a98aed..51b98cfb6 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -231,6 +231,26 @@ return: 0 END +TEST "A Digxel (numeric component)" Digxel_test < : Yes out: HasNested_Core : No diff --git a/tests/lib/time/digxel-configurations-test.cpp b/tests/lib/time/digxel-configurations-test.cpp new file mode 100644 index 000000000..185fd06a7 --- /dev/null +++ b/tests/lib/time/digxel-configurations-test.cpp @@ -0,0 +1,105 @@ +/* + DigxelConfigurations(Test) - verify predefined standard Digxel configurations + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/time/display.hpp" +#include "lib/time/digxel.hpp" +#include "lib/error.hpp" +#include "lib/util.hpp" + +#include +#include + +using lumiera::error::LUMIERA_ERROR_ASSERTION; +using util::isSameObject; +using lib::test::showType; +using std::rand; +using std::cout; +using std::endl; + + +namespace lib { +namespace time{ +namespace test{ + + + + + + + /*********************************************************************** + * @test verify correctness of the predefined standard Digxels. + * Some widely used standard configurations, including + * - default Digxel for int and double values + * - sexagesimal Digxel + * - hex byte Digxel + * - ...more to come + * @todo cover any newly added Digxel configurations. + */ + class DigxelConfigurations_test : public Test + { + virtual void + run (Arg) + { + verifyConfiguration > (123); + verifyConfiguration > (123.4567); + verifyConfiguration (42); + verifyConfiguration (-5); + verifyConfiguration (0xc); + verifyConfiguration (0x6f); + } + + + template + void + verifyConfiguration (VAL testval) + { + DIX digxel; + CHECK (0 == digxel); + cout << showType(digxel) << "--empty--"< #include +using lumiera::error::LUMIERA_ERROR_ASSERTION; using util::isSameObject; using std::rand; using std::cout; @@ -247,7 +248,7 @@ namespace test{ string formatted; #if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT VERIFY_ERROR (ASSERTION, formatted = digi.show() ); // should trigger assertion - formatted = digi.show(); // second time doesn't reformat + formatted = digi.show(); // second time doesn't reformat #endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT CHECK (formatted.length() <= digi.maxlen()); From 84c73c645dce48c3e5c647e1edc389d82a02890d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 6 Jan 2011 04:21:18 +0100 Subject: [PATCH 036/140] draft unit-test to cover basic quantiser behaviour --- src/lib/time/quantiser.cpp | 12 ++- src/lib/time/quantiser.hpp | 9 ++ tests/40components.tests | 5 ++ tests/lib/time/quantiser-basics-test.cpp | 101 +++++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 tests/lib/time/quantiser-basics-test.cpp diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index cf384822b..10b5cc4e2 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -32,6 +32,9 @@ namespace lib { namespace time { + Quantiser::~Quantiser() { } // hint to emit the VTable here... + + /** */ QuTime::QuTime (TimeValue raw, Symbol gridID) : Time(raw) /////////////////////////////////////////////////TODO fetch quantiser @@ -49,10 +52,17 @@ namespace time { FixedFrameQuantiser::FixedFrameQuantiser (FSecs frames_per_second) : Quantiser() /////////////////////////////////////////////////TODO we ought to do something { } - + /** */ + Time + FixedFrameQuantiser::align (TimeValue const& raw) + { + UNIMPLEMENTED ("simple demo quantisation to hard wired grid"); + } + + LUMIERA_ERROR_DEFINE (UNKNOWN_GRID, "referring to an undefined grid or scale in value quantisation"); diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 84881141a..26ec82544 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -75,12 +75,17 @@ namespace time { typedef lib::PtrDerefIter<_SrcIter> _Iter; public: + virtual ~Quantiser(); ///< this is an ABC + + template bool supports() const; typedef _Iter iterator; iterator getSupportedFormats() const; + virtual Time align (TimeValue const& raw) =0; + }; @@ -98,8 +103,12 @@ namespace time { : public Quantiser { + public: FixedFrameQuantiser (FSecs frames_per_second); + + + Time align (TimeValue const& raw); }; diff --git a/tests/40components.tests b/tests/40components.tests index 51b98cfb6..0055ce202 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -663,6 +663,11 @@ return: 0 END +PLANNED "Quantiser API basics" QuantiserBasics_test < + + 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 "lib/test/run.hpp" +//#include "lib/test/test-helper.hpp" +//#include "lib/time/timequant.hpp" +#include "lib/time/quantiser.hpp" +//#include "lib/time/display.hpp" +#include "lib/util.hpp" + +#include +//#include +#include +//#include + +using boost::lexical_cast; +using util::isnil; +//using util::contains; +//using std::rand; +using std::cout; +using std::endl; + +//using boost::algorithm::join; + + +namespace lib { +namespace time{ +namespace test{ + + namespace { + + const uint MAX_FRAMES = 25*500; + const uint MAX_DIRT = 50; + + const FSecs F25(1,25); + + } + + + + /******************************************************** + * @test cover the basic Quantiser API. + * This test uses a special quantiser implementation + * with hard coded behaviour to demonstrate and verify + * the usage of a quantiser entity in isolation. + */ + class QuantiserBasics_test : public Test + { + + virtual void + run (Arg) + { + checkSimpleQuantisation (); + } + + + void + checkSimpleQuantisation () + { + FixedFrameQuantiser fixQ(25); + + uint frames = (rand() % MAX_FRAMES); + FSecs dirt = (F25 / (rand() % MAX_DIRT)); + + Time rawTime = FSecs(frames, 25) + dirt; + CHECK (Time( frames*F25) <= rawTime); + CHECK (Time((frames+1)*F25) > rawTime); + + Time quantTime = fixQ.align (rawTime); + + CHECK (Time(frames*F25) == quantTime); + } + }; + + + /** Register this test class... */ + LAUNCHER (QuantiserBasics_test, "unit common"); + + + +}}} // namespace lib::time::test From dede87d384c589f8eb64aab70b10d75b8d291e96 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 6 Jan 2011 13:30:27 +0100 Subject: [PATCH 037/140] implement basic quantisation operation for timehandling library --- src/lib/time.c | 44 ++++++++++++++++++++++++++++++++++++++++++ src/lib/time.h | 24 +++++++++++++++++++++++ wiki/renderengine.html | 26 ++++++++++++++++++++----- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index 5af40d640..17831a77a 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -23,6 +23,10 @@ #include "lib/time.h" #include "lib/tmpbuf.h" +#include +#include + + /* GAVL_TIME_SCALE is the correct factor or dividend when using gavl_time_t for * units of whole seconds from gavl_time_t. Since we want to use milliseconds, * we need to multiply or divide by 1000 to get correct results. */ @@ -57,6 +61,46 @@ lumiera_tmpbuf_print_time (gavl_time_t time) return buffer; } + +static double +calculate_quantisation (gavl_time_t time, double grid, gavl_time_t origin) +{ + double val = time; + val -= origin; + val /= grid; + return floor (val); +} + +static double +clip_to_64bit (double val) +{ + if (val > LLONG_MAX) + val = LLONG_MAX; + else + if (val < LLONG_MIN) + val = LLONG_MIN; + + return val; +} + + +int64_t +lumiera_quantise_frames (gavl_time_t time, double grid, gavl_time_t origin) +{ + double gridNr = calculate_quantisation (time, grid, origin); + gridNr = clip_to_64bit (gridNr); + return (int64_t) gridNr; +} + +gavl_time_t +lumiera_quantise_time (gavl_time_t time, double grid, gavl_time_t origin) +{ + double count = calculate_quantisation (time, grid, origin); + double alignedTime = clip_to_64bit (count * grid); + return (gavl_time_t) alignedTime; +} + + gavl_time_t lumiera_build_time(long millis, uint secs, uint mins, uint hours) { diff --git a/src/lib/time.h b/src/lib/time.h index f812c1b9d..eabc4d5f8 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -34,6 +34,30 @@ char* lumiera_tmpbuf_print_time (gavl_time_t time); +/** + * Quantise the given time into a fixed grid, relative to the origin. + * The time grid used for quantisation is comprised of equally spaced intervals, + * rooted at the given origin. The interval starting with the origin is numbered + * as zero. Each interval includes its lower bound, but excludes its upper bound. + * @param grid spacing of the grid intervals, measured in GAVL_TIME_SCALE + * @return number of the grid interval containing the given time. + * @warning the resulting value is limited such as to fit into a 64bit long + */ +int64_t +lumiera_quantise_frames (gavl_time_t time, double grid, gavl_time_t origin); + +/** + * Similar to #lumiera_quantise_frames, but returns a grid aligned \em time value + * @return time of start of the grid interval containing the given time, + * but measured relative to the origin + * @warning because the resulting value needs to be limited to fit into a 64bit long, + * the addressable time range can be considerably reduced. For example, if + * origin = LLONG_MIN, then all original time values above zero will be + * clipped, because the result, relative to origin, needs to be <= LLONG_MAX + */ +gavl_time_t +lumiera_quantise_time (gavl_time_t time, double grid, gavl_time_t origin); + /** * Builds a time value by summing up the given components. */ diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 63faf288a..27ecbe2da 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -4121,11 +4121,11 @@ Besides, they provide an __inward interface__ for the [[ProcNode]]s, enabling th
-
+
The Render Engine is the part of the application doing the actual video calculations. Utilising system level services and retrieving raw audio and video data through [[Lumiera's Backend|backend.html]], its operations are guided by the objects and parameters edited by the user in [[the session|Session]]. The middle layer of the Lumiera architecture, known as the Proc-Layer, spans the area between these two exteremes, providing the the (abstract) edit operations available to the user, the representation of [["editable things"|MObjects]] and the translation of those into structures and facilities allowing to [[drive the rendering|Rendering]].
 
 !About this wiki page
-|background-color:#E3F3F1;width:96ex;padding:2ex; This TiddlyWiki is the central location for design, planning and documentation of the Lumiera Proc-Layer. Some parts are used as //extended brain// &mdash; collecting ideas, considerations and conclusions &mdash; while other tiddlers contain the decisions and document the planned or implemented facilities. The intention is to move over the more mature parts into the emerging technical documentation section on the [[Lumiera website|http://www.lumiera.org]] eventually. <br/><br/>Besides cross-references, content is largely organised through [[Tags|TabTags]], most notably <br/><<tag overview>> &middot; <<tag def>> &middot; <<tag decision>> &middot; <<tag Concepts>> <br/> <<tag Model>> &middot; <<tag SessionLogic>> &middot; <<tag GuiIntegration>> &middot; <<tag Builder>> &middot; <<tag Rendering>> &middot; <<tag Player>> &middot; <<tag Rules>> |
+|background-color:#E3F3F1;width:96ex;padding:2ex; This TiddlyWiki is the central location for design, planning and documentation of the Lumiera Proc-Layer. Some parts are used as //extended brain// &mdash; collecting ideas, considerations and conclusions &mdash; while other tiddlers contain the decisions and document the planned or implemented facilities. The intention is to move over the more mature parts into the emerging technical documentation section on the [[Lumiera website|http://www.lumiera.org]] eventually. <br/><br/>Besides cross-references, content is largely organised through [[Tags|TabTags]], most notably <br/><<tag overview>> &middot; <<tag def>> &middot; <<tag decision>> &middot; <<tag Concepts>> <br/> <<tag Model>> &middot; <<tag SessionLogic>> &middot; <<tag GuiIntegration>> &middot; <<tag Builder>> &middot; <<tag Rendering>> &middot; <<tag Player>> &middot; <<tag Rules>> &middot; <<tag Types>> |
 
 !~Proc-Layer Summary
 When editing, the user operates several kinds of //things,// organized as [[assets|Asset]] in the AssetManager, like media, clips, effects, codecs, configuration templates. Within the context of the [[Project or Session|Session]], we can use these as &raquo;[[Media Objects|MObjects]]&laquo; &mdash; especially, we can [[place|Placement]] them in various kinds within the session and relative to one another.
@@ -4192,6 +4192,19 @@ Viewed as a micro program, the processing patterns are ''weak typed'' &mdash
 
a given Render Engine configuration is a list of Processors. Each Processor in turn contains a Graph of ProcNode.s to do the acutal data processing. In order to cary out any calculations, the Processor needs to be called with a StateProxy containing the state information for this RenderProcess
 
+
+
The Quantiser implementation works by determining the grid interval containing a given raw time.
+
+!frame quantisation convention
+Within Lumiera, there is a fixed convention how these frame intervals are to be defined (&rArr; time handling RfC)
+[img[Lumiera's frame quantisation convention|draw/FramePositions1.png]]
+
+!range limitation problems
+because times are represented as 64bit integers, the time points addressable within a given scale grid can be limited, compared with time points addressable through raw (internal) time values. As an extreme example, consider a time scale with origin at Time::MIN -- such a scale is unable to represent any value above zero, because the resulting coordinates would exceed the range of the 64bit integer.
+
+To avoid problems with larger intermediate values, the actual calculations are performed with doubles, which are clipped to the allowed value range prior to casting back to the integral data type. In all practically relevant cases, there is no danger of imprecisions or rounding errors, because the quantisation includes a floor operation. (Hypothetically, an imprecision could arise through extinction, when calculating the offset from the origin; but in practice this is irrelevant, assumed that the conversion 64bit integer to double yields reproducible double values)
+
+
{{red{WIP as of 10/09}}}...//brainstorming about the first ideas towards a query subsystem//
 
@@ -4689,9 +4702,10 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin
 
A facility allowing the Proc-Layer to work with abstracted [[media stream types|StreamType]], linking (abstract or opaque) [[type tags|StreamTypeDescriptor]] to an [[library|MediaImplLib]], which provides functionality for acutally dealing with data of this media stream type. Thus, the stream type manager is a kind of registry of all the external libraries which can be bridged and accessed by Lumiera (for working with media data, that is). The most basic set of libraries is instelled here automatically at application start, most notably the [[GAVL]] library for working with uncompressed video and audio data. //Later on, when plugins will introduce further external libraries, these need to be registered here too.//
-
+
A scale grid controls the way of measuring and aligining a quantity the application has to deal with. The most prominent example is the way to handle time in fixed atomic chunks (''frames'') addressed through a fixed format (''timecode''): while internally the application uses time values of sufficiently fine grained resolution, the acutally visible timing coordinates of objects within the session are ''quantised'' to some predefined and fixed time grid.
-
+ +&rarr; QuantiserImpl
A link to relate a compound of [[nested placement scopes|PlacementScope]] to the //current// session and the //current//&nbsp; [[focus for querying|QueryFocus]] and exploring the structure. ScopeLocator is a singleton service, allowing to ''explore'' a [[Placement]] as a scope, i.e. discover any other placements within this scope, and allowing to locate the position of this scope by navigating up the ScopePath finally to reach the root scope of the HighLevelModel.
@@ -6561,7 +6575,7 @@ Thus no server and no network connection is needed. Simply open the file in your
  * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
 
-
+
The term &raquo;Time&laquo; spans a variety of vastly different entities. Within a NLE we get to deal with various //flavours of time values.//
 ;continuous time
 :without any additional assumptions, ''points in time'' can be specified with arbitrary precision.
@@ -6619,6 +6633,8 @@ For Lumiera, the static typing approach is of limited value -- it excels when va
 
 At the level of individual timecode formats, we're lacking a common denominator; thus it is preferrable to work with different concrete timecode classes through //generic programming.// This way, each timecode format can expose operations specific only to the given format. Especially, different timecode formats expose different //component fields,// modelled by the generic ''Digxel'' concept. There is a common baseclass ~TCode though, which can be used for //type erasure.//
 &rarr; more on [[usage situations|TimeUsage]]
+&rarr; Quantiser [[implementation details|QuantiserImpl]]
+
 
From b66b778c42d6a0f973b49c9805cdb36340747326 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 6 Jan 2011 13:31:13 +0100 Subject: [PATCH 038/140] implement and test simple demo quantiser --- src/lib/time/quantiser.cpp | 58 ++++++++++++++++++++---- src/lib/time/quantiser.hpp | 9 ++-- src/lib/time/timevalue.hpp | 4 +- src/proc/asset/meta/time-grid.hpp | 4 +- tests/lib/time/quantiser-basics-test.cpp | 2 +- 5 files changed, 60 insertions(+), 17 deletions(-) diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 10b5cc4e2..c1608184f 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -25,16 +25,41 @@ #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" +extern "C" { +#include "lib/time.h" +} + +#include + +using boost::rational_cast; using std::string; +namespace error = lumiera::error; + namespace lib { namespace time { + namespace { // implementation helpers... + + template + inline NUM + __ensure_notZero (NUM n) + { + if (!n) + throw error::Logic ("Impossible to quantise to an zero spaced grid" + , error::LUMIERA_ERROR_BOTTOM_VALUE); + return n; + } + + }//(End) implementation helpers + + + Quantiser::~Quantiser() { } // hint to emit the VTable here... - + /** */ QuTime::QuTime (TimeValue raw, Symbol gridID) : Time(raw) /////////////////////////////////////////////////TODO fetch quantiser @@ -48,18 +73,33 @@ namespace time { - /** */ - FixedFrameQuantiser::FixedFrameQuantiser (FSecs frames_per_second) - : Quantiser() /////////////////////////////////////////////////TODO we ought to do something + /** Create a quantiser based on a fixed constant spaced grid, rooted at the reference point + * as origin of the scale. Quantisation then means to determine the grid interval containing + * a given raw time value. Here, the grid interval number zero \em starts at the origin; + * each interval includes its lower bound and excludes its upper bound.*/ + FixedFrameQuantiser::FixedFrameQuantiser (FSecs frames_per_second, TimeValue referencePoint) + : origin_(referencePoint) + , raster_(rational_cast (GAVL_TIME_SCALE / __ensure_notZero(frames_per_second))) { } - /** */ - Time - FixedFrameQuantiser::align (TimeValue const& raw) + /** alignment to a simple fixed size grid. + * The actual calculation first determines the number + * of the grid interval containing the given rawTime, + * then followed by multiplying this interval number + * with the grid spacing. + * @return time of the start point of the grid interval + * containing the rawTime, relative to the origin + * of the time scale used by this quantiser. + * @warning returned time values are limited by the + * range of an 64bit integer + * @see #lumiera_quantise_time + */ + TimeValue + FixedFrameQuantiser::gridAlign (TimeValue const& rawTime) { - UNIMPLEMENTED ("simple demo quantisation to hard wired grid"); + return TimeValue (lumiera_quantise_time (_raw(rawTime), raster_, _raw(origin_))); } @@ -69,4 +109,4 @@ namespace time { }} // lib::time - + diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 26ec82544..83467d7f0 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -84,7 +84,7 @@ namespace time { typedef _Iter iterator; iterator getSupportedFormats() const; - virtual Time align (TimeValue const& raw) =0; + virtual TimeValue gridAlign (TimeValue const& raw) =0; }; @@ -102,13 +102,14 @@ namespace time { class FixedFrameQuantiser : public Quantiser { - + Time origin_; + double raster_; public: - FixedFrameQuantiser (FSecs frames_per_second); + FixedFrameQuantiser (FSecs frames_per_second, TimeValue referencePoint =TimeValue(0)); - Time align (TimeValue const& raw); + TimeValue gridAlign (TimeValue const&); }; diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 8fee6aca6..396711719 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -81,6 +81,9 @@ namespace time { : t_(o.t_) { } + /** @internal to pass Time values to C functions */ + friend gavl_time_t _raw (TimeValue const& time) { return time.t_; } + // Supporting totally_ordered friend bool operator< (TimeValue const& t1, TimeValue const& t2) { return t1.t_ < t2.t_; } friend bool operator< (TimeValue const& t1, gavl_time_t t2) { return t1.t_ < t2 ; } @@ -236,7 +239,6 @@ namespace time { /** @internal diagnostics */ operator std::string () const; - }; diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 1e449a372..1020b016e 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -94,8 +94,8 @@ namespace meta { template<> struct Builder { - FSecs fps_; - Time origin_; + FSecs fps_; + Time origin_; /** when building a compound or variable grid, * the predecessor is the grid active \em before diff --git a/tests/lib/time/quantiser-basics-test.cpp b/tests/lib/time/quantiser-basics-test.cpp index 0b4b6515e..9c12295ab 100644 --- a/tests/lib/time/quantiser-basics-test.cpp +++ b/tests/lib/time/quantiser-basics-test.cpp @@ -86,7 +86,7 @@ namespace test{ CHECK (Time( frames*F25) <= rawTime); CHECK (Time((frames+1)*F25) > rawTime); - Time quantTime = fixQ.align (rawTime); + Time quantTime (fixQ.gridAlign (rawTime)); CHECK (Time(frames*F25) == quantTime); } From 48b3f39c49a145aab63c3f9aaa8d03362d96e4b0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 7 Jan 2011 16:28:13 +0100 Subject: [PATCH 039/140] improve and cover Time convenience handling shortcuts --- src/lib/time.c | 2 +- src/lib/time/timevalue.hpp | 4 +++ tests/lib/time/time-value-test.cpp | 48 ++++++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index 17831a77a..f81a2cfc7 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -54,7 +54,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time) time /= 60; hours = time; - char *buffer = lumiera_tmpbuf_snprintf(64, "%s%02d:%02d:%02d.%03d", + char *buffer = lumiera_tmpbuf_snprintf(64, "%s%01d:%02d:%02d.%03d", negative ? "-" : "", hours, minutes, seconds, milliseconds); ENSURE(buffer != NULL); diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 396711719..08ed06f02 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -239,6 +239,10 @@ namespace time { /** @internal diagnostics */ operator std::string () const; + + /** convenience start for time calculations */ + TimeVar operator+ (TimeValue const& tval) const { return TimeVar(*this) + tval; } + TimeVar operator- (TimeValue const& tval) const { return TimeVar(*this) - tval; } }; diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 7511fb0ff..67d9feb9c 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -23,6 +23,7 @@ #include "lib/test/run.hpp" #include "lib/time/timevalue.hpp" +#include "lib/time/display.hpp" #include "lib/util.hpp" #include @@ -68,6 +69,7 @@ namespace test{ checkBasicTimeValues (ref); checkMutableTime (ref); + checkTimeConveniance (ref); createOffsets (ref); buildDuration (ref); buildTimeSpan (ref); @@ -145,6 +147,48 @@ namespace test{ } + /** @test additional convenience shortcuts supported + * especially by the canonical Time values. + */ + void + checkTimeConveniance (TimeValue org) + { + Time o1(org); + TimeVar v(org); + Time o2(v); + CHECK (o1 == o2); + CHECK (o1 == org); + + // integer interpreted as second + Time t1(1); + CHECK (t1 == TimeValue(GAVL_TIME_SCALE)); + + // create from fractional seconds + FSecs halve(1,2); + CHECK (0.5 == boost::rational_cast (halve)); + Time th(halve); + CHECK (th == TimeValue(GAVL_TIME_SCALE/2)); + + Time tx1(500,0); + CHECK (tx1 == th); + Time tx2(1,2); + CHECK (tx2 == TimeValue(2.001*GAVL_TIME_SCALE)); + Time tx3(1,1,1,1); + CHECK (tx3 == TimeValue(GAVL_TIME_SCALE*(0.001 + 1 + 60 + 60*60))); + + CHECK ("1:01:01.001" == string(tx3)); + + // create time variable on the fly.... + CHECK (th+th == t1); + CHECK (t1-th == th); + CHECK (((t1-th)*=2) == t1); + + // that was indeed a temporary and didn't affect the originals + CHECK (t1 == TimeValue(GAVL_TIME_SCALE)); + CHECK (th == TimeValue(GAVL_TIME_SCALE/2)); + } + + void createOffsets (TimeValue org) { @@ -215,8 +259,8 @@ namespace test{ Time endpoint = interval.getEnd(); CHECK (Offset(interval,endpoint) == Offset(org,five).abs()); - cout << "Interval: " << string(interval) - << " Endpoint: " << string(endpoint) << endl; + cout << "Interval: " << interval + << " Endpoint: " << endpoint << endl; } }; From d30515c37ed320d05193fb1a350f7bcad21da2ff Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 8 Jan 2011 03:30:10 +0100 Subject: [PATCH 040/140] analysis of range limit problems in quantisation --- src/lib/time.c | 4 +- src/tool/try.cpp | 46 +++++++++------- tests/lib/time/quantiser-basics-test.cpp | 68 ++++++++++++++++++++++++ wiki/renderengine.html | 46 ++++++++++++++-- 4 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index f81a2cfc7..6f62a8ada 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -65,10 +65,10 @@ lumiera_tmpbuf_print_time (gavl_time_t time) static double calculate_quantisation (gavl_time_t time, double grid, gavl_time_t origin) { - double val = time; + double val = time; //////TODO this solution doesn't work due to precission loss! val -= origin; val /= grid; - return floor (val); + return floor (val); //////TODO need a hand coded floor-function for integers } static double diff --git a/src/tool/try.cpp b/src/tool/try.cpp index af04c66eb..16716ccae 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -16,27 +16,32 @@ // 12/9 - tracking down a strange "warning: type qualifiers ignored on function return type" // 1/10 - can we determine at compile time the presence of a certain function (for duck-typing)? // 4/10 - pretty printing STL containers with python enabled GDB? +// 1/11 - exploring numeric limits //#include -//#define LUMIERA_LOGGING_CXX -//#include "include/logging.h" -//#include "include/nobugcfg.h" #include //#include #include -#include -#include -#include +//#include +#include + +#include +using boost::format; //using std::rand; using std::string; using std::cout; using std::endl; +void +checkDiv(int lhs, int rhs) + { + cout << format ("%f / %f = %f \n") % lhs % rhs % (lhs / rhs); + } int main (int, char**) @@ -44,21 +49,26 @@ main (int, char**) // NOBUG_INIT; - std::string str = "hullo wöld"; - std::vector vec (1000); + checkDiv (8,4); + checkDiv (9,4); + checkDiv (-8,4); + checkDiv (-9,4); + checkDiv (8,-4); + checkDiv (9,-4); + checkDiv (-8,-4); + checkDiv (-9,-4); - for (uint i = 0; i < vec.size(); ++i) - vec[i] = i; - cout << str << "\n.gulp.\n"; + int64_t muks = std::numeric_limits::max(); + muks -= 5; + double murks(muks); - // Note: when selecting the string or the vector in the Eclipse variables view - // (or when issuing "print str" at the GDB prompt), the GDB python pretty-printer - // should be activated. Requires an python enabled GDB (>6.8.50). Debian/Lenny isn't enough, - // but compiling the GDB package from Debian/Squeeze (GDB-7.1) is straightforward. - // Moreover, you need to check out and install the python pretty-printers and - // you need to activate them through your ~/.gdbinit - // see http://sourceware.org/gdb/wiki/STLSupport + cout << format("%f // %f || %g \n") % muks % murks % std::numeric_limits::epsilon(); + + int64_t glucks = murks; + cout << glucks <gridAlign(TimeValue(testPoint)); + return int(quantised); + } + }; + + void + coverQuantisationStandardCases() + { + TestQuant q0; + TestQuant q1(1); + + CHECK ( 6 == q0.quant(7) ); + CHECK ( 6 == q0.quant(6) ); + CHECK ( 3 == q0.quant(5) ); + CHECK ( 3 == q0.quant(4) ); + CHECK ( 3 == q0.quant(3) ); + CHECK ( 0 == q0.quant(2) ); + CHECK ( 0 == q0.quant(1) ); + CHECK ( 0 == q0.quant(0) ); + CHECK (-3 == q0.quant(-1)); + CHECK (-3 == q0.quant(-2)); + CHECK (-3 == q0.quant(-3)); + CHECK (-6 == q0.quant(-4)); + + CHECK ( 6 == q1.quant(7) ); + CHECK ( 3 == q1.quant(6) ); + CHECK ( 3 == q1.quant(5) ); + CHECK ( 3 == q1.quant(4) ); + CHECK ( 0 == q1.quant(3) ); + CHECK ( 0 == q1.quant(2) ); + CHECK ( 0 == q1.quant(1) ); + CHECK (-3 == q1.quant(0) ); + CHECK (-3 == q1.quant(-1)); + CHECK (-3 == q1.quant(-2)); + CHECK (-6 == q1.quant(-3)); + CHECK (-6 == q1.quant(-4)); + } + + + void + coverQuantisationCornerCases() + { + FixedFrameQuantiser case1 (1, Time::MIN); + CHECK (Time(0) == case1.gridAlign(Time::MIN)); + CHECK (Time(0) == case1.gridAlign(Time::MIN + TimeValue(1) )); + CHECK (Time(1) == case1.gridAlign(Time::MIN + Time(1) )); + CHECK (Time::MAX - Time(1) > case1.gridAlign( Time(-1) )); + CHECK (Time::MAX - Time(1) <= case1.gridAlign( Time (0) )); + CHECK (Time::MAX > case1.gridAlign( Time (0) )); + CHECK (Time::MAX == case1.gridAlign( Time(+1) )); + } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 27ecbe2da..ed5b6a57c 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -4192,17 +4192,55 @@ Viewed as a micro program, the processing patterns are ''weak typed'' &mdash
a given Render Engine configuration is a list of Processors. Each Processor in turn contains a Graph of ProcNode.s to do the acutal data processing. In order to cary out any calculations, the Processor needs to be called with a StateProxy containing the state information for this RenderProcess
 
-
+
The Quantiser implementation works by determining the grid interval containing a given raw time.
+These grid intervals are denoted by ordinal numbers (frame numbers), with interval #0 starting at the grid's origin and negative ordinals allowed.
 
 !frame quantisation convention
-Within Lumiera, there is a fixed convention how these frame intervals are to be defined (&rArr; time handling RfC)
+Within Lumiera, there is a fixed convention how these frame intervals are to be defined (&rArr; [[time handling RfC|http://staging.lumiera.org/documentation/devel/rfc/TimeHandling.html]])
 [img[Lumiera's frame quantisation convention|draw/FramePositions1.png]]
+Especially, this convention is agnostic of the actual zero-point of the scale and allows direct length calculations and seamless sequences of intervals.
+The //nominal coordinate// of an interval is also the starting point -- for automation keys frames we'll utilise special provisions.
 
 !range limitation problems
-because times are represented as 64bit integers, the time points addressable within a given scale grid can be limited, compared with time points addressable through raw (internal) time values. As an extreme example, consider a time scale with origin at Time::MIN -- such a scale is unable to represent any value above zero, because the resulting coordinates would exceed the range of the 64bit integer.
+because times are represented as 64bit integers, the time points addressable within a given scale grid can be limited, compared with time points addressable through raw (internal) time values. As an extreme example, consider a time scale with origin at Time::MIN -- such a scale is unable to represent any of the original scale's value above zero, because the resulting coordinates would exceed the range of the 64bit integer. Did I mention that 64bit micro ticks can represent about 300000 years?
 
-To avoid problems with larger intermediate values, the actual calculations are performed with doubles, which are clipped to the allowed value range prior to casting back to the integral data type. In all practically relevant cases, there is no danger of imprecisions or rounding errors, because the quantisation includes a floor operation. (Hypothetically, an imprecision could arise through extinction, when calculating the offset from the origin; but in practice this is irrelevant, assumed that the conversion 64bit integer to double yields reproducible double values)
+Now the actual problem is that using 64bit integers already means pushing to the limit. There is no easy escape hatch, like using a larger scale data type for intermediaries -- it //is// the largest built-in type. Basically we're touching the topic of ''safe integer arithmetics'' here, which is frequently discussed as a security concern. The situation is as follows:
+* every programmer promises "I'll do the checks when necessary" -- just to forget doing so in practice then.
+* for C programming, the situation is hopeless. Calling functions for simple arithmetics is outright impractical -- that won't happen volountarily
+* it is possible to build a ~SafeInt datatype in C++ though. While theoretically fine, in practice this also creates a host of problems:
+** actually detecting all cases and coding the checks is surprisingly hard and intricate.
+** the attempt to create a smooth integration with the built-in data types drives us right into one of the most problematic areas of C++
+** the performance hit is considerable (factor 2 - 4)  -- again luring people into "being clever".
+
+There is an existing [[SafeInt class by David LeBlanc|http://safeint.codeplex.com/]], provided by Microsoft through the ~CodePlex platform. It is part of the libraries shipped with ~VisualStudio and extensively used in Office 2010 and Windows, but also provided under a somewhat liberal license (you may use it but any derived work has to reproduce copyright and usage terms). Likely this situation also hindered the further [[development|http://thread.gmane.org/gmane.comp.lib.boost.devel/191010]] of a comparable library in boost (&rarr; [[vault|https://svn.boost.org/trac/boost/wiki/LibrariesUnderConstruction#Boost.SafeInt]]).
+
+!!!solution possibilities
+;abort operation
+:an incriminating calculation need to be detected somehow (see below).
+:the whole usage context gets aborted by excaption in case of an alarm, similar to an out of memory...
+:thus immediate corruption is avoided, but the user has to realise and avoid the general situation.
+;limit values
+:provide a limiter to kick in after detecting an alarm (see below).
+:time values will just stick to the maximum/minimum boundary value....
+:the rest of the application needs to be prepared for timing calculations to return degenerate results
+;~SafeInt
+:base ~TimeValue on a ~SafeInt<gavl_time_t>
+:this way we could easily guarantee for detecting any //situation.//
+:of course the price is the performance hit on all timing calculations.
+:Moreover -- if we want to limit values instead of raising an exception, we'd need to write our own ~SaveInt.
+;check some operations
+:time values are mostly immutable, thus it would likely be sufficient only to check some strategically important points
+:#parsing or de-serialising
+:#calculations in ~TimeVar
+:#quantisation
+:#build Time from framecount
+;limit time range
+:because the available time range is so huge, it wouldn't hurt to reduce it by one or two decimals
+:this way both the implementation of the checks could be simplified and the probability of an overflow reduced
+;ignore the problem
+:again, because of the huge time range, the problem could generally be deemed irrelevant
+:we could apply a limiter to enforce a reduced time range just in the quantisation and evaluation of timecodes
 
From 3cf9974211928f07bf338d9fa500ee32cb0a702b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 8 Jan 2011 19:11:42 +0100 Subject: [PATCH 041/140] provide unary minus to flip a TimeVar around origin --- src/lib/time/timevalue.hpp | 3 +++ tests/lib/time/time-value-test.cpp | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 08ed06f02..a19c806be 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -138,6 +138,9 @@ namespace time { // Supporting multiplication with integral factor TimeVar& operator*= (int fact) { t_ *= fact; return *this; } + + // Supporting flip sign + TimeVar operator- () const { return TimeVar(*this)*=-1; } // baseclass TimeValue is already totally_ordered }; diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 67d9feb9c..342697b8f 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -144,6 +144,11 @@ namespace test{ gavl_time_t raw (var); CHECK (raw == org); CHECK (raw > org - two); + + // unary minus will flip around origin + CHECK (zero == -var + var); + CHECK (zero != -var); + CHECK (var == org); // unaltered } From 52eb4c470932937a175223136bf6d1e59a597528 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 8 Jan 2011 20:01:26 +0100 Subject: [PATCH 042/140] set an explicit artificial limit on the allowed time range this is the first building block in an attempt to protrect against time wraparound. The intention is not to be airtight, but practically effective. A really airtight solution would require writing our own SafeInt class --- src/lib/time/lumitime.cpp | 14 ++++++++++++-- src/lib/time/timevalue.hpp | 27 ++++++++++++++++++++++++++- src/tool/try.cpp | 2 +- wiki/renderengine.html | 6 +++++- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index d2daba3c2..15444a60c 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -50,8 +50,9 @@ namespace time { } - const Time Time::MAX ( TimeValue (+std::numeric_limits::max()) ); - const Time Time::MIN ( TimeValue (-std::numeric_limits::max()) ); + /** @note the allowed time range is explicitly limited to help overflow protection */ + const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits::max() / 30) ); + const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) ); /** convenience constructor to build an @@ -100,6 +101,15 @@ namespace time { } + /** @internal backdoor to sneak in a raw time value + * bypassing any normalisation and limiting */ + TimeValue + TimeValue::buildRaw_ (gavl_time_t raw) + { + return reinterpret_cast (raw); + } + + }} // namespace lib::Time diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index a19c806be..94b51d209 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -71,9 +71,13 @@ namespace time { } public: + /** explicit limit of allowed time range */ + static gavl_time_t limited (gavl_time_t raw); + + explicit TimeValue (gavl_time_t val=0) - : t_(val) + : t_(limited (val)) { } /** copy initialisation allowed */ @@ -83,6 +87,7 @@ namespace time { /** @internal to pass Time values to C functions */ friend gavl_time_t _raw (TimeValue const& time) { return time.t_; } + static TimeValue buildRaw_(gavl_time_t); // Supporting totally_ordered friend bool operator< (TimeValue const& t1, TimeValue const& t2) { return t1.t_ < t2.t_; } @@ -306,5 +311,25 @@ namespace time { }; + + + /* == implementations == */ + + /** @internal applies a limiter on the provided + * raw time value to keep it within the arbitrary + * boundaries defined by (Time::MAX, Time::MIN). + * While Time entities are \c not a "safeInt" + * implementation, we limit new values and + * establish this safety margin to prevent + * wrap-around during time quantisation */ + inline gavl_time_t + TimeValue::limited (gavl_time_t raw) + { + return raw > Time::MAX? Time::MAX.t_ + : raw < Time::MIN? Time::MIN.t_ + : raw; + } + + }} // lib::time #endif diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 16716ccae..ef05aaa97 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -60,7 +60,7 @@ main (int, char**) int64_t muks = std::numeric_limits::max(); - muks -= 5; + muks /= 30; double murks(muks); cout << format("%f // %f || %g \n") % muks % murks % std::numeric_limits::epsilon(); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index ed5b6a57c..550b53eaa 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -4192,7 +4192,7 @@ Viewed as a micro program, the processing patterns are ''weak typed'' &mdash
a given Render Engine configuration is a list of Processors. Each Processor in turn contains a Graph of ProcNode.s to do the acutal data processing. In order to cary out any calculations, the Processor needs to be called with a StateProxy containing the state information for this RenderProcess
 
-
+
The Quantiser implementation works by determining the grid interval containing a given raw time.
 These grid intervals are denoted by ordinal numbers (frame numbers), with interval #0 starting at the grid's origin and negative ordinals allowed.
 
@@ -4241,6 +4241,10 @@ There is an existing [[SafeInt class by David LeBlanc|http://safeint.codeplex.co
 ;ignore the problem
 :again, because of the huge time range, the problem could generally be deemed irrelevant
 :we could apply a limiter to enforce a reduced time range just in the quantisation and evaluation of timecodes
+
+A combination of the last approaches seems to be most appropriate here:
+Limit the officially allowed time range and perform simple checks when quantising a time value and when applying a scale factor.
+We limit {{{Time::MAX}}} by factor 1/30 and define the minimum //symmetrically to this.// This still leaves us with ''±9700 years allowed timerange''.
 
From 9d8961d650d1c81233177cecaada6243918460e1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 9 Jan 2011 00:38:51 +0100 Subject: [PATCH 043/140] Fix test definitions tue to the slightly changed time display format In the course of the preceeding work, I changed the standard Time formatting in time.c, such as to omit the leading "0" on the hour; it doesn't seem likely that displayed hour values will frequently have two digits... --- tests/40components.tests | 6 +++--- tests/45controller.tests | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/40components.tests b/tests/40components.tests index 0055ce202..ac27d14bf 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -471,9 +471,9 @@ TEST "Iterable data source" IterSource_test 13 <. +out: Command-State. arguments=Closure\(.,0:..:....00\), ·noUNDO·. +out: Command-State. arguments=Closure\(0:..:....00,glorious,..\), . out: would be serialised.....Command-State. arguments=Closure\(\), ·noUNDO·. out: would be serialised.....Command-State. arguments=Closure\(.\), ·noUNDO·. -out: would be serialised.....Command-State. arguments=Closure\(.,00:..:....00\), ·noUNDO·. -out: would be serialised.....Command-State. arguments=Closure\(00:..:....00,glorious,..\), . +out: would be serialised.....Command-State. arguments=Closure\(.,0:..:....00\), ·noUNDO·. +out: would be serialised.....Command-State. arguments=Closure\(0:..:....00,glorious,..\), . out: sizeof\( .+control1.ArgumentHolder.+ \) = .+ -out: Command-State. arguments=Closure\(00:..:....00,Lumiera rocks,..\), ·noUNDO·. +out: Command-State. arguments=Closure\(0:..:....00,Lumiera rocks,..\), ·noUNDO·. out: capture state... out: captured state: START...Lumiera rocks -out: Command-State. arguments=Closure\(00:..:....00,Lumiera rocks,..\), . +out: Command-State. arguments=Closure\(0:..:....00,Lumiera rocks,..\), . out: invoke operation... -out: START...doIt\( Time=00:..:....00 "Lumiera rocks" rand=.. \) +out: START...doIt\( Time=0:..:....00 "Lumiera rocks" rand=.. \) out: undo... memento=START...Lumiera rocks -out: START...doIt\( Time=00:..:....00 "Lumiera rocks" rand=.. \)undoIt\(time=00:..:....00\)----memento-:START...Lumiera rocks +out: START...doIt\( Time=0:..:....00 "Lumiera rocks" rand=.. \)undoIt\(time=0:..:....00\)----memento-:START...Lumiera rocks out: capture state... -out: modified: Command-State. arguments=Closure\(00:00:00.123,unbelievable,..\), . -out: copied : Command-State. arguments=Closure\(00:..:....00,Lumiera rocks,..\), . -out: undo... memento=START...doIt\( Time=00:..:....00 "Lumiera rocks" rand=.. \)undoIt\(time=00:..:....00\)----memento-:START...Lumiera rocksLumiera rocks -out: RESET...undoIt\(time=00:..:....00\)----memento-:START...doIt\( Time=00:..:....00 "Lumiera rocks" rand=.. \)undoIt\(time=00:..:....00\)----memento-:START...Lumiera rocksLumiera rocks +out: modified: Command-State. arguments=Closure\(0:00:00.123,unbelievable,..\), . +out: copied : Command-State. arguments=Closure\(0:..:....00,Lumiera rocks,..\), . +out: undo... memento=START...doIt\( Time=0:..:....00 "Lumiera rocks" rand=.. \)undoIt\(time=0:..:....00\)----memento-:START...Lumiera rocksLumiera rocks +out: RESET...undoIt\(time=0:..:....00\)----memento-:START...doIt\( Time=0:..:....00 "Lumiera rocks" rand=.. \)undoIt\(time=0:..:....00\)----memento-:START...Lumiera rocksLumiera rocks END TEST "build argument accepting function" ArgumentTupleAccept_test < Date: Sun, 9 Jan 2011 00:40:27 +0100 Subject: [PATCH 044/140] implement an Integer-floor function for time quantisation Contrary to the built-in division operator, this function always truncates towards the next smaller integer (also for negative numbers) --- src/lib/util.hpp | 21 +++ src/tool/try.cpp | 32 +++- tests/40components.tests | 5 + tests/lib/meta/generator-test.cpp | 2 +- tests/lib/util-floordiv-test.cpp | 262 ++++++++++++++++++++++++++++++ 5 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 tests/lib/util-floordiv-test.cpp diff --git a/src/lib/util.hpp b/src/lib/util.hpp index cc1d18909..c8ea896ed 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -56,6 +56,27 @@ namespace util { return n1 < n2? N1(n2) : n1; } + /** floor function for integer arithmetics. + * Unlike the built-in integer division, this function + * always rounds towards the next \em smaller integer, + * even for negative numbers. + * @warning floor on doubles performs way better + * @see UtilFloordiv_test + */ + template + inline LI + floordiv (LI num, LI den) + { + if (0 < (num^den)) + return num/den; + else + { + ldiv_t res = ldiv(num,den); + return (res.rem)? res.quot-1 + : res.quot; + } + } + /** a family of util functions providing a "no value whatsoever" test. Works on strings and all STL containers, includes NULL test for pointers */ diff --git a/src/tool/try.cpp b/src/tool/try.cpp index ef05aaa97..f73e9dd6f 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -27,6 +27,7 @@ #include //#include #include +#include #include @@ -37,10 +38,31 @@ using std::string; using std::cout; using std::endl; +long +floordiv (long num, long den) + { + if (0 < (num^den)) + return num/den; + else + { + ldiv_t res = ldiv(num,den); + return (res.rem)? res.quot-1 + : res.quot; + } + } + +long +floordiv2 (long num, long den) + { + ldiv_t res = ldiv(num,den); + return (0 >= res.quot && res.rem)? res.quot-1 + : res.quot; + } + void checkDiv(int lhs, int rhs) { - cout << format ("%f / %f = %f \n") % lhs % rhs % (lhs / rhs); + cout << format ("%f / %f = %f \tfloor=%f floordiv=%f \n") % lhs % rhs % (lhs / rhs) % floor(double(lhs)/rhs) % floordiv2(lhs,rhs); } int @@ -57,6 +79,14 @@ main (int, char**) checkDiv (9,-4); checkDiv (-8,-4); checkDiv (-9,-4); + + checkDiv (0,4); + checkDiv (0,-4); + checkDiv (1,4); + checkDiv (1,-4); + checkDiv (-1,4); + checkDiv (-1,-4); + int64_t muks = std::numeric_limits::max(); diff --git a/tests/40components.tests b/tests/40components.tests index ac27d14bf..0afb978db 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -1083,6 +1083,11 @@ return: 0 END +TEST "integer rounding utility" UtilFloordiv_test < + + 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 "lib/test/run.hpp" +#include "lib/util.hpp" + +#include +#include +#include +#include + +using ::Test; +using std::cout; +using std::rand; +using boost::format; + + +namespace util { +namespace test { + + + + namespace{ // Test data and operations + + const uint NUM_ELMS_PERFORMANCE_TEST = 50000000; + const uint NUMBER_LIMIT = 1 << 30; + + typedef std::vector VecI; + + VecI + buildTestNumberz (uint cnt) + { + VecI data; + for (uint i=0; i= res.quot && res.rem)? res.quot-1 + : res.quot; + } + + } // (End) test data and operations + + + + /********************************************************************** + * @test Evaluate a custom built integer floor function. + * This function is crucial for Lumiera's rule of quantisation + * of time values into frame intervals. This rule requires time + * points to be rounded towards the next lower frame border always, + * irrespective of the relation to the actual time origin. + * Contrast this to the built-in integer division operator, which + * truncates towards zero. + * + * @note if invoked with an non empty parameter, this test performs + * some interesting timing comparisons, which initially were + * used to tweak the implementation a bit. + * @see util.hpp + * @see QuantiserBasics_test + */ + class UtilFloordiv_test : public Test + { + + virtual void + run (Arg arg) + { + verifyBehaviour (); + + if (arg.size()) + runPerformanceTest(); + } + + + void + verifyBehaviour () + { + CHECK ( 3 == floordiv ( 12,4)); + CHECK ( 2 == floordiv ( 11,4)); + CHECK ( 2 == floordiv ( 10,4)); + CHECK ( 2 == floordiv ( 9,4)); + CHECK ( 2 == floordiv ( 8,4)); + CHECK ( 1 == floordiv ( 7,4)); + CHECK ( 1 == floordiv ( 6,4)); + CHECK ( 1 == floordiv ( 5,4)); + CHECK ( 1 == floordiv ( 4,4)); + CHECK ( 0 == floordiv ( 3,4)); + CHECK ( 0 == floordiv ( 2,4)); + CHECK ( 0 == floordiv ( 1,4)); + CHECK ( 0 == floordiv ( 0,4)); + CHECK (-1 == floordiv (- 1,4)); + CHECK (-1 == floordiv (- 2,4)); + CHECK (-1 == floordiv (- 3,4)); + CHECK (-1 == floordiv (- 4,4)); + CHECK (-2 == floordiv (- 5,4)); + CHECK (-2 == floordiv (- 6,4)); + CHECK (-2 == floordiv (- 7,4)); + CHECK (-2 == floordiv (- 8,4)); + CHECK (-3 == floordiv (- 9,4)); + CHECK (-3 == floordiv (-10,4)); + CHECK (-3 == floordiv (-11,4)); + CHECK (-3 == floordiv (-12,4)); + } + + + + /** @test timing measurements to compare implementation details. + * This test uses a sequence of random integers, where the values + * used as denominator are ensured not to be zero. + * + * \par measurement results + * My experiments (AMD Athlon-64 4200 X2) gave me + * the following timing measurements in nanoseconds: + * + * Verification.......... 127.7 + * Integer_div........... 111.7 + * double_floor.......... 74.8 + * floordiv_int.......... 112.7 + * floordiv_long......... 119.8 + * floordiv_int64_t...... 121.4 + * floordiv_long_alt..... 122.7 + * + * These figures are the average of 6 runs with 50 million + * iterations each (as produced by this function) + * + * \par conclusions + * The most significant result is the striking performance of the + * fpu based calculation. Consequently, integer arithmetics should + * only be used when necessary due to resolution requirements, as + * is the case for int64_t based Lumiera Time values, which require + * a precision beyond the 16 digits provided by double. + * Besides that, we can conclude that the additional tests and + * adjustment of the custom floordiv only creates a slight overhead + * compared to the built-in integer div function. The comparison + * with the \c floordiv instantiation is largely moot, because + * this internally calls \c fdiv on values promoted to long. Another + * oddity in the same category is the slightly better performance + * of long over int64_t. Also, the alternative formulation of + * the function, which uses the \c fdiv() function also to divide + * the positive results, performs only slightly worse. So this + * actual implementation was chosen mainly because it seems + * to state its intent more clearly in code. + */ + void + runPerformanceTest () + { + VecI testdata = buildTestNumberz (2*NUM_ELMS_PERFORMANCE_TEST); + typedef VecI::const_iterator I; + + clock_t start(0), stop(0); + format resultDisplay("timings(%s)%|30T.|%5.3fsec\n"); + +#define START_TIMINGS start=clock(); +#define DISPLAY_TIMINGS(ID) \ + stop = clock(); \ + cout << resultDisplay % STRINGIFY (ID) % (double(stop-start)/CLOCKS_PER_SEC) ; + + START_TIMINGS + for (I ii =testdata.begin(); ii!=testdata.end(); ) + { + int num = *ii; + ++ii; + int den = *ii; + ++ii; + CHECK (floor(double(num)/den) == floordiv(num,den)); + } + DISPLAY_TIMINGS (Verification) + + START_TIMINGS + for (I ii =testdata.begin(); ii!=testdata.end(); ) + { + integerDiv (*ii++, *ii++); + } + DISPLAY_TIMINGS (Integer_div) + + START_TIMINGS + for (I ii =testdata.begin(); ii!=testdata.end(); ) + { + floor (double(*ii++) / *ii++); + } + DISPLAY_TIMINGS (double_floor) + + START_TIMINGS + for (I ii =testdata.begin(); ii!=testdata.end(); ) + { + floordiv (*ii++, *ii++); + } + DISPLAY_TIMINGS (floordiv_int) + + START_TIMINGS + for (I ii =testdata.begin(); ii!=testdata.end(); ) + { + floordiv (long(*ii++), long(*ii++)); + } + DISPLAY_TIMINGS (floordiv_long) + + START_TIMINGS + for (I ii =testdata.begin(); ii!=testdata.end(); ) + { + floordiv (int64_t(*ii++), int64_t(*ii++)); + } + DISPLAY_TIMINGS (floordiv_int64_t) + + START_TIMINGS + for (I ii =testdata.begin(); ii!=testdata.end(); ) + { + floordiv_alternate (*ii++, *ii++); + } + DISPLAY_TIMINGS (floordiv_long_alt) + } + }; + + + + + LAUNCHER (UtilFloordiv_test, "unit common"); + + +}} // namespace util::test From 237d287021b70d24943e6b46ecc1d0b50294d5a1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 9 Jan 2011 02:13:58 +0100 Subject: [PATCH 045/140] change time.h into a multilingual header --- src/gui/panels/timeline-panel.cpp | 3 +- src/gui/widgets/timeline/timeline-ruler.cpp | 3 +- src/lib/Makefile.am | 2 +- src/lib/lumitime-fmt.hpp | 4 +- src/lib/{time.c => time.cpp} | 6 +- src/lib/time.h | 79 +++++++++++++++++---- src/lib/time/lumitime.cpp | 3 - src/lib/time/quantiser.cpp | 3 - src/lib/time/timecode.cpp | 3 - tests/library/test-time.c | 2 +- 10 files changed, 75 insertions(+), 33 deletions(-) rename src/lib/{time.c => time.cpp} (98%) diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 5c79bbe44..9441e8969 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -29,9 +29,8 @@ #include "gui/model/project.hpp" #include "gui/controller/controller.hpp" -extern "C" { #include "lib/time.h" -} + using namespace Gtk; using namespace sigc; diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index 63d6ecc7a..c01835c71 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -27,9 +27,8 @@ #include "gui/window-manager.hpp" #include "gui/util/cairo-util.hpp" -extern "C" { #include "lib/time.h" -} + using namespace Gtk; using namespace Cairo; diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 8374eab38..fe64f516a 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -59,7 +59,7 @@ liblumiera_la_SOURCES = \ $(liblumiera_la_srcdir)/test/suite.cpp \ $(liblumiera_la_srcdir)/test/test-helper.cpp \ $(liblumiera_la_srcdir)/test/testoption.cpp \ - $(liblumiera_la_srcdir)/time.c \ + $(liblumiera_la_srcdir)/time.cpp \ $(liblumiera_la_srcdir)/time/lumitime.cpp \ $(liblumiera_la_srcdir)/time/quantiser.cpp \ $(liblumiera_la_srcdir)/time/timecode.cpp \ diff --git a/src/lib/lumitime-fmt.hpp b/src/lib/lumitime-fmt.hpp index 20b24f951..b916c13a6 100644 --- a/src/lib/lumitime-fmt.hpp +++ b/src/lib/lumitime-fmt.hpp @@ -45,13 +45,11 @@ #define LUMIERA_LUMITIME_FMT_H #include "lib/lumitime.hpp" - -extern "C" { #include "lib/time.h" -} #include + namespace lumiera { diff --git a/src/lib/time.c b/src/lib/time.cpp similarity index 98% rename from src/lib/time.c rename to src/lib/time.cpp index 6f62a8ada..a85e2d1ea 100644 --- a/src/lib/time.c +++ b/src/lib/time.cpp @@ -19,12 +19,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include "lib/time.h" +#include "lib/error.hpp" +extern "C" { #include "lib/tmpbuf.h" +} -#include #include +#include /* GAVL_TIME_SCALE is the correct factor or dividend when using gavl_time_t for diff --git a/src/lib/time.h b/src/lib/time.h index eabc4d5f8..666fa4206 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -19,17 +19,66 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/** @file time.h + ** Common functions for handling of time values. + ** Working with time values in sexagesimal format, quantising time and converting + ** to/from common timecode formats can be tricky to get right. Thus the goal is + ** to concentrate the actual bits of math for these operations into a small number + ** of library functions, which are easy to test thoroughly in isolation. + ** + ** Built on top of that, the actual time handling in the GUI and within the Lumiera + ** session is mostly confined to use the opaque lib::time::Time wrapper objects. + ** When time values actually need to be \em quantised (aligned to a frame grid), + ** this is expressed at the API through using the lib::time::QuTime type, which + ** then in turn can be materialised into a number of \em timecode formats. + ** These definitions ensure that whenever an actual quantisation (rounding) + ** operation is performed, the link to the appropriate time grid is available, + ** so that multiple output or rendering operations can use differing time origins + ** and frame rates simultaneously on the same model. + ** + ** The Lumiera backend functions mostly operate on raw frame counts, which in this + ** model are defined to be a special kind of timecode, and thus dependent on a + ** preceding time quantisation. + ** + ** + ** @see lib::time::Time + ** @see timequant.hpp + ** @see TimeValue_test + ** + */ + + #ifndef LUMIERA_TIME_H #define LUMIERA_TIME_H #include #include +#ifdef __cplusplus /*=================== C++ facilities ===================== */ +#include "lib/time/timevalue.hpp" + + /** - * Formats a time in a safeclib tmpbuf in HH:MM:SS:mmm format. - * @param size maximal length for the string - * @param the time value to print - * @return safeclib temporary buffer containing the constructed of the string + * Converts a fraction of seconds to Lumiera's internal opaque time scale. + * @param fractionalSeconds given as rational number + */ +inline gavl_time_t +lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds) +{ + return boost::rational_cast (GAVL_TIME_SCALE * fractionalSeconds); +} + + + + + +extern "C" { /* ===================== C interface ======================== */ +#endif + + +/** + * Formats a time value in H:MM:SS.mmm format into a temporary buffer. + * @return safeclib temporary buffer containing formatted time string */ char* lumiera_tmpbuf_print_time (gavl_time_t time); @@ -41,19 +90,19 @@ lumiera_tmpbuf_print_time (gavl_time_t time); * as zero. Each interval includes its lower bound, but excludes its upper bound. * @param grid spacing of the grid intervals, measured in GAVL_TIME_SCALE * @return number of the grid interval containing the given time. - * @warning the resulting value is limited such as to fit into a 64bit long + * @warning the resulting value is limited to (Time::Min, Time::MAX) */ int64_t lumiera_quantise_frames (gavl_time_t time, double grid, gavl_time_t origin); /** - * Similar to #lumiera_quantise_frames, but returns a grid aligned \em time value + * Similar to #lumiera_quantise_frames, but returns a grid aligned \em time value * @return time of start of the grid interval containing the given time, * but measured relative to the origin * @warning because the resulting value needs to be limited to fit into a 64bit long, * the addressable time range can be considerably reduced. For example, if - * origin = LLONG_MIN, then all original time values above zero will be - * clipped, because the result, relative to origin, needs to be <= LLONG_MAX + * origin = Time::MIN, then all original time values above zero will be + * clipped, because the result, relative to origin, needs to be <= Time::MAX */ gavl_time_t lumiera_quantise_time (gavl_time_t time, double grid, gavl_time_t origin); @@ -65,24 +114,28 @@ gavl_time_t lumiera_build_time (long millis, uint secs, uint mins, uint hours); /** - * Get the hour part of given time. + * Extract the hour part of given time. */ int lumiera_time_hours(gavl_time_t time); /** - * Get the minute part of given time. + * Extract the minute part of given time. */ int lumiera_time_minutes(gavl_time_t time); /** - * Get the seconds part of given time. + * Extract the seconds part of given time. */ int lumiera_time_seconds(gavl_time_t time); /** - * Get the milliseconds part of given time. + * Extract the milliseconds part of given time. */ int lumiera_time_millis(gavl_time_t time); -#endif + +#ifdef __cplusplus +}//extern "C" +#endif +#endif diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index 15444a60c..53338e3a6 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -23,10 +23,7 @@ #include "lib/lumitime.hpp" #include "lib/time/timevalue.hpp" - -extern "C" { #include "lib/time.h" -} #include #include diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index c1608184f..02ef85be8 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -24,10 +24,7 @@ #include "lib/time/quantiser.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" - -extern "C" { #include "lib/time.h" -} #include diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 3bb80e697..37e00a9ea 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -26,10 +26,7 @@ #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" #include "lib/time/formats.hpp" - -extern "C" { #include "lib/time.h" -} using std::string; diff --git a/tests/library/test-time.c b/tests/library/test-time.c index 4ce8b63bb..52ce79279 100644 --- a/tests/library/test-time.c +++ b/tests/library/test-time.c @@ -21,8 +21,8 @@ typedef unsigned int uint; -#include "lib/time.h" #include "tests/test.h" +#include "lib/time.h" #include From af9c799fc8296df3d61e15af9bdc77fafaf347fe Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 9 Jan 2011 04:56:46 +0100 Subject: [PATCH 046/140] Fix time quantisation to circumvent the precision problem required to re-arrange several functions in order to use the new util::floordiv and to get all relevant calculations into time.h --- src/lib/time.cpp | 64 +++++++++++-------- src/lib/time.h | 23 ++++--- src/lib/time/lumitime.cpp | 44 +++++++++---- src/lib/time/quantiser.cpp | 6 +- src/lib/time/quantiser.hpp | 6 +- src/lib/time/timevalue.hpp | 78 +++++++++++++++++++++++- src/lib/util.hpp | 6 +- tests/lib/time/quantiser-basics-test.cpp | 2 +- 8 files changed, 176 insertions(+), 53 deletions(-) diff --git a/src/lib/time.cpp b/src/lib/time.cpp index a85e2d1ea..5181c5fc9 100644 --- a/src/lib/time.cpp +++ b/src/lib/time.cpp @@ -21,6 +21,8 @@ #include "lib/time.h" #include "lib/error.hpp" +#include "lib/util.hpp" + extern "C" { #include "lib/tmpbuf.h" } @@ -28,6 +30,15 @@ extern "C" { #include #include +using util::floordiv; +using lib::time::FSecs; +using lib::time::FrameRate; +using boost::rational_cast; + +namespace error = lumiera::error; + + + /* GAVL_TIME_SCALE is the correct factor or dividend when using gavl_time_t for * units of whole seconds from gavl_time_t. Since we want to use milliseconds, @@ -63,43 +74,48 @@ lumiera_tmpbuf_print_time (gavl_time_t time) return buffer; } - -static double -calculate_quantisation (gavl_time_t time, double grid, gavl_time_t origin) +gavl_time_t +lumiera_rational_to_time (FSecs const& fractionalSeconds) { - double val = time; //////TODO this solution doesn't work due to precission loss! - val -= origin; - val /= grid; - return floor (val); //////TODO need a hand coded floor-function for integers + return rational_cast (GAVL_TIME_SCALE * fractionalSeconds); } -static double -clip_to_64bit (double val) + +gavl_time_t +lumiera_frame_duration (FrameRate const& fps) { - if (val > LLONG_MAX) - val = LLONG_MAX; - else - if (val < LLONG_MIN) - val = LLONG_MIN; + if (!fps) + throw error::Logic ("Impossible to quantise to an zero spaced frame grid" + , error::LUMIERA_ERROR_BOTTOM_VALUE); - return val; + FSecs duration = rational_cast (1/fps); + return lumiera_rational_to_time (duration); } -int64_t -lumiera_quantise_frames (gavl_time_t time, double grid, gavl_time_t origin) +namespace { // implementation helper + + inline long + calculate_quantisation (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) + { + time -= origin; + return floordiv (time,grid); + } +} + + +long +lumiera_quantise_frames (gavl_time_t time, gavl_time_t grid, gavl_time_t origin) { - double gridNr = calculate_quantisation (time, grid, origin); - gridNr = clip_to_64bit (gridNr); - return (int64_t) gridNr; + return calculate_quantisation (time, origin, grid); } gavl_time_t -lumiera_quantise_time (gavl_time_t time, double grid, gavl_time_t origin) +lumiera_quantise_time (gavl_time_t time, gavl_time_t grid, gavl_time_t origin) { - double count = calculate_quantisation (time, grid, origin); - double alignedTime = clip_to_64bit (count * grid); - return (gavl_time_t) alignedTime; + int64_t count = calculate_quantisation (time, origin, grid); + gavl_time_t alignedTime = count * grid; + return alignedTime; } diff --git a/src/lib/time.h b/src/lib/time.h index 666fa4206..5d22908b1 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -61,12 +61,21 @@ /** * Converts a fraction of seconds to Lumiera's internal opaque time scale. * @param fractionalSeconds given as rational number + * @note inconsistent with Lumiera's general quantisation behaviour, + * here negative fractional micro-ticks are truncated towards zero. + * This was deemed irrelevant in practice. */ -inline gavl_time_t -lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds) -{ - return boost::rational_cast (GAVL_TIME_SCALE * fractionalSeconds); -} +gavl_time_t +lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds); + + +/** + * Calculates the duration of one frame in Lumiera time units. + * @param framerate underlying framerate as rational number + * @throw error::Logic on zero framerate + */ +gavl_time_t +lumiera_frame_duration (lib::time::FrameRate const& fps); @@ -92,7 +101,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time); * @return number of the grid interval containing the given time. * @warning the resulting value is limited to (Time::Min, Time::MAX) */ -int64_t +long lumiera_quantise_frames (gavl_time_t time, double grid, gavl_time_t origin); /** @@ -105,7 +114,7 @@ lumiera_quantise_frames (gavl_time_t time, double grid, gavl_time_t origin); * clipped, because the result, relative to origin, needs to be <= Time::MAX */ gavl_time_t -lumiera_quantise_time (gavl_time_t time, double grid, gavl_time_t origin); +lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid); /** * Builds a time value by summing up the given components. diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index 53338e3a6..b42fd4f4c 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -35,16 +35,6 @@ using std::string; namespace lib { namespace time { - namespace { - /** implementation detail: convert a rational number denoting fractionalSeconds - * into the internal time scale used by GAVL and Lumiera internal time values. - */ - inline gavl_time_t - rational2time (FSecs const& fractionalSeconds) - { - return boost::rational_cast (GAVL_TIME_SCALE * fractionalSeconds); - } - } /** @note the allowed time range is explicitly limited to help overflow protection */ @@ -76,7 +66,7 @@ namespace time { * An example would be to the time unit of a framerate. */ Time::Time (FSecs const& fractionalSeconds) - : TimeValue(rational2time (fractionalSeconds)) + : TimeValue(lumiera_rational_to_time (fractionalSeconds)) { } @@ -108,6 +98,38 @@ namespace time { + /** predefined constant for PAL framerate */ + const FrameRate FrameRate::PAL (25); + const FrameRate FrameRate::NTSC (3000,1001); + + + /** @return time span of one frame of this rate, + * cast into internal Lumiera time scale */ + Duration + FrameRate::duration() const + { + if (0 == *this) + throw error::Logic ("Impossible to quantise to an zero spaced frame grid" + , error::LUMIERA_ERROR_BOTTOM_VALUE); + + return Duration (1, *this); + } + + + /** duration of the given number of frames */ + Duration::Duration (ulong count, FrameRate const& fps) + : Offset(TimeValue (count? lumiera_frame_duration (fps/count) : _raw(Duration::NIL))) + { } + + + /** constant to indicate "no duration" */ + const Duration Duration::NIL = Offset(TimeValue(0)); + + + + + + }} // namespace lib::Time ///////////////////////////////////////////////////////////////////////////TODO leftover of the existing/initial lumitime-Implementation diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 02ef85be8..567a7d6de 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -74,9 +74,9 @@ namespace time { * as origin of the scale. Quantisation then means to determine the grid interval containing * a given raw time value. Here, the grid interval number zero \em starts at the origin; * each interval includes its lower bound and excludes its upper bound.*/ - FixedFrameQuantiser::FixedFrameQuantiser (FSecs frames_per_second, TimeValue referencePoint) + FixedFrameQuantiser::FixedFrameQuantiser (FrameRate const& frames_per_second, TimeValue referencePoint) : origin_(referencePoint) - , raster_(rational_cast (GAVL_TIME_SCALE / __ensure_notZero(frames_per_second))) + , raster_(frames_per_second.duration()) { } @@ -96,7 +96,7 @@ namespace time { TimeValue FixedFrameQuantiser::gridAlign (TimeValue const& rawTime) { - return TimeValue (lumiera_quantise_time (_raw(rawTime), raster_, _raw(origin_))); + return TimeValue (lumiera_quantise_time (_raw(rawTime), _raw(origin_), _raw(raster_))); } diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 83467d7f0..8d79f381e 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -102,11 +102,11 @@ namespace time { class FixedFrameQuantiser : public Quantiser { - Time origin_; - double raster_; + Time origin_; + Duration raster_; public: - FixedFrameQuantiser (FSecs frames_per_second, TimeValue referencePoint =TimeValue(0)); + FixedFrameQuantiser (FrameRate const& frames_per_second, TimeValue referencePoint =TimeValue(0)); TimeValue gridAlign (TimeValue const&); diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 94b51d209..65402882a 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -24,6 +24,8 @@ #ifndef LIB_TIME_TIMEVALUE_H #define LIB_TIME_TIMEVALUE_H +#include "lib/error.hpp" + #include #include #include @@ -38,6 +40,8 @@ extern "C" { namespace lib { namespace time { + namespace error = lumiera::error; + /** * basic constant internal time value. @@ -199,7 +203,9 @@ namespace time { /** rational representation of fractional seconds * @warning do not mix up gavl_time_t and FSecs */ typedef boost::rational FSecs; - + + class FrameRate; + /** * Lumiera's internal time value datatype. * This is a TimeValue, but now more specifically denoting @@ -270,6 +276,10 @@ namespace time { Duration (Offset const& distance) : Offset(distance.abs()) { } + + Duration (ulong count, FrameRate const& fps); + + static const Duration NIL; }; @@ -311,9 +321,48 @@ namespace time { }; + /** + * Framerate specified as frames per second. + * Implemented as rational number. + */ + class FrameRate + : public boost::rational + { + typedef boost::rational IFrac; + + public: + FrameRate (uint fps) ; + FrameRate (uint num, uint denom); + FrameRate (IFrac const& fractionalRate); + + // standard copy acceptable; + + static const FrameRate PAL; + static const FrameRate NTSC; + + /** duration of one frame */ + Duration duration() const; + }; + + /* == implementations == */ + + namespace { // implementation helpers... + + template + inline NUM + __ensure_nonzero (NUM n) + { + if (!n) + throw error::Logic ("Zero spaced grid not allowed" + , error::LUMIERA_ERROR_BOTTOM_VALUE); + return n; + } + }//(End) implementation helpers + + /** @internal applies a limiter on the provided * raw time value to keep it within the arbitrary @@ -330,6 +379,33 @@ namespace time { : raw; } + inline + FrameRate::FrameRate (uint fps) + : IFrac (__ensure_nonzero(fps)) + { } + + inline + FrameRate::FrameRate (uint num, uint denom) + : IFrac (__ensure_nonzero(num), denom) + { } + + inline + FrameRate::FrameRate (IFrac const& fractionalRate) + : IFrac (__ensure_nonzero(fractionalRate)) + { } + + }} // lib::time + + +namespace util { + + inline bool + isnil (lib::time::Duration const& dur) + { + return 0 == dur; + } + +} #endif diff --git a/src/lib/util.hpp b/src/lib/util.hpp index c8ea896ed..916374a90 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -70,10 +70,10 @@ namespace util { if (0 < (num^den)) return num/den; else - { + { // truncate similar to floor() ldiv_t res = ldiv(num,den); - return (res.rem)? res.quot-1 - : res.quot; + return (res.rem)? res.quot-1 // negative results truncated towards next smaller int + : res.quot; //..unless the division result not truncated at all } } diff --git a/tests/lib/time/quantiser-basics-test.cpp b/tests/lib/time/quantiser-basics-test.cpp index d69e2d1d2..537d51a49 100644 --- a/tests/lib/time/quantiser-basics-test.cpp +++ b/tests/lib/time/quantiser-basics-test.cpp @@ -101,7 +101,7 @@ namespace test{ : FixedFrameQuantiser { TestQuant (int origin=0) - : FixedFrameQuantiser(FSecs(GAVL_TIME_SCALE,3), TimeValue(origin)) + : FixedFrameQuantiser( FrameRate(3,GAVL_TIME_SCALE), TimeValue(origin)) { } int From edc2598f277da2f25305481038ca19e877e2a746 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 9 Jan 2011 05:51:26 +0100 Subject: [PATCH 047/140] cover additional time handling convenience shortcuts --- src/lib/time/timevalue.hpp | 9 +++++++++ tests/lib/time/time-value-test.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 65402882a..dfda0009a 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -337,6 +337,8 @@ namespace time { // standard copy acceptable; + double asDouble() const; + static const FrameRate PAL; static const FrameRate NTSC; @@ -394,6 +396,13 @@ namespace time { : IFrac (__ensure_nonzero(fractionalRate)) { } + inline double + FrameRate::asDouble() const + { + return boost::rational_cast (*this); + } + + }} // lib::time diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index 342697b8f..d504197d5 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -22,6 +22,7 @@ #include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/display.hpp" #include "lib/util.hpp" @@ -38,6 +39,7 @@ using std::cout; using std::endl; using std::string; +using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE; namespace lib { namespace time{ @@ -70,6 +72,7 @@ namespace test{ checkBasicTimeValues (ref); checkMutableTime (ref); checkTimeConveniance (ref); + verify_invalidFramerateProtection(); createOffsets (ref); buildDuration (ref); buildTimeSpan (ref); @@ -194,6 +197,17 @@ namespace test{ } + void + verify_invalidFramerateProtection() + { + VERIFY_ERROR (BOTTOM_VALUE, FrameRate infinite(0) ); + VERIFY_ERROR (BOTTOM_VALUE, FrameRate infinite(0,123) ); + + CHECK (isnil (Duration (0, FrameRate::PAL))); + CHECK (isnil (Duration (0, FrameRate(123)))); + } + + void createOffsets (TimeValue org) { @@ -234,6 +248,16 @@ namespace test{ CHECK (distance > zero); CHECK (distance == backwards.abs()); + Duration unit(50, FrameRate::PAL); + CHECK (Time(2) == unit); // duration of 50 frames at 25fps is... (guess what) + + CHECK (FrameRate::PAL.duration() == Time(FSecs(1,25))); + CHECK (FrameRate::NTSC.duration() == Time(FSecs(1001,3000))); + cout << "NTSC-Framerate = " << FrameRate::NTSC.asDouble() << endl; + + CHECK (zero == Duration::NIL); + CHECK (isnil (Duration::NIL)); + // assign to variable for calculations point = backwards; point *= 2; From c7a887a528963e95386b06024356133176be3d8f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 9 Jan 2011 07:04:20 +0100 Subject: [PATCH 048/140] Quantiser basic unit test pass, including corner case ufff... finally --- src/lib/time.cpp | 4 ++-- src/lib/time.h | 2 +- tests/40components.tests | 2 +- tests/lib/time/quantiser-basics-test.cpp | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib/time.cpp b/src/lib/time.cpp index 5181c5fc9..83ffa4e53 100644 --- a/src/lib/time.cpp +++ b/src/lib/time.cpp @@ -105,13 +105,13 @@ namespace { // implementation helper long -lumiera_quantise_frames (gavl_time_t time, gavl_time_t grid, gavl_time_t origin) +lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) { return calculate_quantisation (time, origin, grid); } gavl_time_t -lumiera_quantise_time (gavl_time_t time, gavl_time_t grid, gavl_time_t origin) +lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) { int64_t count = calculate_quantisation (time, origin, grid); gavl_time_t alignedTime = count * grid; diff --git a/src/lib/time.h b/src/lib/time.h index 5d22908b1..873157e7f 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -102,7 +102,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time); * @warning the resulting value is limited to (Time::Min, Time::MAX) */ long -lumiera_quantise_frames (gavl_time_t time, double grid, gavl_time_t origin); +lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid); /** * Similar to #lumiera_quantise_frames, but returns a grid aligned \em time value diff --git a/tests/40components.tests b/tests/40components.tests index 0afb978db..97bacd935 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -663,7 +663,7 @@ return: 0 END -PLANNED "Quantiser API basics" QuantiserBasics_test < case1.gridAlign( Time (0) )); CHECK (Time::MAX == case1.gridAlign( Time(+1) )); + CHECK (Time::MAX == case1.gridAlign( Time(+2) )); } }; From e2cab1f51204bbf3cb537f5b7ad09d3bafa486e7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 9 Jan 2011 09:49:48 +0100 Subject: [PATCH 049/140] some more smoothing of rough edges --- src/lib/time/timevalue.hpp | 18 ++++++++++++++++-- tests/lib/time/time-value-test.cpp | 22 +++++++++++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index dfda0009a..1a230f605 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -261,6 +261,8 @@ namespace time { + class TimeSpan; + /** * Duration is the internal Lumiera time metric. * It is an absolute (positive) value, but can be @@ -277,6 +279,12 @@ namespace time { : Offset(distance.abs()) { } + explicit + Duration (Time const& timeSpec) + : Offset(Offset(timeSpec).abs()) + { } + + Duration (TimeSpan const& interval); Duration (ulong count, FrameRate const& fps); static const Duration NIL; @@ -307,13 +315,14 @@ namespace time { { } ///////////TODO creating timespans needs to be more convenient.... - operator Duration() const + Duration + duration() const { return dur_; } Time - getEnd() const + end() const { TimeVar startPoint (*this); return (startPoint + dur_); @@ -381,6 +390,11 @@ namespace time { : raw; } + inline + Duration::Duration (TimeSpan const& interval) + : Offset(interval.duration()) + { } + inline FrameRate::FrameRate (uint fps) : IFrac (__ensure_nonzero(fps)) diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index d504197d5..ddc026b9d 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -248,6 +248,13 @@ namespace test{ CHECK (distance > zero); CHECK (distance == backwards.abs()); + Duration len1(Time(23,4,5,6)); + CHECK (len1 == Time(FSecs(23,1000)) + Time(4 + 5*60 + 6*3600)); + + Duration len2(Time(-10)); // negative specs... + CHECK (len2 == Time(10));// + CHECK (len2 > zero); // will be taken absolute + Duration unit(50, FrameRate::PAL); CHECK (Time(2) == unit); // duration of 50 frames at 25fps is... (guess what) @@ -275,7 +282,7 @@ namespace test{ TimeValue zero; TimeValue five(5); - TimeSpan interval (Time(org), Duration(Offset (org,five))); /////////////TODO need more convenient constructors + TimeSpan interval (Time(org), Duration(Offset (org,five))); // the time span behaves like a time CHECK (org == interval); @@ -285,11 +292,16 @@ namespace test{ Duration theLength(interval); CHECK (theLength == Offset(org,five).abs()); - Time endpoint = interval.getEnd(); - CHECK (Offset(interval,endpoint) == Offset(org,five).abs()); + Time endpoint = interval.end(); + TimeSpan successor (endpoint, Duration(Time(2))); - cout << "Interval: " << interval - << " Endpoint: " << endpoint << endl; + CHECK (Offset(interval,endpoint) == Offset(org,five).abs()); + CHECK (Offset(endpoint,successor.end()) == Duration(successor)); + + cout << "Interval-1: " << interval + << " Interval-2: " << successor + << " End point: " << successor.end() + << endl; } }; From debe032f490f5ac68e263ebf56646388c8442f3e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 9 Jan 2011 11:41:08 +0100 Subject: [PATCH 050/140] basic quantisation now working and covered by unit test --- src/lib/time/quantiser.cpp | 17 ++-- src/lib/time/quantiser.hpp | 1 + src/lib/time/timevalue.hpp | 4 +- tests/lib/time/quantiser-basics-test.cpp | 103 ++++++++++++++++------- 4 files changed, 84 insertions(+), 41 deletions(-) diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 567a7d6de..e6d33da42 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -40,15 +40,7 @@ namespace time { namespace { // implementation helpers... - template - inline NUM - __ensure_notZero (NUM n) - { - if (!n) - throw error::Logic ("Impossible to quantise to an zero spaced grid" - , error::LUMIERA_ERROR_BOTTOM_VALUE); - return n; - } + ///////////TODO superfluous?? }//(End) implementation helpers @@ -76,7 +68,12 @@ namespace time { * each interval includes its lower bound and excludes its upper bound.*/ FixedFrameQuantiser::FixedFrameQuantiser (FrameRate const& frames_per_second, TimeValue referencePoint) : origin_(referencePoint) - , raster_(frames_per_second.duration()) + , raster_(__ensure_nonzero(frames_per_second.duration())) + { } + + FixedFrameQuantiser::FixedFrameQuantiser (Duration const& frame_duration, TimeValue referencePoint) + : origin_(referencePoint) + , raster_(__ensure_nonzero(frame_duration)) { } diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 8d79f381e..4f4a38bac 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -107,6 +107,7 @@ namespace time { public: FixedFrameQuantiser (FrameRate const& frames_per_second, TimeValue referencePoint =TimeValue(0)); + FixedFrameQuantiser (Duration const& frame_duration, TimeValue referencePoint =TimeValue(0)); TimeValue gridAlign (TimeValue const&); diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 1a230f605..8a0b1f5fe 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -366,7 +366,7 @@ namespace time { inline NUM __ensure_nonzero (NUM n) { - if (!n) + if (n == 0) throw error::Logic ("Zero spaced grid not allowed" , error::LUMIERA_ERROR_BOTTOM_VALUE); return n; @@ -415,7 +415,7 @@ namespace time { { return boost::rational_cast (*this); } - + diff --git a/tests/lib/time/quantiser-basics-test.cpp b/tests/lib/time/quantiser-basics-test.cpp index c719f404b..bd8c11c72 100644 --- a/tests/lib/time/quantiser-basics-test.cpp +++ b/tests/lib/time/quantiser-basics-test.cpp @@ -22,25 +22,15 @@ #include "lib/test/run.hpp" -//#include "lib/test/test-helper.hpp" -//#include "lib/time/timequant.hpp" +#include "lib/test/test-helper.hpp" #include "lib/time/quantiser.hpp" -//#include "lib/time/display.hpp" #include "lib/util.hpp" +#include -#include -//#include -#include -//#include - -using boost::lexical_cast; +using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE; using util::isnil; -//using util::contains; -//using std::rand; -using std::cout; -using std::endl; +using std::rand; -//using boost::algorithm::join; namespace lib { @@ -52,17 +42,27 @@ namespace test{ const uint MAX_FRAMES = 25*500; const uint MAX_DIRT = 50; - const FSecs F25(1,25); - + const FSecs F25(1,25); // duration of one PAL frame } + /******************************************************** * @test cover the basic Quantiser API. - * This test uses a special quantiser implementation - * with hard coded behaviour to demonstrate and verify - * the usage of a quantiser entity in isolation. + * This test uses a standalone quantiser implementation + * to demonstrate and verify the basic behaviour + * and the usage corner cases of a quantiser. + * + * In this most simple form, a quantiser is defined + * by the time reference point (origin) to use, and + * the frame rate (grid spacing). For each raw time + * value, the quantiser yields a time value aligned + * at the next lower frame bound. Besides that, + * time values are confined to be within + * the interval (Time::MIN, Time::Max) + * + * @see TimeQuantisation_test */ class QuantiserBasics_test : public Test { @@ -84,8 +84,9 @@ namespace test{ uint frames = (rand() % MAX_FRAMES); FSecs dirt = (F25 / (rand() % MAX_DIRT)); - Time rawTime = FSecs(frames, 25) + dirt; - CHECK (Time( frames*F25) <= rawTime); + Time rawTime = dirt + frames*F25; + + CHECK (Time( frames *F25) <= rawTime); CHECK (Time((frames+1)*F25) > rawTime); Time quantTime (fixQ.gridAlign (rawTime)); @@ -149,15 +150,59 @@ namespace test{ void coverQuantisationCornerCases() { + // origin at lower end of the time range FixedFrameQuantiser case1 (1, Time::MIN); - CHECK (Time(0) == case1.gridAlign(Time::MIN)); - CHECK (Time(0) == case1.gridAlign(Time::MIN + TimeValue(1) )); - CHECK (Time(1) == case1.gridAlign(Time::MIN + Time(1) )); - CHECK (Time::MAX - Time(1) > case1.gridAlign( Time(-1) )); - CHECK (Time::MAX - Time(1) <= case1.gridAlign( Time (0) )); - CHECK (Time::MAX > case1.gridAlign( Time (0) )); - CHECK (Time::MAX == case1.gridAlign( Time(+1) )); - CHECK (Time::MAX == case1.gridAlign( Time(+2) )); + CHECK (Time(0) == case1.gridAlign(Time::MIN )); + CHECK (Time(0) == case1.gridAlign(Time::MIN +TimeValue(1) )); + CHECK (Time(1) == case1.gridAlign(Time::MIN +Time(1) )); + CHECK (Time::MAX -Time(1) > case1.gridAlign( Time(-1) )); + CHECK (Time::MAX -Time(1) <= case1.gridAlign( Time (0) )); + CHECK (Time::MAX > case1.gridAlign( Time (0) )); + CHECK (Time::MAX == case1.gridAlign( Time(+1) )); + CHECK (Time::MAX == case1.gridAlign( Time(+2) )); + + // origin at upper end of the time range + FixedFrameQuantiser case2 (1, Time::MAX); + CHECK (Time( 0) == case2.gridAlign(Time::MAX )); + CHECK (Time(-1) == case2.gridAlign(Time::MAX -TimeValue(1) )); // note: next lower frame + CHECK (Time(-1) == case2.gridAlign(Time::MAX -Time(1) )); // i.e. the same as a whole frame down + CHECK (Time::MIN +Time(1) < case2.gridAlign( Time(+2) )); + CHECK (Time::MIN +Time(1) >= case2.gridAlign( Time(+1) )); + CHECK (Time::MIN < case2.gridAlign( Time(+1) )); + CHECK (Time::MIN == case2.gridAlign( Time( 0) )); // note: because of downward truncating, + CHECK (Time::MIN == case2.gridAlign( Time(-1) )); // resulting values will already exceed + CHECK (Time::MIN == case2.gridAlign( Time(-2) )); // allowed range and thus will be clipped + + // maximum frame size is half the time range + Duration hugeFrame(Offset(Time::MAX)); + FixedFrameQuantiser case3 (hugeFrame); + CHECK (Time::MIN == case3.gridAlign(Time::MIN )); + CHECK (Time::MIN == case3.gridAlign(Time::MIN +TimeValue(1) )); + CHECK (Time::MIN == case3.gridAlign( Time(-1) )); + CHECK (Time(0) == case3.gridAlign( Time( 0) )); + CHECK (Time(0) == case3.gridAlign( Time(+1) )); + CHECK (Time(0) == case3.gridAlign(Time::MAX -TimeValue(1) )); + CHECK (Time::MAX == case3.gridAlign(Time::MAX )); + + // now displacing this grid by +1sec.... + FixedFrameQuantiser case4 (hugeFrame, Time(1)); + CHECK (Time::MIN == case4.gridAlign(Time::MIN )); + CHECK (Time::MIN == case4.gridAlign(Time::MIN +TimeValue(1) )); // clipped... + CHECK (Time::MIN == case4.gridAlign(Time::MIN +Time(1) )); // but now exact (unclipped) + CHECK (Time::MIN == case4.gridAlign( Time(-1) )); + CHECK (Time::MIN == case4.gridAlign( Time( 0) )); + CHECK (Time(0) == case4.gridAlign( Time(+1) )); //.....now exactly the frame number zero + CHECK (Time(0) == case4.gridAlign(Time::MAX -TimeValue(1) )); + CHECK (Time(0) == case4.gridAlign(Time::MAX )); //.......still truncated down to frame #0 + + // larger frames aren't possible + Duration not_really_larger(Time(10000) + hugeFrame); + CHECK (hugeFrame == not_really_larger); + + // frame sizes below the time micro grid get trapped + long subAtomic = 2*GAVL_TIME_SCALE; // too small for this universe... + VERIFY_ERROR (BOTTOM_VALUE, FixedFrameQuantiser quark(subAtomic) ); + VERIFY_ERROR (BOTTOM_VALUE, FixedFrameQuantiser quark(Duration (FSecs (1,subAtomic))) ); } }; From a1f2a604274cfb5020d9fea4d20b80ec3ce4af4d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 13 Jan 2011 23:56:52 +0100 Subject: [PATCH 051/140] WIP design draft regarding the interplay of quantisation and timecode --- src/lib/time/digxel.hpp | 6 +++++ src/lib/time/formats.hpp | 37 ++++++++++++++---------------- src/lib/time/quantiser.cpp | 21 +++++++++++++++-- src/lib/time/quantiser.hpp | 24 +++++++++++++++++++ src/lib/time/timecode.cpp | 17 +++++++++----- src/lib/time/timecode.hpp | 26 +++++++++++++++++---- src/lib/time/timequant.hpp | 28 +++++++++++++++++++++-- wiki/renderengine.html | 47 ++++++++++++++++++++++++++++++++------ 8 files changed, 164 insertions(+), 42 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index ac5f5f8f8..4a359f462 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -169,6 +169,11 @@ namespace time { HexaFormatter() : PrintfFormatter("%02X") { } }; + struct CountFormatter + : PrintfFormatter + { + CountFormatter() : PrintfFormatter("%04l") { } + }; } //(End) digxel configuration namespace @@ -268,6 +273,7 @@ namespace time { /* == predefined Digxel configurations == */ typedef Digxel< int, digxel::SexaFormatter> SexaDigit; ///< for displaying time components (sexagesimal) typedef Digxel HexaDigit; ///< for displaying a hex byte + typedef Digxel CountVal; ///< for displaying a hex byte }} // lib::time diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index ffb46a868..5478e8c21 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -34,6 +34,18 @@ namespace lib { namespace time { + // ====== forward declarations of concrete Timecode types + + class FrameNr; + class SmpteTC; + class HmsTC; + class Secs; + + + class Quantiser; // API for grid aligning + + + /** * descriptor for a time representation format (ABC). * A time format describes how to specify time; it allows @@ -51,6 +63,7 @@ namespace time { virtual ~Format(); }; + namespace format { /** @@ -64,7 +77,7 @@ namespace time { class Frames : public Format { - + static void rebuild (FrameNr&, Quantiser const&, TimeValue const&); }; @@ -77,7 +90,7 @@ namespace time { class Smpte : public Format { - + static void rebuild (SmpteTC&, Quantiser const&); }; @@ -91,7 +104,7 @@ namespace time { class Hms : public Format { - + static void rebuild (HmsTC&, Quantiser const&); }; @@ -107,28 +120,12 @@ namespace time { class Seconds : public Format { - + static void rebuild (Secs&, Quantiser const&); }; - /* ==== predefined constants for specifying the format to use ==== */ - - static const Seconds SECONDS; - static const Frames FRAMES; - static const Smpte SMPTE; - static const Hms HMS; - } - // ====== forward declarationss of concrete Timecode types - - class FrameNr; - class SmpteTC; - class HmsTC; - class Secs; - - - namespace format { template struct Traits; diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index e6d33da42..9879eac47 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -57,7 +57,8 @@ namespace time { /** */ QuTime::QuTime (TimeValue raw, Quantiser const& quantisation_to_use) - : Time(raw) /////////////////////////////////////////////////TODO fetch quantiser + : Time(raw) + , quantiser_(&quantisation_to_use) { } @@ -87,7 +88,7 @@ namespace time { * containing the rawTime, relative to the origin * of the time scale used by this quantiser. * @warning returned time values are limited by the - * range of an 64bit integer + * valid range of lumiera::Time * @see #lumiera_quantise_time */ TimeValue @@ -97,6 +98,22 @@ namespace time { } + /** grid quantisation (alignment). + * Determine the next lower grid interval start point, + * using a simple constant spaced time grid defined by + * origin and framerate stored within this quantiser. + * @warning returned frame count might exceed the valid + * range when converting back into a TimeValue. + * @see #lumiera_quantise_frames + */ + long + FixedFrameQuantiser::gridPoint (TimeValue const& rawTime) + { + return lumiera_quantise_frames (_raw(rawTime), _raw(origin_), _raw(raster_)); + } + + + LUMIERA_ERROR_DEFINE (UNKNOWN_GRID, "referring to an undefined grid or scale in value quantisation"); diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 4f4a38bac..a5b8273eb 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -85,10 +85,33 @@ namespace time { iterator getSupportedFormats() const; virtual TimeValue gridAlign (TimeValue const& raw) =0; + virtual long gridPoint (TimeValue const& raw) =0; }; + /** + * smart reference + * for accessing an existing quantiser + */ + class QuantiserRef + { + size_t hashID_; + + public: + QuantiserRef (Quantiser const&); + + // using standard copy; + + + Quantiser const& + operator-> () + { + UNIMPLEMENTED ("how to manage and address the existing quantisers"); + } + }; + + /** * Simple stand-alone Quantiser implementation for debugging and test. @@ -111,6 +134,7 @@ namespace time { TimeValue gridAlign (TimeValue const&); + long gridPoint (TimeValue const&); }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 37e00a9ea..97cffcf7f 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -39,12 +39,17 @@ namespace time { Format::~Format() { } // emit VTable here.... TCode::~TCode() { } - - /** */ - FrameNr::FrameNr (QuTime const& quantisedTime) - : FACTOR_TODO (1,25) - , nr_(TimeVar(quantisedTime) / (25*GAVL_TIME_SCALE)) - { } /////////////////////////////TODO temporary bullshit (of course that is the job of the quantiser) + namespace format { + + /** build up a frame count + * by quantising the given time value + */ + void + Frames::rebuild (FrameNr& framecnt, Quantiser const& quantiser, TimeValue const& rawTime) + { + framecnt.setValueRaw(quantiser.gridPoint (rawTime)); + } + } /** */ diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 05b5fb3ff..793b07aaf 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -26,6 +26,7 @@ #include "lib/time/timevalue.hpp" #include "lib/time/formats.hpp" +#include "lib/time/digxel.hpp" #include "lib/symbol.hpp" //#include @@ -49,6 +50,7 @@ namespace time { */ class TCode { + QuantiserRef qID_; public: virtual ~TCode(); @@ -58,6 +60,10 @@ namespace time { Time getTime() const { return Time(value()); } protected: + TCode (QuantiserRef const& qID) + : qID_(qID) + { } + virtual string show() const =0; virtual Literal tcID() const =0; virtual TimeValue value() const =0; @@ -75,18 +81,22 @@ namespace time { */ class FrameNr : public TCode + , CountVal { - FSecs FACTOR_TODO; /////////////////////////////TODO temporary bullshit (of course that is the job of the quantiser) - - long nr_; - string show() const { return lexical_cast(nr_)+"fr"; } Literal tcID() const { return "Frame-count"; } TimeValue value() const { return Time(FACTOR_TODO * nr_); } public: - FrameNr (QuTime const& quantisedTime); + typedef format::Frames Format; + + FrameNr (QuTime const& quantisedTime) + : TCode(quantisedTime) + , CountVal() + { + quantisedTime.castInto (*this); + } operator long() const { return nr_; } }; @@ -106,6 +116,8 @@ namespace time { virtual TimeValue value() const { return tpoint_; } public: + typedef format::Smpte Format; + SmpteTC (QuTime const& quantisedTime); int getSecs () const; @@ -129,6 +141,8 @@ namespace time { virtual TimeValue value() const { return tpoint_; } public: + typedef format::Hms Format; + HmsTC (QuTime const& quantisedTime); double getMillis () const; @@ -152,6 +166,8 @@ namespace time { virtual TimeValue value() const { return Time(sec_); } public: + typedef format::Seconds Format; + Secs (QuTime const& quantisedTime); operator FSecs() const; diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp index 89c5db63f..6978bc63a 100644 --- a/src/lib/time/timequant.hpp +++ b/src/lib/time/timequant.hpp @@ -47,22 +47,36 @@ namespace time { class QuTime : public Time { + const Quantiser *quantiser_; public: QuTime (TimeValue raw, Symbol gridID); QuTime (TimeValue raw, Quantiser const& quantisation_to_use); + operator QuantiserRef() const; + template bool supports() const; template typename format::Traits::TimeCode formatAs() const; + + template + void + castInto (TC& timecode) const; }; /* == implementation == */ + + QuTime::operator QuantiserRef() const + { + ASSERT (quantiser_); + return QuantiserRef(*quantiser_); + } + template bool QuTime::supports() const @@ -75,8 +89,18 @@ namespace time { typename format::Traits::TimeCode QuTime::formatAs() const { - typedef typename format::Traits::TimeCode TimeCode; - return TimeCode(*this); + typedef typename format::Traits::TimeCode TC; + return TC(*this); + } + + + template + void + QuTime::castInto (TC& timecode) const + { + typedef typename TC::Format Format; + + Format::rebuild (timecode, *quantiser_); } diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 550b53eaa..83e9cff48 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -540,7 +540,7 @@ In a more elaborate scheme, the advised entity could provide a signal to be invo &rarr; AdviceImplementation
-
+
[<img[Advice solution|uml/fig141573.png]]
 
 
@@ -601,7 +601,7 @@ Thus the second approach looks favourable, but we should //note the fact that it
 The advice system is (hopefully) written such as not to be corrupted in case an exception is thrown. Adding new requests, setting advice data on a provision and any binding change might fail due to exhausted memory. The advice system remains operational in this case, but the usual reaction would be //subsystem shutdown,// because the Advice facility typically is used in a very low-level manner, assuming it //just works.// As far as I can see, the other mutation operations can't throw.
 
 The individual operations on the interface objects are //deliberately not thread-safe.// The general assumption is that {{{advice::Request}}} and {{{advice::Provision}}} will be used in a safe environment and not be accessed or modified concurrently. An notable exception to this rule is accessing Advice: as this just includes checking and dereferentiating a pointer, it might be done concurrently. But note, //the advice system does nothing to ensure visibility of the solution within a separate thread.// If this thread still has the old pointer value in his local cache, it won't pick up the new solution. In case the old solution got retracted, this even might cause access to already released objects. You have been warned. So it's probably a good idea to ensure a read barrier happens somewhere in the enclosing usage context prior to picking up a possibly changed advice solution concurrently.
-{{red{TODO}}}: the underlying operations on the embedded global {{{advice::Index}}} obviously need to be protected by locking the whole index table on each mutation, which also ensures a memory barrier and thus propagates changed solutions.
+''Note'': the underlying operations on the embedded global {{{advice::Index}}} obviously need to be protected by locking the whole index table on each mutation, which also ensures a memory barrier and thus propagates changed solutions. While this settles the problem for the moment, we might be forced into a more fine grained locking due to contention prolems later on...
 
 !!!index datastructure
 It is clear by now that the implementation datastrucutre has to serve as a kind of //reference count.// Within this datastructure, any constructed advice solution needs to be reflected somehow, to prevent us from discarding an advice provision still accessible. Allowing lock-free access to the advice solution (planned feature) adds an special twist, because in this case we can't even tell for sure if an overwritten old solution is actually gone (or if its still referred from some thread's cached memeory). This could be addressed with an transactional approach (which might be good anyway) &mdash; but I tend to leave this special concern asside for now.
@@ -2092,7 +2092,7 @@ Besides routing to a global pipe, wiring plugs can also connect to the source po
 Finally, this example shows an ''automation'' data set controlling some parameter of an effect contained in one of the global pipes. From the effect's POV, the automation is simply a ParamProvider, i.e a function yielding a scalar value over time. The automation data set may be implemented as a bézier curve, or by a mathematical function (e.g. sine or fractal pseudo random) or by some captured and interpolated data values. Interestingly, in this example the automation data set has been placed relatively to the meta clip (albeit on another track), thus it will follow and adjust when the latter is moved.
 
-
+
This wiki page is the entry point to detail notes covering some technical decisions, details and problems encountered in the course of the implementation of the Lumiera Renderengine, the Builder and the related parts.
 
 * [[Packages, Interfaces and Namespaces|InterfaceNamespaces]]
@@ -2103,7 +2103,7 @@ Finally, this example shows an ''automation'' data set controlling some paramete
 * [[Editing Operations|EditingOperations]]
 * [[Handling of the current Session|CurrentSession]]
 * [[collecting Ideas for Implementation Guidelines|ImplementationGuidelines]]
-* [[using the Visitor pattern?|VisitorUse]] &mdash; resulting in [[»Visiting-Tool« library implementation|VisitingToolImpl]]
+* [[using the Visitor pattern?|VisitorUse]] -- resulting in [[»Visiting-Tool« library implementation|VisitingToolImpl]]
 * [[Handling of Tracks and render Pipes in the session|TrackPipeSequence]]. [[Handling of Tracks|TrackHandling]] and [[Pipes|PipeHandling]]
 * [[getting default configured|DefaultsManagement]] Objects relying on [[rule-based Configuration Queries|ConfigRules]]
 * [[integrating the Config Query system|ConfigQueryIntegration]]
@@ -2123,6 +2123,7 @@ Finally, this example shows an ''automation'' data set controlling some paramete
 * working out a [[Wiring concept|Wiring]] and the foundations of OutputManagement
 * shaping the foundations of the [[player subsystem|Player]]
 * detail considerations regarding [[time and time quantisation|TimeQuant]]
+* [[Timecode]] -- especially the link of [[TC formats and quantisation|TimecodeFormat]]
 * designing how to [[build|BuildFixture]] the [[Fixture]] (...{{red{WIP}}}...)
 
 
@@ -6617,7 +6618,7 @@ Thus no server and no network connection is needed. Simply open the file in your * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
-
+
The term &raquo;Time&laquo; spans a variety of vastly different entities. Within a NLE we get to deal with various //flavours of time values.//
 ;continuous time
 :without any additional assumptions, ''points in time'' can be specified with arbitrary precision.
@@ -6675,11 +6676,11 @@ For Lumiera, the static typing approach is of limited value -- it excels when va
 
 At the level of individual timecode formats, we're lacking a common denominator; thus it is preferrable to work with different concrete timecode classes through //generic programming.// This way, each timecode format can expose operations specific only to the given format. Especially, different timecode formats expose different //component fields,// modelled by the generic ''Digxel'' concept. There is a common baseclass ~TCode though, which can be used for //type erasure.//
 &rarr; more on [[usage situations|TimeUsage]]
+&rarr; Timecode [[format and quantisation|TimecodeFormat]]
 &rarr; Quantiser [[implementation details|QuantiserImpl]]
-
 
-
+
the following collection of usage situations helps to shape the details of the time values and time quantisation design. &rarr; see also  [[time quantisation|TimeQuant]]
 
 ;time position of an object
@@ -6725,6 +6726,8 @@ Note that the ''display window might be treated as just an independent instance
 !substantial problems to be solved
 * how to align multiple grids
 * how to integrate modifications of quantised values.
+* how to isolate the Time/Quantisation part from the grid MetaAsset in the session
+* how to design the relation of Timecode, Timecode formatting and Quantisation &rarr; [[more here|TimecodeFormat]]
 
 The problem with modification of quantised values highlights an inner contratiction or conflicting goals
 * the whole system should fit in naturally and just feel like using raw time values
@@ -6741,6 +6744,36 @@ Question is: how fine grained and configurable needs this to be?
 * for example, when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
 * likely we need a "nudge by unit(s)"
 
+
+
+
+
The handling of [[Timecode]] is closely related to [[time representation and quantisation|TimeQuant]]. In fact, these two topics blend into one another. Time will be quantised into a //grid,// but this grid only makes sense when linked to a externally relevant meaning and representation, which is the Timecode. But a timecode value also denotes a specific point in time -- performing operations on a timecode is equivalent to manipulating a quantised time value.
+
+!Problem of dependencies
+The general design of time and time quantisation in Lumiera requires us to do the actual quantisation as late as possible. The relation of the time-like entities creates a //one way route:// starting from the ''internal time'', which is contiguous and definite, but completely opaque for the client, the usage proceeds to a ''quantised time value'', requiring the specification of a concrete ''time grid''. Now, the possible ''timecode formats'' can be determined, and commiting to one specific timecode format finally allows to get an explicit, number-like value, e.g. the frame count, or the seconds part of a SMPTE Timecode.
+
+In this handling sequence, the ~QuTime with the embedded link to a ''quantiser'' plays the role of an coordinating hub.
+* a quantised time element depends on a concrete quantiser, which might be fetched by symbolic ID.
+* the quantiser draws upon a configured collection of concrete timecode ''formats''.
+* only the concrete format knows how to build the components of a value representation in that format, but in doing so, has to rely on the primitives exposed by the quantiser.
+* the concrete individual ''timecode value'' is comprised of several value-like components called ''Digxel'' (generalised digits)
+* the timecode value can be //(re)-built,// assumed there is a concrete quantised time and a format and a quantiser
+
+!Usage and operations
+Timecode //is a value.// It has an explicit and distinct structure, and the component parts can be accessed as plain numeric values, or in a formatted string representation. To this end, these values need to be (re)-built. This operation -- building the value(s) -- is what effectively //materialises// the quantisation: At this point, the actual rounding and truncating operation is performed.
+
+But timecode values can also be //mutated.// In this respect they differ from a plain ~TimeValue, which is immutable like a number.
+* the individual components (digxels) can be assigned, causing rebuilding of the timecode
+* the value as a whole can be incremented, decremented or be offset.
+
+And last but not least, it is possible to get a new ~TimeValue, reflecting the current (quantised) time of this timecode. This closes the circle of time handling and quantisation; actually this is the usual way to enter a new time value into the system, by starting out from an explicitly specified grid aligned timecode value.
+
+!!{{red{WIP 1/11}}}design tasks
+* @@color:green;✔@@ find out about the connection to the MetaAsset &rarr; [[Advice]]
+* determine the primitives which need to be on the //real quantiser API.//
+* find out how a format can address the individual components it's comprised of
+* decide how a concrete TC value can refer to his quantiser
+* maybe coin a //value handle// -- to tie the three required parts together
 
From 457d4fb7c4a3920b35c1de5814035ee1ea1b2cb6 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 14 Jan 2011 05:33:50 +0100 Subject: [PATCH 052/140] define or stub to get it to compile; add function time-from gridnr --- src/lib/time.cpp | 7 ++++++ src/lib/time.h | 9 ++++++++ src/lib/time/digxel.hpp | 2 +- src/lib/time/formats.hpp | 47 ++++++++++++++++++++++++++++---------- src/lib/time/quantiser.cpp | 18 ++++++++++++--- src/lib/time/quantiser.hpp | 30 +++++------------------- src/lib/time/timecode.cpp | 32 +++++++++++++++++++++++--- src/lib/time/timecode.hpp | 19 +++++++-------- src/lib/time/timequant.hpp | 11 +++++---- 9 files changed, 116 insertions(+), 59 deletions(-) diff --git a/src/lib/time.cpp b/src/lib/time.cpp index 83ffa4e53..16acf2622 100644 --- a/src/lib/time.cpp +++ b/src/lib/time.cpp @@ -118,6 +118,13 @@ lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) return alignedTime; } +gavl_time_t +lumiera_time_of_gridpoint (long nr, gavl_time_t origin, gavl_time_t grid) +{ + gavl_time_t offset = nr * grid; + return origin + offset; +} + gavl_time_t lumiera_build_time(long millis, uint secs, uint mins, uint hours) diff --git a/src/lib/time.h b/src/lib/time.h index 873157e7f..6f9974dab 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -116,6 +116,15 @@ lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid) gavl_time_t lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid); +/** + * Calculate time of a grid point (frame start) + * @param nr index number of the grid point (0 is at origin) + * @param grid spacing of the grid intervals, measured in GAVL_TIME_SCALE + * @return time point (frame start) on the Lumiera internal time scale + */ +gavl_time_t +lumiera_time_of_gridpoint (long nr, gavl_time_t origin, gavl_time_t grid); + /** * Builds a time value by summing up the given components. */ diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 4a359f462..6ade5c742 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -273,7 +273,7 @@ namespace time { /* == predefined Digxel configurations == */ typedef Digxel< int, digxel::SexaFormatter> SexaDigit; ///< for displaying time components (sexagesimal) typedef Digxel HexaDigit; ///< for displaying a hex byte - typedef Digxel CountVal; ///< for displaying a hex byte + typedef Digxel CountVal; ///< for displaying a hex byte }} // lib::time diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index 5478e8c21..ca92b048c 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -44,6 +44,25 @@ namespace time { class Quantiser; // API for grid aligning + /** + * smart reference for accessing an existing quantiser + */ + class QuantiserRef + { + size_t hashID_; + + public: + QuantiserRef (Quantiser const&); + + // using standard copy; + + + const Quantiser * + operator-> () + { + UNIMPLEMENTED ("how to manage and address the existing quantisers"); + } + }; /** @@ -74,10 +93,11 @@ namespace time { * these frame counts. As with any timecode, the * underlying framerate/quantisation remains implicit. */ - class Frames - : public Format + struct Frames + : Format { - static void rebuild (FrameNr&, Quantiser const&, TimeValue const&); + static void rebuild (FrameNr&, Quantiser const&, TimeValue const&); + static TimeValue evaluate (FrameNr const&, QuantiserRef); }; @@ -87,10 +107,11 @@ namespace time { * by specifying time as hour-minute-second plus the * frame number within the actual second. */ - class Smpte - : public Format + struct Smpte + : Format { - static void rebuild (SmpteTC&, Quantiser const&); + static void rebuild (SmpteTC&, Quantiser const&); + static TimeValue evaluate (SmpteTC const&, QuantiserRef); }; @@ -101,10 +122,11 @@ namespace time { * entity in time. HMS-Timecode is similar to SMPTE, but uses a * floating point milliseconds value instead of the frame count */ - class Hms - : public Format + struct Hms + : Format { - static void rebuild (HmsTC&, Quantiser const&); + static void rebuild (HmsTC&, Quantiser const&); + static TimeValue evaluate (HmsTC const&, QuantiserRef); }; @@ -117,10 +139,11 @@ namespace time { * @note Seconds is implemented as rational number and thus uses * decimal format, not the usual sexagesimal time format */ - class Seconds - : public Format + struct Seconds + : Format { - static void rebuild (Secs&, Quantiser const&); + static void rebuild (Secs&, Quantiser const&); + static TimeValue evaluate (Secs const&, QuantiserRef); }; diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 9879eac47..9b81d40a2 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -92,7 +92,7 @@ namespace time { * @see #lumiera_quantise_time */ TimeValue - FixedFrameQuantiser::gridAlign (TimeValue const& rawTime) + FixedFrameQuantiser::gridAlign (TimeValue const& rawTime) const { return TimeValue (lumiera_quantise_time (_raw(rawTime), _raw(origin_), _raw(raster_))); } @@ -107,11 +107,23 @@ namespace time { * @see #lumiera_quantise_frames */ long - FixedFrameQuantiser::gridPoint (TimeValue const& rawTime) + FixedFrameQuantiser::gridPoint (TimeValue const& rawTime) const { return lumiera_quantise_frames (_raw(rawTime), _raw(origin_), _raw(raster_)); } - + + + /** calculate time value of a grid interval (frame) start point + * @return time point measured in Lumiera internal time + * @warning returned time values are limited by the + * valid range of lumiera::Time + */ + TimeValue + FixedFrameQuantiser::timeOf (long gridPoint) const + { + return TimeValue (lumiera_time_of_gridpoint (gridPoint, _raw(origin_), _raw(raster_))); + } + diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index a5b8273eb..bbc88db70 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -84,32 +84,13 @@ namespace time { typedef _Iter iterator; iterator getSupportedFormats() const; - virtual TimeValue gridAlign (TimeValue const& raw) =0; - virtual long gridPoint (TimeValue const& raw) =0; + virtual TimeValue gridAlign (TimeValue const& raw) const =0; + virtual long gridPoint (TimeValue const& raw) const =0; + virtual TimeValue timeOf (long gridPoint) const =0; }; - /** - * smart reference - * for accessing an existing quantiser - */ - class QuantiserRef - { - size_t hashID_; - - public: - QuantiserRef (Quantiser const&); - - // using standard copy; - - - Quantiser const& - operator-> () - { - UNIMPLEMENTED ("how to manage and address the existing quantisers"); - } - }; @@ -133,8 +114,9 @@ namespace time { FixedFrameQuantiser (Duration const& frame_duration, TimeValue referencePoint =TimeValue(0)); - TimeValue gridAlign (TimeValue const&); - long gridPoint (TimeValue const&); + TimeValue gridAlign (TimeValue const&) const; + long gridPoint (TimeValue const&) const; + TimeValue timeOf (long gridPoint) const; }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 97cffcf7f..978739635 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -49,24 +49,50 @@ namespace time { { framecnt.setValueRaw(quantiser.gridPoint (rawTime)); } + + /** calculate the time point denoted by this frame count */ + TimeValue + Frames::evaluate (FrameNr const& framecnt, QuantiserRef quantiser) + { + return quantiser->timeOf (framecnt); + } + } + /** */ + QuantiserRef::QuantiserRef (Quantiser const&) + : hashID_(123) /////////////////////////////////////////////////TODO + { } + + + /** */ + FrameNr::FrameNr (QuTime const& quantisedTime) + : TCode(quantisedTime) + , CountVal() + { + quantisedTime.castInto (*this); + } + + /** */ SmpteTC::SmpteTC (QuTime const& quantisedTime) - : tpoint_(quantisedTime) /////////////////////////////TODO eternal bullshit + : TCode(quantisedTime) +// : tpoint_(quantisedTime) /////////////////////////////TODO eternal bullshit { } /** */ HmsTC::HmsTC (QuTime const& quantisedTime) - : tpoint_(quantisedTime) /////////////////////////////TODO bullshit + : TCode(quantisedTime) +// : tpoint_(quantisedTime) /////////////////////////////TODO bullshit { } /** */ Secs::Secs (QuTime const& quantisedTime) - : sec_(TimeVar(quantisedTime) / GAVL_TIME_SCALE) /////////////TODO bullshit + : TCode(quantisedTime) +// : sec_(TimeVar(quantisedTime) / GAVL_TIME_SCALE) /////////////TODO bullshit { } diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 793b07aaf..fdfe5f303 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -50,7 +50,6 @@ namespace time { */ class TCode { - QuantiserRef qID_; public: virtual ~TCode(); @@ -67,6 +66,9 @@ namespace time { virtual string show() const =0; virtual Literal tcID() const =0; virtual TimeValue value() const =0; + + protected: + QuantiserRef qID_; }; @@ -81,24 +83,19 @@ namespace time { */ class FrameNr : public TCode - , CountVal + , public CountVal { - string show() const { return lexical_cast(nr_)+"fr"; } + string show() const { return string(show())+"fr"; } Literal tcID() const { return "Frame-count"; } - TimeValue value() const { return Time(FACTOR_TODO * nr_); } + TimeValue value() const { return Format::evaluate (*this, qID_); } public: typedef format::Frames Format; - FrameNr (QuTime const& quantisedTime) - : TCode(quantisedTime) - , CountVal() - { - quantisedTime.castInto (*this); - } + FrameNr (QuTime const& quantisedTime); - operator long() const { return nr_; } + // CountVal implicitly convertible to long }; diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp index 6978bc63a..be5e82b71 100644 --- a/src/lib/time/timequant.hpp +++ b/src/lib/time/timequant.hpp @@ -70,7 +70,8 @@ namespace time { /* == implementation == */ - + + inline QuTime::operator QuantiserRef() const { ASSERT (quantiser_); @@ -78,7 +79,7 @@ namespace time { } template - bool + inline bool QuTime::supports() const { return false; ////////////////TODO; @@ -86,7 +87,7 @@ namespace time { template - typename format::Traits::TimeCode + inline typename format::Traits::TimeCode QuTime::formatAs() const { typedef typename format::Traits::TimeCode TC; @@ -95,12 +96,12 @@ namespace time { template - void + inline void QuTime::castInto (TC& timecode) const { typedef typename TC::Format Format; - Format::rebuild (timecode, *quantiser_); + Format::rebuild (timecode, *quantiser_, TimeValue(*this)); } From c01d6dbd0b62952713ee0754e2df05dcb3989a25 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Mon, 27 Dec 2010 07:16:02 +0100 Subject: [PATCH 053/140] Move Track enabled/locked state to GUI model. --- src/gui/model/track.cpp | 42 +++++- src/gui/model/track.hpp | 136 ++++++++++++++------ src/gui/widgets/timeline/timeline-track.cpp | 119 ++++++++++------- src/gui/widgets/timeline/timeline-track.hpp | 74 +++++++---- 4 files changed, 262 insertions(+), 109 deletions(-) diff --git a/src/gui/model/track.cpp b/src/gui/model/track.cpp index 6d5cf2a65..b8588ed49 100644 --- a/src/gui/model/track.cpp +++ b/src/gui/model/track.cpp @@ -33,6 +33,8 @@ namespace model { const list< shared_ptr > Track::NoChildren; Track::Track() + : enabled(true), + locked(false) { } @@ -43,12 +45,38 @@ Track::get_child_tracks() const return Track::NoChildren; } +bool +Track::getEnabled() const +{ + return enabled; +} + +bool +Track::getLocked() const +{ + return locked; +} + const string Track::get_name() const { return name; } +void +Track::setEnabled(bool enabled) +{ + this->enabled = enabled; + enabledChangedSignal.emit(enabled); +} + +void +Track::setLocked(bool locked) +{ + this->locked = locked; + lockedChangedSignal.emit(locked); +} + void Track::set_name(const string &name) { @@ -75,8 +103,20 @@ Track::find_descendant_track_parent( return shared_ptr(); } +sigc::signal +Track::signalEnabledChanged() const +{ + return enabledChangedSignal; +} + +sigc::signal +Track::signalLockedChanged() const +{ + return lockedChangedSignal; +} + sigc::signal -Track::signal_name_changed() const +Track::signalNameChanged() const { return nameChangedSignal; } diff --git a/src/gui/model/track.hpp b/src/gui/model/track.hpp index 7517219a7..4aebc95ee 100644 --- a/src/gui/model/track.hpp +++ b/src/gui/model/track.hpp @@ -48,27 +48,56 @@ protected: public: + /** + * Returns true if this track can own any child tracks. + **/ + virtual bool + can_host_children () const; + /** * Gets the list of child tracks. **/ virtual const std::list< boost::shared_ptr >& - get_child_tracks() const; + get_child_tracks () const; + /** + * Gets the enabled status of this track, i.e. if the track is to be rendered. + **/ + bool + getEnabled () const; + + /** + * Gets the locked status of this track, i.e. if the track can be edited. + **/ + bool + getLocked () const; + /** * Gets the name of this track. **/ - const std::string get_name() const; - + const std::string + get_name () const; + + /** + * Sets the enabled status of this track, i.e. if the track is to be rendered. + * @param[in] name The new enabled status. + **/ + void + setEnabled (bool enabled); + + /** + * Gets the locked status of this track, i.e. if the track can be edited. + * @param[in] name The new locked status. + **/ + void + setLocked (bool locked); + /** * Sets the name of this track. * @param[in] name The new name to set this track to. **/ - void set_name(const std::string &name); - - /** - * Returns true if this track can own any child tracks. - **/ - virtual bool can_host_children() const; + void + set_name (const std::string &name); /** * A utility function that attempts to find the parent of a track by @@ -78,59 +107,94 @@ public: * shared_ptr if none was found. **/ virtual boost::shared_ptr - find_descendant_track_parent(boost::shared_ptr child); + find_descendant_track_parent (boost::shared_ptr child); -public: + /** + * A signal which fires when the enabled status changes. + * @return Returns the signal. The signal sends the new name for the + * track. + **/ + sigc::signal + signalEnabledChanged () const; + + /** + * A signal which fires when the locked status changes changes. + * @return Returns the signal. The signal sends the new name for the + * track. + **/ + sigc::signal + signalLockedChanged () const; /** * A signal which fires when the name changes. * @return Returns the signal. The signal sends the new name for the * track. **/ - sigc::signal signal_name_changed() const; + sigc::signal + signalNameChanged () const; -public: /** * A debugging helper function that prints this track, and all it's * child tracks in a human-readable form. * @return Returns the human readable string. **/ - std::string print_branch(); + std::string + print_branch (); /** * A pure-virtual function which is the base of functions that print * this track in human readable form. * @return Returns the human readable string. **/ - virtual std::string print_track() = 0; + virtual std::string + print_track () = 0; -protected: - /** - * The internal implementation of print_branch. - * @param indentation The level of recursion into the tree. This value - * is used to specify the width of indentation to print with. - * @return Returns the human readable string. - **/ - std::string print_branch_recursive(const unsigned int indentation); - -private: - //----- Data -----// - /** - * The name of this track. - **/ - std::string name; - - /** - * A signal which fires when the name changes. - **/ - sigc::signal nameChangedSignal; - protected: /** * An object used internally as a return value for when there's no * children. **/ static const std::list< boost::shared_ptr > NoChildren; + + /** + * The internal implementation of print_branch. + * @param indentation The level of recursion into the tree. This value + * is used to specify the width of indentation to print with. + * @return Returns the human readable string. + **/ + std::string + print_branch_recursive (const unsigned int indentation); + +private: + /** + * The name of this track. + **/ + std::string name; + + /** + * True if this track is enabled, i.e. will not be rendered. + */ + bool enabled; + + /** + * True if this track is locked, i.e. cannot be edited. + */ + bool locked; + + /** + * A signal which fires when the enabled status changes. + **/ + sigc::signal enabledChangedSignal; + + /** + * A signal which fires when the locked status changes. + **/ + sigc::signal lockedChangedSignal; + + /** + * A signal which fires when the name changes. + **/ + sigc::signal nameChangedSignal; }; } // namespace model diff --git a/src/gui/widgets/timeline/timeline-track.cpp b/src/gui/widgets/timeline/timeline-track.cpp index ea76eb1ea..af33ce725 100644 --- a/src/gui/widgets/timeline/timeline-track.cpp +++ b/src/gui/widgets/timeline/timeline-track.cpp @@ -40,10 +40,8 @@ Track::Track(TimelineWidget &timeline_widget, shared_ptr track) : timelineWidget(timeline_widget), model_track(track), - enabled(true), expanded(true), expandDirection(None), - locked(false), headerWidget(*this), enableButton(Gtk::StockID("track_enabled"), WindowManager::MenuIconSize), lockButton(Gtk::StockID("track_unlocked"), WindowManager::MenuIconSize) @@ -53,8 +51,8 @@ Track::Track(TimelineWidget &timeline_widget, titleMenuButton.set_relief(RELIEF_HALF); titleMenuButton.unset_flags(CAN_FOCUS); - buttonBar.append(enableButton, mem_fun(this, &Track::on_enable)); - buttonBar.append(lockButton, mem_fun(this, &Track::on_lock)); + buttonBar.append(enableButton, mem_fun(this, &Track::onToggleEnabled)); + buttonBar.append(lockButton, mem_fun(this, &Track::onToggleLocked)); headerWidget.set_child_widget(headerBox); @@ -71,11 +69,11 @@ Track::Track(TimelineWidget &timeline_widget, title_list.push_back( Menu_Helpers::MenuElem(_("_Remove"), mem_fun(this, &Track::on_remove_track) ) ); - update_name(); - - // Setup tooltips - enableButton.set_tooltip_text(_("Disable track")); - lockButton.set_tooltip_text(_("Lock track")); + updateEnableButton(); + + updateLockButton(); + + updateName(); // Setup the context menu Menu::MenuList& context_list = contextMenu.items(); @@ -85,9 +83,12 @@ Track::Track(TimelineWidget &timeline_widget, mem_fun(this, &Track::on_remove_track) ) ); // Connect to the model - model_track->signal_name_changed().connect(sigc::mem_fun(this, - &Track::on_name_changed)); - + model_track->signalEnabledChanged().connect(sigc::mem_fun(this, + &Track::onEnabledChanged)); + model_track->signalLockedChanged().connect(sigc::mem_fun(this, + &Track::onLockedChanged)); + model_track->signalNameChanged().connect(sigc::mem_fun(this, + &Track::onNameChanged)); } Track::~Track() @@ -226,42 +227,15 @@ Track::show_header_context_menu(guint button, guint32 time) } void -Track::update_name() +Track::onEnabledChanged(bool) { - REQUIRE(model_track); - titleMenuButton.set_label(model_track->get_name()); + updateEnableButton(); } void -Track::on_enable() +Track::onLockedChanged(bool) { - enabled = !enabled; - if (enabled) - { - enableButton.set_stock_id(Gtk::StockID("track_enabled"), WindowManager::MenuIconSize); - enableButton.set_tooltip_text(_("Disable track")); - } - else - { - enableButton.set_stock_id(Gtk::StockID("track_disabled"), WindowManager::MenuIconSize); - enableButton.set_tooltip_text(_("Enable track")); - } -} - -void -Track::on_lock() -{ - locked = !locked; - if (locked) - { - lockButton.set_stock_id(Gtk::StockID("track_locked"), WindowManager::MenuIconSize); - lockButton.set_tooltip_text(_("Unlock track")); - } - else - { - lockButton.set_stock_id(Gtk::StockID("track_unlocked"), WindowManager::MenuIconSize); - lockButton.set_tooltip_text(_("Lock track")); - } + updateLockButton(); } void @@ -281,9 +255,9 @@ Track::on_set_name() } void -Track::on_name_changed(std::string) +Track::onNameChanged(std::string) { - update_name(); + updateName(); } void @@ -296,6 +270,61 @@ Track::on_remove_track() state->get_sequence()->remove_descendant_track(model_track); } +void +Track::onToggleEnabled() +{ + bool status = model_track->getEnabled(); + model_track->setEnabled(!status); +} + +void +Track::onToggleLocked() +{ + bool status = model_track->getLocked(); + model_track->setLocked(!status); +} + +void +Track::updateEnableButton() +{ + REQUIRE (model_track); + + if (model_track->getEnabled()) + { + enableButton.set_stock_id(Gtk::StockID("track_enabled"), WindowManager::MenuIconSize); + enableButton.set_tooltip_text(_("Disable track")); + } + else + { + enableButton.set_stock_id(Gtk::StockID("track_disabled"), WindowManager::MenuIconSize); + enableButton.set_tooltip_text(_("Enable track")); + } +} + +void +Track::updateLockButton() +{ + REQUIRE (model_track); + + if (model_track->getLocked()) + { + lockButton.set_stock_id(Gtk::StockID("track_locked"), WindowManager::MenuIconSize); + lockButton.set_tooltip_text(_("Unlock track")); + } + else + { + lockButton.set_stock_id(Gtk::StockID("track_unlocked"), WindowManager::MenuIconSize); + lockButton.set_tooltip_text(_("Lock track")); + } +} + +void +Track::updateName() +{ + REQUIRE(model_track); + titleMenuButton.set_label(model_track->get_name()); +} + } // namespace timeline } // namespace widgets } // namespace gui diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 474b74e41..3d9a14ab8 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -62,6 +62,9 @@ public: }; public: + /** + * Constructor + */ Track(TimelineWidget &timeline_widget, boost::shared_ptr track); @@ -74,6 +77,10 @@ public: boost::shared_ptr get_model_track() const; + /** + * Return the visual height of the track in pixels. + * @return The visual height of the track in pixels. + */ int get_height() const; /** @@ -125,45 +132,63 @@ public: virtual void draw_track(Cairo::RefPtr cairo, TimelineViewWindow* const window) const = 0; - -public: - //----- Constants -----// - + +private: + /** * Specifies the period of the expand animation in seconds. **/ static const float ExpandAnimationPeriod; -private: - //----- Internals -----// - void update_name(); - private: - //----- Event Handlers -----// - void on_enable(); - void on_lock(); + /** + * Event handler for when the enabled status changes. + **/ + void onEnabledChanged(bool); + + /** + * Event handler for when the track name changes. + **/ + void onNameChanged(std::string); + + /** + * Event handler for when the user requested to remove the track. + **/ + void on_remove_track(); + + /** + * Event handler for when the locked status changes. + **/ + void onLockedChanged(bool); + + /** + * Event handler for when the user requested a name change. + **/ void on_set_name(); /** - * Event handler for when the track name changes - **/ - void on_name_changed(std::string); + * Event handler for when the user pressed the Enable button. + */ + void onToggleEnabled(); + + /** + * Event handler for when the user pressed the Lock button. + */ + void onToggleLocked(); + + void updateEnableButton(); + + void updateLockButton(); + + void updateName(); - void on_remove_track(); - protected: TimelineWidget &timelineWidget; boost::shared_ptr model_track; private: - - /** - * True if this track is enabled. - */ - bool enabled; - /** * This bool is true if this branch is expanded. false if it is * collapsed. @@ -194,11 +219,6 @@ private: **/ boost::scoped_ptr expand_timer; - /** - * True if this track is locked. - */ - bool locked; - //----- Header Widgets ------// timeline::TimelineHeaderWidget headerWidget; From aed9193cfaa3c43e022e9327d55e569495957c3e Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Mon, 27 Dec 2010 10:35:57 +0100 Subject: [PATCH 054/140] Move clip name and duration to GUI model. --- src/gui/model/clip.cpp | 55 ++++++++++-- src/gui/model/clip.hpp | 90 +++++++++++++++++-- src/gui/widgets/timeline-widget.cpp | 2 +- src/gui/widgets/timeline/timeline-body.cpp | 2 +- .../widgets/timeline/timeline-clip-track.cpp | 5 +- .../widgets/timeline/timeline-clip-track.hpp | 2 +- src/gui/widgets/timeline/timeline-clip.cpp | 35 +++++--- src/gui/widgets/timeline/timeline-clip.hpp | 14 ++- 8 files changed, 171 insertions(+), 34 deletions(-) diff --git a/src/gui/model/clip.cpp b/src/gui/model/clip.cpp index af53ce7a6..95233b20f 100644 --- a/src/gui/model/clip.cpp +++ b/src/gui/model/clip.cpp @@ -1,6 +1,6 @@ /* clip.cpp - Implementation of the Clip object - + Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -25,10 +25,55 @@ namespace gui { namespace model { -Clip::Clip() -{ - -} + Clip::Clip() + : begin(1000000), + end(2000000) + { } + + gavl_time_t + Clip::getBegin() const + { + return begin; + } + + gavl_time_t + Clip::getEnd() const + { + return end; + } + + const std::string + Clip::getName() const + { + return name; + } + + void + Clip::setBegin(gavl_time_t begin) + { + this->begin = begin; + // TODO: emit signal + } + + void + Clip::setEnd(gavl_time_t end) + { + this->end = end; + // TODO: emit signal + } + + void + Clip::setName(const std::string &name) + { + this->name = name; + nameChangedSignal.emit(name); + } + + sigc::signal + Clip::signalNameChanged() const + { + return nameChangedSignal; + } } // namespace model } // namespace gui diff --git a/src/gui/model/clip.hpp b/src/gui/model/clip.hpp index 1c895239f..a024cfb67 100644 --- a/src/gui/model/clip.hpp +++ b/src/gui/model/clip.hpp @@ -23,21 +23,95 @@ ** This file contains the definition of the Clip object */ +#include +#include "gui/gtk-lumiera.hpp" + +// TODO: Remove once we get real measure of duration. +// This is here *only* for purposes of testing the GUI. +extern "C" { +#include +#include +} + +using Cairo::Pattern; + #ifndef CLIP_HPP #define CLIP_HPP namespace gui { namespace model { -class Clip -{ -public: - /** - * Constructor - **/ - Clip(); + class Clip + { + public: + /** + * Constructor + **/ + Clip(); -}; + /** + * Gets the begin time of this clip. + **/ + gavl_time_t + getBegin() const; + + /** + * Gets the end time of this clip. + **/ + gavl_time_t + getEnd() const; + + /** + * Gets the name of this clip. + **/ + const std::string + getName() const; + + /** + * Sets the begin time of this clip. + * @param[in] begin The new begin time to set this clip to. + **/ + void + setBegin(gavl_time_t begin); + + /** + * Sets the end time of this clip. + * @param[in] end The new end time to set this clip to. + **/ + void + setEnd(gavl_time_t end); + + /** + * Sets the name of this clip. + * @param[in] name The new name to set this clip to. + **/ + void + setName(const std::string &name); + + /** + * A signal which fires when the name changes. + * @return Returns the signal. The signal sends the new name for the clip. + **/ + sigc::signal + signalNameChanged() const; + + private: + + /** + * The name of this clip. + **/ + std::string name; + + /** + * A signal which fires when the name changes. + **/ + sigc::signal nameChangedSignal; + + // TODO: Use a good measure of duration, probably TimeSpan. + // These are here *only* for purposes of testing the GUI. + gavl_time_t begin; + gavl_time_t end; + }; } // namespace model } // namespace gui diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index eabf25514..b1f64b07b 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -105,7 +105,7 @@ TimelineWidget::set_state(shared_ptr new_state) // Clear the track tree trackMap.clear(); - if(state) + if (state) { // Hook up event handlers state->get_view_window().changed_signal().connect( sigc::mem_fun( diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 35a58f011..5ec1ca50f 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -324,7 +324,7 @@ TimelineBody::draw_tracks(Cairo::RefPtr cr) iterator != layout_tree.end(); iterator++) { - const shared_ptr model_track(*iterator); + // const shared_ptr model_track(*iterator); const shared_ptr timeline_track = timelineWidget.lookup_timeline_track(*iterator); diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index 92a2f4511..cf02f1bab 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -32,12 +32,13 @@ namespace gui { namespace widgets { namespace timeline { -ClipTrack::ClipTrack(TimelineWidget &timeline_widget, +ClipTrack::ClipTrack(TimelineWidget &timelineWidget, boost::shared_ptr track) : - Track(timeline_widget, track) + Track(timelineWidget, track) { // TEST CODE: add a clip to the track boost::shared_ptr model_clip(new model::Clip()); + model_clip->setName("Clip Name"); boost::shared_ptr timeline_clip(new timeline::Clip(model_clip)); clips.push_back(timeline_clip); // END TEST CODE diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index a0c630018..cd71e44f1 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -43,7 +43,7 @@ class TimelineViewWindow; class ClipTrack : public timeline::Track { public: - ClipTrack(TimelineWidget &timeline_widget, + ClipTrack(TimelineWidget &timelineWidget, boost::shared_ptr track); void draw_track(Cairo::RefPtr cairo, diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index fa8903fc9..b09dd7b1b 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -1,6 +1,6 @@ /* timeline-clip.cpp - Implementation of the timeline clip object - + Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -27,9 +27,14 @@ namespace widgets { namespace timeline { Clip::Clip(boost::shared_ptr clip) - : model_clip(clip) + : modelClip(clip), + selected(false) { - REQUIRE(model_clip); + REQUIRE(modelClip); + + // TODO: Connect signals + //modelClip->signalNameChanged().connect(mem_fun(this, + // &Clip::onNameChanged); } void @@ -38,36 +43,38 @@ Clip::draw_clip(Cairo::RefPtr cr, { REQUIRE(cr); REQUIRE(window); + REQUIRE(modelClip); + + int x = window->time_to_x(modelClip->getBegin()); + int width = window->time_to_x( + modelClip->getEnd()) - window->time_to_x(modelClip->getBegin()); - int x = window->time_to_x(1000000); - int width = window->time_to_x(2000000) - window->time_to_x(1000000); - // Draw a rectangle for the clip - cr->rectangle(x, 1, width, 100-2); - - // TODO: get duration from the model::Clip + cr->rectangle(x, 1, width, 100-2); // TODO: get height from the Timeline::Track - cr->set_source_rgb(0.4, 0.4, 0.4); + if (selected) + cr->set_source(Cairo::SolidPattern::create_rgb (0.3, 0.3, 0.3)); + else + cr->set_source(Cairo::SolidPattern::create_rgb (0.4, 0.4, 0.4)); cr->fill_preserve(); - + cr->set_source_rgb(0.25, 0.25, 0.25); cr->stroke(); // Show the clip name - cr->rectangle(x, 1, width, 100-2); + cr->rectangle(x, 1, width, 100-2); cr->clip(); cr->move_to (x + 3, 12); cr->set_source_rgb (1.0, 1.0, 1.0); cr->set_font_size (9); - cr->show_text ("Clip Name"); // TODO: get clip name from model + cr->show_text (modelClip->getName()); // TODO: Show thumbnails for clip } - } // namespace timeline } // namespace widgets } // namespace gui diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index fbc128166..6ac571d06 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -22,7 +22,8 @@ /** @file widgets/timeline/timeline-clip.hpp ** This file contains the definition of timeline clip object */ - + +#include #include "gui/gtk-lumiera.hpp" #include "gui/model/clip.hpp" @@ -44,9 +45,18 @@ public: void draw_clip(Cairo::RefPtr cairo, TimelineViewWindow* const window) const; + void + setSelected(bool state); + private: - boost::shared_ptr model_clip; + boost::shared_ptr modelClip; + + /** + * True when this clip is selected in the GUI. + */ + bool selected; + }; } // namespace timeline From 3f4c7a5e467ce2359cff51527a56e414521b1b00 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Sun, 2 Jan 2011 06:03:28 +0100 Subject: [PATCH 055/140] Spelling and style fixes --- src/gui/widgets/timeline-widget.cpp | 1 + src/gui/widgets/timeline/timeline-body.cpp | 16 ++++++++-------- src/gui/widgets/timeline/timeline-body.hpp | 6 ++++-- .../timeline/timeline-layout-helper.hpp | 2 +- src/gui/widgets/timeline/timeline-tool.cpp | 8 +++++--- src/gui/widgets/timeline/timeline-tool.hpp | 18 +++++++++--------- src/gui/widgets/timeline/timeline-track.hpp | 12 ++++++++---- 7 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index b1f64b07b..1eb0c19cb 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -175,6 +175,7 @@ TimelineWidget::hovering_track_changed_signal() const { return hoveringTrackChangedSignal; } + sigc::signal TimelineWidget::state_changed_signal() const { diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 5ec1ca50f..8a6c8128a 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -43,7 +43,7 @@ namespace gui { namespace widgets { namespace timeline { -TimelineBody::TimelineBody(TimelineWidget &timeline_widget) : +TimelineBody::TimelineBody(TimelineWidget &timelineWidget) : Glib::ObjectBase("TimelineBody"), tool(NULL), mouseDownX(0), @@ -51,10 +51,10 @@ TimelineBody::TimelineBody(TimelineWidget &timeline_widget) : dragType(None), beginShiftTimeOffset(0), selectionAlpha(0.5), - timelineWidget(timeline_widget) + timelineWidget(timelineWidget) { // Connect up some events - timeline_widget.state_changed_signal().connect( + timelineWidget.state_changed_signal().connect( sigc::mem_fun(this, &TimelineBody::on_state_changed) ); // Install style properties @@ -274,12 +274,12 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) // Forward the event to the tool tool->on_motion_notify_event(event); - + // See if the track that we're hovering over has changed - shared_ptr new_hovering_track( + shared_ptr newHoveringTrack( timelineWidget.layoutHelper.track_from_y(event->y)); - if(timelineWidget.get_hovering_track() != new_hovering_track) - timelineWidget.set_hovering_track(new_hovering_track); + if (timelineWidget.get_hovering_track() != newHoveringTrack) + timelineWidget.set_hovering_track(newHoveringTrack); } // false so that the message is passed up to the owner TimelineWidget @@ -507,7 +507,7 @@ TimelineBody::register_styles() const "The colour of the playback marker line", GDK_TYPE_COLOR, G_PARAM_READABLE)); } - + void TimelineBody::read_styles() { diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index f9adee189..b6e6a3c29 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -68,13 +68,15 @@ public: /** * Returns the type of the currently selected timeline tool. */ - ToolType get_tool() const; + 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); + void + set_tool(ToolType tool_type); /* ===== Events ===== */ protected: diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index b488f8ced..03d019067 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -21,7 +21,7 @@ */ /** @file timeline-layout-helper.cpp - ** This file contains the definition of the layout helpeer class + ** This file contains the definition of the layout helper class */ #ifndef TIMELINE_LAYOUT_HELPER_HPP diff --git a/src/gui/widgets/timeline/timeline-tool.cpp b/src/gui/widgets/timeline/timeline-tool.cpp index 0e8a95542..9e261441d 100644 --- a/src/gui/widgets/timeline/timeline-tool.cpp +++ b/src/gui/widgets/timeline/timeline-tool.cpp @@ -30,8 +30,8 @@ namespace gui { namespace widgets { namespace timeline { -Tool::Tool(TimelineBody &timeline_body) : - timelineBody(timeline_body), +Tool::Tool(TimelineBody &timelineBody) : + timelineBody(timelineBody), isDragging(false) { } @@ -40,7 +40,7 @@ bool Tool::apply_cursor() { Glib::RefPtr window = timelineBody.get_window(); - if(!window) + if (!window) return false; window->set_cursor(get_cursor()); @@ -69,6 +69,8 @@ Tool::on_button_release_event(GdkEventButton* event) void Tool::on_motion_notify_event(GdkEventMotion *event) { + REQUIRE (event != NULL); + mousePoint = Point(event->x, event->y); } diff --git a/src/gui/widgets/timeline/timeline-tool.hpp b/src/gui/widgets/timeline/timeline-tool.hpp index 25d3e3263..6b5ca5398 100644 --- a/src/gui/widgets/timeline/timeline-tool.hpp +++ b/src/gui/widgets/timeline/timeline-tool.hpp @@ -58,13 +58,13 @@ class Tool protected: /** * Constructor - * @param timeline_body The owner timeline body object + * @param timelineBody The owner timeline body object */ - Tool(TimelineBody &timeline_body); + Tool(TimelineBody &timelineBody); public: /** - * Destructor to be overriden by derived classes. + * Destructor to be overridden by derived classes. * @remarks If this were not present, derrived class destructors * would not be called. */ @@ -85,25 +85,25 @@ public: /* ===== Event Handlers ===== */ /** * The event handler for button press events. - * @remarks This can be overriden by the derrived classes, but + * @remarks This can be overridden by the derived classes, but * Tool::on_button_press_event must be called at the start - * of the derrived class's override. + * of the derived class's override. */ virtual void on_button_press_event(GdkEventButton* event); /** * The event handler for button release events. - * @remarks This can be overriden by the derrived classes, but + * @remarks This can be overridden by the derived classes, but * Tool::on_button_release_event must be called at the end of - * the derrived class's override. + * the derived class's override. */ virtual void on_button_release_event(GdkEventButton* event); /** * The event handler for mouse move events. - * @remarks This can be overriden by the derrived classes, but + * @remarks This can be overridden by the derived classes, but * Tool::on_motion_notify_event must be called at the start of - * the derrived class's override. + * the derived class's override. */ virtual void on_motion_notify_event(GdkEventMotion *event); diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 3d9a14ab8..4707cb436 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -61,7 +61,6 @@ public: Collapse }; -public: /** * Constructor */ @@ -75,7 +74,8 @@ public: Gtk::Widget& get_header_widget(); - boost::shared_ptr get_model_track() const; + boost::shared_ptr + get_model_track() const; /** * Return the visual height of the track in pixels. @@ -127,8 +127,14 @@ public: **/ Gtk::ExpanderStyle get_expander_style() const; + /** + * + **/ void show_header_context_menu(guint button, guint32 time); + /** + * Draw the track + **/ virtual void draw_track(Cairo::RefPtr cairo, TimelineViewWindow* const window) const = 0; @@ -140,8 +146,6 @@ private: **/ static const float ExpandAnimationPeriod; -private: - /** * Event handler for when the enabled status changes. **/ From 7500732976fd24dc96d24defe3978b604c3eb2fa Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Sun, 2 Jan 2011 10:40:17 +0100 Subject: [PATCH 056/140] Move Clips for ClipTracks to the GUI model. --- src/gui/model/clip-track.cpp | 43 ++++-- src/gui/model/clip-track.hpp | 44 ++++-- src/gui/widgets/timeline-widget.cpp | 2 +- src/gui/widgets/timeline-widget.hpp | 4 +- .../widgets/timeline/timeline-clip-track.cpp | 134 +++++++++++++----- .../widgets/timeline/timeline-clip-track.hpp | 69 +++++++-- .../widgets/timeline/timeline-group-track.cpp | 2 +- 7 files changed, 222 insertions(+), 76 deletions(-) diff --git a/src/gui/model/clip-track.cpp b/src/gui/model/clip-track.cpp index 30407a1f8..bc691993f 100644 --- a/src/gui/model/clip-track.cpp +++ b/src/gui/model/clip-track.cpp @@ -22,22 +22,39 @@ #include "clip-track.hpp" +#include "clip.hpp" + +#include + namespace gui { namespace model { - -ClipTrack::ClipTrack() -{ -} -std::string -ClipTrack::print_track() -{ - std::ostringstream os; - - os << "ClipTrack\t\"" << get_name() << "\""; - - return os.str(); -} + ClipTrack::ClipTrack() + { + // TEST CODE: add a clip to the track + + boost::shared_ptr model_clip(new model::Clip()); + model_clip->setName("Clip Name"); + clips.push_back(model_clip); + + // END TEST CODE + } + + std::string + ClipTrack::print_track() + { + std::ostringstream os; + + os << "ClipTrack\t\"" << get_name() << "\""; + + return os.str(); + } + + lumiera::observable_list< boost::shared_ptr >& + ClipTrack::getClipList() + { + return clips; + } } // namespace model } // namespace gui diff --git a/src/gui/model/clip-track.hpp b/src/gui/model/clip-track.hpp index 1fa86f7f1..ed4957581 100644 --- a/src/gui/model/clip-track.hpp +++ b/src/gui/model/clip-track.hpp @@ -29,24 +29,38 @@ #define CLIP_TRACK_HPP #include "track.hpp" +#include "lib/observable-list.hpp" namespace gui { namespace model { - -class Clip; - -class ClipTrack : public Track -{ -public: - ClipTrack(); - - std::string print_track(); - -private: - - std::vector clips; - -}; + + class Clip; + + class ClipTrack : public Track + { + public: + /** + * Constructor + **/ + ClipTrack(); + + /** + * Gets a string representation of the track that is suitable for debugging + **/ + std::string + print_track(); + + /** + * Gets the list of clips associated with this track. + **/ + lumiera::observable_list< boost::shared_ptr >& + getClipList(void); + + private: + + lumiera::observable_list< boost::shared_ptr > clips; + + }; } // namespace timeline } // namespace gui diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 1eb0c19cb..0f44402af 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -315,7 +315,7 @@ TimelineWidget::create_timeline_track_from_model_track( // class if(typeid(*model_track) == typeid(model::ClipTrack)) return shared_ptr(new timeline::ClipTrack( - *this, model_track)); + *this, dynamic_pointer_cast(model_track))); else if(typeid(*model_track) == typeid(model::GroupTrack)) return shared_ptr(new timeline::GroupTrack( *this, dynamic_pointer_cast(model_track))); diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index ee9cd184a..3bfa76593 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -241,9 +241,9 @@ protected: /** * The trackMap maps model tracks to timeline widget tracks which are * responsible for the UI representation of a track. - * @remarks The tree structure is maintianed by the model, and as the + * @remarks The tree structure is maintained by the model, and as the * widget is updated with update_tracks, timeline tracks are added and - * removed from the map in correspondance with the tree. + * removed from the map in correspondence with the tree. **/ std::map, boost::shared_ptr > diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index cf02f1bab..beddafc62 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -27,47 +27,117 @@ #include "timeline-view-window.hpp" using namespace Gtk; +using boost::dynamic_pointer_cast; +using boost::shared_ptr; +using util::contains; namespace gui { namespace widgets { namespace timeline { -ClipTrack::ClipTrack(TimelineWidget &timelineWidget, - boost::shared_ptr track) : - Track(timelineWidget, track) -{ - // TEST CODE: add a clip to the track - boost::shared_ptr model_clip(new model::Clip()); - model_clip->setName("Clip Name"); - boost::shared_ptr timeline_clip(new timeline::Clip(model_clip)); - clips.push_back(timeline_clip); - // END TEST CODE -} + ClipTrack::ClipTrack (TimelineWidget &timelineWidget, + shared_ptr track) : + Track(timelineWidget, track) + { + REQUIRE (track); -void -ClipTrack::draw_track(Cairo::RefPtr cairo, + // Connect signals + track->getClipList().signal_changed().connect( + sigc::mem_fun(this, &ClipTrack::onClipListChanged)); + + updateClips(); + } + + void + ClipTrack::draw_track ( + Cairo::RefPtr cairo, TimelineViewWindow* const window) const -{ - REQUIRE(cairo); - REQUIRE(window); + { + REQUIRE (cairo); + REQUIRE (window); - // Draw a rectangle to let us know it works? :-) - cairo->rectangle(window->time_to_x(0), 1, - window->time_to_x(500000) - window->time_to_x(0), - get_height() - 2); - - cairo->set_source_rgb(0.5, 0.5, 0.5); - cairo->fill_preserve(); - - cairo->set_source_rgb(0.25, 0.25, 0.25); - cairo->stroke(); + // Draw a rectangle to let us know it works? :-) + cairo->rectangle(window->time_to_x(0), 1, + window->time_to_x(500000) - window->time_to_x(0), + get_height() - 2); - // Draw all clips - BOOST_FOREACH(boost::shared_ptr c, clips) - { - c->draw_clip(cairo, window); - } -} + cairo->set_source_rgb(0.5, 0.5, 0.5); + cairo->fill_preserve(); + + cairo->set_source_rgb(0.25, 0.25, 0.25); + cairo->stroke(); + + // Draw all clips + std::pair, shared_ptr > + pair; + BOOST_FOREACH (pair, clipMap) + { + pair.second->draw_clip(cairo, window); + } + } + + //// private methods + + void + ClipTrack::createTimelineClips() + { + BOOST_FOREACH (shared_ptr modelClip, getModelTrack()->getClipList()) + { + // Is a timeline UI clip present in the map already? + if (!contains (clipMap, modelClip)) + { + // The timeline UI clip is not present + // We will need to create one + clipMap[modelClip] = shared_ptr( + new timeline::Clip (modelClip)); + } + } + } + + shared_ptr + ClipTrack::getModelTrack () + { + return dynamic_pointer_cast(model_track); + } + + void + ClipTrack::onClipListChanged () + { + updateClips (); + } + + void + ClipTrack::removeOrphanedClips () + { + std::map< shared_ptr, + shared_ptr > + orphanClipMap (clipMap); + + // Remove all clips which are still present in the sequence + BOOST_FOREACH (shared_ptr modelClip, getModelTrack()->getClipList()) + if (contains (orphanClipMap, modelClip)) + orphanClipMap.erase(modelClip); + + // orphanClipMap now contains all the orphaned clips + // Remove them + std::pair< shared_ptr, shared_ptr > + pair; + BOOST_FOREACH (pair, orphanClipMap) + { + ENSURE (pair.first); + clipMap.erase (pair.first); + } + } + + void + ClipTrack::updateClips() + { + // Remove any clips which are no longer present in the model + removeOrphanedClips (); + + // Create timeline clips from all the model clips + createTimelineClips (); + } } // namespace timeline } // namespace widgets diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index cd71e44f1..49a29bb19 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -37,21 +37,66 @@ namespace gui { namespace widgets { namespace timeline { -class Clip; -class TimelineViewWindow; + class Clip; + class TimelineViewWindow; -class ClipTrack : public timeline::Track -{ -public: - ClipTrack(TimelineWidget &timelineWidget, - boost::shared_ptr track); + class ClipTrack : public timeline::Track + { + public: + /** + * Constructor. + **/ + ClipTrack(TimelineWidget &timelineWidget, + boost::shared_ptr track); - void draw_track(Cairo::RefPtr cairo, - TimelineViewWindow* const window) const; + /** + * + **/ + void draw_track(Cairo::RefPtr cairo, + TimelineViewWindow* const window) const; -private: - std::vector > clips; -}; + private: + + /** + * Ensures timeline UI clips have been created for every model clip in track. + **/ + void + createTimelineClips(); + + /** + * Gets the modelTrack as a ClipTrack. + **/ + boost::shared_ptr + getModelTrack (); + + /** + * An event handler that receives notifications for when the models clip list has been + * changed. + **/ + void + onClipListChanged(); + + /** + * Removes any UI clips which no longer have corresponding model clips present in the + * sequence. + **/ + void + removeOrphanedClips(); + + /** + * Update the attached timeline clips. + **/ + void + updateClips(); + + /** + * The clipMap maps model clips to timeline widget clips which are responsible for the + * UI representation of a clip. + **/ + std::map, + boost::shared_ptr > + clipMap; + }; } // namespace timeline } // namespace widgets diff --git a/src/gui/widgets/timeline/timeline-group-track.cpp b/src/gui/widgets/timeline/timeline-group-track.cpp index 9407a41da..aa43e1970 100644 --- a/src/gui/widgets/timeline/timeline-group-track.cpp +++ b/src/gui/widgets/timeline/timeline-group-track.cpp @@ -35,7 +35,7 @@ GroupTrack::GroupTrack(TimelineWidget &timeline_widget, shared_ptr track) : Track(timeline_widget, track) { - REQUIRE(track); + REQUIRE (track); // Receive notifications of changes to the tracks track->get_child_track_list().signal_changed().connect( From 5c4992310e9dc69e7e3e044cb7cdca16c2db4f33 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Sun, 2 Jan 2011 10:47:04 +0100 Subject: [PATCH 057/140] several style fixes, underscore to camel case --- src/gui/controller/controller.cpp | 4 +- src/gui/controller/controller.hpp | 2 +- src/gui/model/clip-track.cpp | 6 +-- src/gui/widgets/timeline-widget.cpp | 44 +++++++++---------- src/gui/widgets/timeline-widget.hpp | 20 ++++----- src/gui/widgets/timeline/timeline-body.cpp | 2 +- .../widgets/timeline/timeline-clip-track.cpp | 2 +- .../timeline/timeline-header-container.cpp | 6 +-- .../timeline/timeline-header-container.hpp | 8 ++-- .../timeline/timeline-header-widget.cpp | 6 +-- .../timeline/timeline-layout-helper.cpp | 40 ++++++++--------- .../timeline/timeline-layout-helper.hpp | 10 ++--- src/gui/widgets/timeline/timeline-track.cpp | 44 +++++++++---------- src/gui/widgets/timeline/timeline-track.hpp | 4 +- 14 files changed, 99 insertions(+), 99 deletions(-) diff --git a/src/gui/controller/controller.cpp b/src/gui/controller/controller.cpp index c31ca787d..3ddf903d5 100644 --- a/src/gui/controller/controller.cpp +++ b/src/gui/controller/controller.cpp @@ -25,8 +25,8 @@ namespace gui { namespace controller { -Controller::Controller(model::Project &model_project) : - project(model_project) +Controller::Controller(model::Project &modelProject) : + project(modelProject) { } diff --git a/src/gui/controller/controller.hpp b/src/gui/controller/controller.hpp index 29d3684df..e462612aa 100644 --- a/src/gui/controller/controller.hpp +++ b/src/gui/controller/controller.hpp @@ -40,7 +40,7 @@ namespace controller { class Controller { public: - Controller(model::Project &model_project); + Controller(model::Project &modelProject); PlaybackController& get_playback_controller(); diff --git a/src/gui/model/clip-track.cpp b/src/gui/model/clip-track.cpp index bc691993f..95ddd8ac0 100644 --- a/src/gui/model/clip-track.cpp +++ b/src/gui/model/clip-track.cpp @@ -33,9 +33,9 @@ namespace model { { // TEST CODE: add a clip to the track - boost::shared_ptr model_clip(new model::Clip()); - model_clip->setName("Clip Name"); - clips.push_back(model_clip); + boost::shared_ptr modelClip(new model::Clip()); + modelClip->setName("Clip Name"); + clips.push_back(modelClip); // END TEST CODE } diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 0f44402af..89c098783 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -286,39 +286,39 @@ TimelineWidget::create_timeline_tracks() void TimelineWidget::create_timeline_tracks_from_branch( - shared_ptr model_track) + shared_ptr modelTrack) { - REQUIRE(model_track); + REQUIRE(modelTrack); // Is a timeline UI track present in the map already? - if(!contains(trackMap, model_track)) + if(!contains(trackMap, modelTrack)) { // The timeline UI track is not present // We will need to create one - trackMap[model_track] = - create_timeline_track_from_model_track(model_track); + trackMap[modelTrack] = + create_timeline_track_from_modelTrack(modelTrack); } // Recurse to child tracks BOOST_FOREACH(shared_ptr child, - model_track->get_child_tracks()) + modelTrack->get_child_tracks()) create_timeline_tracks_from_branch(child); } shared_ptr -TimelineWidget::create_timeline_track_from_model_track( - shared_ptr model_track) +TimelineWidget::create_timeline_track_from_modelTrack( + shared_ptr modelTrack) { - REQUIRE(model_track); + REQUIRE(modelTrack); // Choose a corresponding timeline track class from the model track's // class - if(typeid(*model_track) == typeid(model::ClipTrack)) + if(typeid(*modelTrack) == typeid(model::ClipTrack)) return shared_ptr(new timeline::ClipTrack( - *this, dynamic_pointer_cast(model_track))); - else if(typeid(*model_track) == typeid(model::GroupTrack)) + *this, dynamic_pointer_cast(modelTrack))); + else if(typeid(*modelTrack) == typeid(model::GroupTrack)) return shared_ptr(new timeline::GroupTrack( - *this, dynamic_pointer_cast(model_track))); + *this, dynamic_pointer_cast(modelTrack))); ASSERT(NULL); // Unknown track type; return shared_ptr(); @@ -349,32 +349,32 @@ TimelineWidget::remove_orphaned_tracks() void TimelineWidget::search_orphaned_tracks_in_branch( - boost::shared_ptr model_track, + boost::shared_ptr modelTrack, std::map, boost::shared_ptr > &orphan_track_map) { - REQUIRE(model_track); + REQUIRE(modelTrack); // Is the timeline UI still present? - if(contains(orphan_track_map, model_track)) - orphan_track_map.erase(model_track); + if(contains(orphan_track_map, modelTrack)) + orphan_track_map.erase(modelTrack); // Recurse to child tracks BOOST_FOREACH(shared_ptr child, - model_track->get_child_tracks()) + modelTrack->get_child_tracks()) search_orphaned_tracks_in_branch(child, orphan_track_map); } shared_ptr TimelineWidget::lookup_timeline_track( - shared_ptr model_track) const + shared_ptr modelTrack) const { - REQUIRE(model_track); - REQUIRE(model_track != sequence()); // The sequence isn't + REQUIRE(modelTrack); + REQUIRE(modelTrack != sequence()); // The sequence isn't // really a track std::map, shared_ptr >:: - const_iterator iterator = trackMap.find(model_track); + const_iterator iterator = trackMap.find(modelTrack); if(iterator == trackMap.end()) { // The track is not present in the map diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index 3bfa76593..b409d7fe1 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -159,17 +159,17 @@ private: * @param list The parent track of the branch. **/ void create_timeline_tracks_from_branch( - boost::shared_ptr model_track); + boost::shared_ptr modelTrack); /** * Creates a timeline UI track to correspond to a model track. - * @param model_track The model track to create a timeline track from. + * @param modelTrack The model track to create a timeline track from. * @return The timeline track created, or an empty shared_ptr if - * model_track has an unreckognised type (this is an error condition). + * modelTrack has an unreckognised type (this is an error condition). **/ boost::shared_ptr - create_timeline_track_from_model_track( - boost::shared_ptr model_track); + create_timeline_track_from_modelTrack( + boost::shared_ptr modelTrack); /** * Removes any UI tracks which no longer have corresponding model @@ -178,20 +178,20 @@ private: void remove_orphaned_tracks(); void search_orphaned_tracks_in_branch( - boost::shared_ptr model_track, + boost::shared_ptr modelTrack, std::map, boost::shared_ptr > &orphan_track_map); /** * Looks up a timeline UI track in trackMap that corresponds to a - * given model_track. - * @param model_track The model track to look up. + * given modelTrack. + * @param modelTrack The model track to look up. * @returns The timeline UI track found, or an empty shared_ptr if - * model_track has no corresponding timeline UI track (this is an + * modelTrack has no corresponding timeline UI track (this is an * error condition). **/ boost::shared_ptr lookup_timeline_track( - boost::shared_ptr model_track) const; + boost::shared_ptr modelTrack) const; // ----- Layout Functions ----- // diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 8a6c8128a..f78f99ec1 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -324,7 +324,7 @@ TimelineBody::draw_tracks(Cairo::RefPtr cr) iterator != layout_tree.end(); iterator++) { - // const shared_ptr model_track(*iterator); + // const shared_ptr modelTrack(*iterator); const shared_ptr timeline_track = timelineWidget.lookup_timeline_track(*iterator); diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index beddafc62..55b7169b8 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -97,7 +97,7 @@ namespace timeline { shared_ptr ClipTrack::getModelTrack () { - return dynamic_pointer_cast(model_track); + return dynamic_pointer_cast(modelTrack); } void diff --git a/src/gui/widgets/timeline/timeline-header-container.cpp b/src/gui/widgets/timeline/timeline-header-container.cpp index 66732d383..3ecfba2d0 100644 --- a/src/gui/widgets/timeline/timeline-header-container.cpp +++ b/src/gui/widgets/timeline/timeline-header-container.cpp @@ -425,12 +425,12 @@ TimelineHeaderContainer::layout_headers() shared_ptr TimelineHeaderContainer::lookup_timeline_track( - shared_ptr model_track) + shared_ptr modelTrack) { - REQUIRE(model_track != NULL); + REQUIRE(modelTrack != NULL); shared_ptr timeline_track = - timelineWidget.lookup_timeline_track(model_track); + timelineWidget.lookup_timeline_track(modelTrack); ENSURE(timeline_track); return timeline_track; diff --git a/src/gui/widgets/timeline/timeline-header-container.hpp b/src/gui/widgets/timeline/timeline-header-container.hpp index 4bf03a1e9..dc1218cbd 100644 --- a/src/gui/widgets/timeline/timeline-header-container.hpp +++ b/src/gui/widgets/timeline/timeline-header-container.hpp @@ -162,28 +162,28 @@ private: /** * Draws the border decoration around the track header. - * @param model_track The track to draw the decoration for. + * @param modelTrack The track to draw the decoration for. * @param clip_rect The clip to drawing to. * @param depth The depth within the tree of this track. This is used * to control the amount of indention. * @param offset The vertical offset of the headers in pixels. **/ void draw_header_decoration( - boost::shared_ptr model_track, + boost::shared_ptr modelTrack, const Gdk::Rectangle &clip_rect); /** * A helper function which calls lookup_timeline_track within the * parent timeline widget, but also applies lots of data consistency * checks in the process. - * @param model_track The model track to look up in the parent widget. + * @param modelTrack The model track to look up in the parent widget. * @return Returns the track found, or returns NULL if no matching * track was found. * @remarks If the return value is going to be NULL, an ENSURE will * fail. **/ boost::shared_ptr lookup_timeline_track( - boost::shared_ptr model_track); + boost::shared_ptr modelTrack); void begin_drag(); diff --git a/src/gui/widgets/timeline/timeline-header-widget.cpp b/src/gui/widgets/timeline/timeline-header-widget.cpp index 5c9bf81bd..1eac796d0 100644 --- a/src/gui/widgets/timeline/timeline-header-widget.cpp +++ b/src/gui/widgets/timeline/timeline-header-widget.cpp @@ -160,8 +160,8 @@ TimelineHeaderWidget::on_expose_event(GdkEventExpose *event) REQUIRE(style); REQUIRE(gdkWindow); - shared_ptr model_track = track.get_model_track(); - REQUIRE(model_track); + shared_ptr modelTrack = track.get_modelTrack(); + REQUIRE(modelTrack); // Get the header box const Gdk::Rectangle allocation = get_allocation(); @@ -181,7 +181,7 @@ TimelineHeaderWidget::on_expose_event(GdkEventExpose *event) else if(hoveringExpander) state_type = STATE_PRELIGHT; - if(!model_track->get_child_tracks().empty()) + if(!modelTrack->get_child_tracks().empty()) style->paint_expander (gdkWindow, state_type, box, *this, "", diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index 372220ff9..ec653ad81 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -149,9 +149,9 @@ TimelineLayoutHelper::begin_dragging_track( dragPoint.get_x() - rect.get_x(), dragPoint.get_y() - rect.get_y()); - const shared_ptr model_track = - dragging_track->get_model_track(); - draggingTrackIter = iterator_from_track(model_track); + const shared_ptr modelTrack = + dragging_track->get_modelTrack(); + draggingTrackIter = iterator_from_track(modelTrack); dragBranchHeight = measure_branch_height(draggingTrackIter); dropPoint.relation = None; @@ -163,7 +163,7 @@ void TimelineLayoutHelper::end_dragging_track(bool apply) { if(apply) - apply_drop_to_model_tree(dropPoint); + apply_drop_to_modelTree(dropPoint); draggingTrackIter.node = NULL; clone_tree_from_sequence(); @@ -300,14 +300,14 @@ TimelineLayoutHelper::is_animating() const TimelineLayoutHelper::TrackTree::pre_order_iterator TimelineLayoutHelper::iterator_from_track( - boost::shared_ptr model_track) + boost::shared_ptr modelTrack) { - REQUIRE(model_track); + REQUIRE(modelTrack); TrackTree::pre_order_iterator iter; for(iter = layoutTree.begin(); iter != layoutTree.end(); iter++) { - if(*iter == model_track) + if(*iter == modelTrack) break; } @@ -384,15 +384,15 @@ TimelineLayoutHelper::layout_headers_recursive( Gdk::Rectangle rect; int track_height = 0; - const shared_ptr &model_track = *iterator; - REQUIRE(model_track); + const shared_ptr &modelTrack = *iterator; + REQUIRE(modelTrack); shared_ptr timeline_track = - lookup_timeline_track(model_track); + lookup_timeline_track(modelTrack); // Is this the root track of a dragging branch? bool being_dragged = false; if(dragging) - being_dragged = (model_track == *draggingTrackIter); + being_dragged = (modelTrack == *draggingTrackIter); // Is the track going to be shown? if(parent_expanded) @@ -473,11 +473,11 @@ TimelineLayoutHelper::layout_headers_recursive( shared_ptr TimelineLayoutHelper::lookup_timeline_track( - shared_ptr model_track) + shared_ptr modelTrack) { - REQUIRE(model_track != NULL); + REQUIRE(modelTrack != NULL); shared_ptr timeline_track = - timelineWidget.lookup_timeline_track(model_track); + timelineWidget.lookup_timeline_track(modelTrack); ENSURE(timeline_track); return timeline_track; @@ -503,10 +503,10 @@ TimelineLayoutHelper::attempt_drop(TrackTree::pre_order_iterator target, const Gdk::Point &point) { // Lookup the tracks - const shared_ptr model_track(*target); - REQUIRE(model_track); + const shared_ptr modelTrack(*target); + REQUIRE(modelTrack); const weak_ptr timeline_track = - lookup_timeline_track(model_track); + lookup_timeline_track(modelTrack); // Calculate coordinates const Gdk::Rectangle &rect = headerBoxes[timeline_track]; @@ -530,9 +530,9 @@ TimelineLayoutHelper::attempt_drop(TrackTree::pre_order_iterator target, full_width, half_height))) { // We're hovering over the lower half of the header - if(model_track->can_host_children()) + if(modelTrack->can_host_children()) { - if(model_track->get_child_tracks().empty()) + if(modelTrack->get_child_tracks().empty()) { // Is our track being dragged after this header? if(dragPoint.get_x() < x_mid) @@ -596,7 +596,7 @@ TimelineLayoutHelper::apply_drop_to_layout_tree( } void -TimelineLayoutHelper::apply_drop_to_model_tree( +TimelineLayoutHelper::apply_drop_to_modelTree( const TimelineLayoutHelper::DropPoint &drop) { const shared_ptr sequence = get_sequence(); diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index 03d019067..4614d373a 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -179,12 +179,12 @@ public: /** * A utility function which finds the iterator of a track in the * layout tree. - * @param model_track The model track to look for. + * @param modelTrack The model track to look for. * @return Returns the model iterator of layoutTree.end() if no * iterator was found. **/ TrackTree::pre_order_iterator iterator_from_track( - boost::shared_ptr model_track); + boost::shared_ptr modelTrack); /** * A function that recursively calculates the visible height of a @@ -290,14 +290,14 @@ protected: * A helper function which calls lookup_timeline_track within the * parent timeline widget, but also applies lots of data consistency * checks in the process. - * @param model_track The model track to look up in the parent widget. + * @param modelTrack The model track to look up in the parent widget. * @return Returns the track found, or returns NULL if no matching * track was found. * @remarks If the return value is going to be NULL, an ENSURE will * fail. **/ boost::shared_ptr lookup_timeline_track( - boost::shared_ptr model_track); + boost::shared_ptr modelTrack); /** * A helper function which kicks off the animation timer. @@ -335,7 +335,7 @@ protected: * specified by drop. * @param[in] drop The point in the tree to drop onto. **/ - void apply_drop_to_model_tree(const DropPoint &drop); + void apply_drop_to_modelTree(const DropPoint &drop); /** * Helper to get the sequence object from the state. diff --git a/src/gui/widgets/timeline/timeline-track.cpp b/src/gui/widgets/timeline/timeline-track.cpp index af33ce725..e2b22ce2e 100644 --- a/src/gui/widgets/timeline/timeline-track.cpp +++ b/src/gui/widgets/timeline/timeline-track.cpp @@ -39,14 +39,14 @@ const float Track::ExpandAnimationPeriod = 0.15; Track::Track(TimelineWidget &timeline_widget, shared_ptr track) : timelineWidget(timeline_widget), - model_track(track), + modelTrack(track), expanded(true), expandDirection(None), headerWidget(*this), enableButton(Gtk::StockID("track_enabled"), WindowManager::MenuIconSize), lockButton(Gtk::StockID("track_unlocked"), WindowManager::MenuIconSize) { - REQUIRE(model_track); + REQUIRE(modelTrack); titleMenuButton.set_relief(RELIEF_HALF); titleMenuButton.unset_flags(CAN_FOCUS); @@ -83,11 +83,11 @@ Track::Track(TimelineWidget &timeline_widget, mem_fun(this, &Track::on_remove_track) ) ); // Connect to the model - model_track->signalEnabledChanged().connect(sigc::mem_fun(this, + modelTrack->signalEnabledChanged().connect(sigc::mem_fun(this, &Track::onEnabledChanged)); - model_track->signalLockedChanged().connect(sigc::mem_fun(this, + modelTrack->signalLockedChanged().connect(sigc::mem_fun(this, &Track::onLockedChanged)); - model_track->signalNameChanged().connect(sigc::mem_fun(this, + modelTrack->signalNameChanged().connect(sigc::mem_fun(this, &Track::onNameChanged)); } @@ -103,9 +103,9 @@ Track::get_header_widget() } shared_ptr -Track::get_model_track() const +Track::get_modelTrack() const { - return model_track; + return modelTrack; } int @@ -241,17 +241,17 @@ Track::onLockedChanged(bool) void Track::on_set_name() { - REQUIRE(model_track); + REQUIRE(modelTrack); Gtk::Window *window = dynamic_cast( timelineWidget.get_toplevel()); REQUIRE(window != NULL); dialogs::NameChooser dialog(*window, - _("Set Track Name"), model_track->get_name()); + _("Set Track Name"), modelTrack->get_name()); if(dialog.run() == RESPONSE_OK) - model_track->set_name(dialog.get_name()); + modelTrack->set_name(dialog.get_name()); } void @@ -263,33 +263,33 @@ Track::onNameChanged(std::string) void Track::on_remove_track() { - REQUIRE(model_track); + REQUIRE(modelTrack); boost::shared_ptr state = timelineWidget.get_state(); REQUIRE(state); - state->get_sequence()->remove_descendant_track(model_track); + state->get_sequence()->remove_descendant_track(modelTrack); } void Track::onToggleEnabled() { - bool status = model_track->getEnabled(); - model_track->setEnabled(!status); + bool status = modelTrack->getEnabled(); + modelTrack->setEnabled(!status); } void Track::onToggleLocked() { - bool status = model_track->getLocked(); - model_track->setLocked(!status); + bool status = modelTrack->getLocked(); + modelTrack->setLocked(!status); } void Track::updateEnableButton() { - REQUIRE (model_track); + REQUIRE (modelTrack); - if (model_track->getEnabled()) + if (modelTrack->getEnabled()) { enableButton.set_stock_id(Gtk::StockID("track_enabled"), WindowManager::MenuIconSize); enableButton.set_tooltip_text(_("Disable track")); @@ -304,9 +304,9 @@ Track::updateEnableButton() void Track::updateLockButton() { - REQUIRE (model_track); + REQUIRE (modelTrack); - if (model_track->getLocked()) + if (modelTrack->getLocked()) { lockButton.set_stock_id(Gtk::StockID("track_locked"), WindowManager::MenuIconSize); lockButton.set_tooltip_text(_("Unlock track")); @@ -321,8 +321,8 @@ Track::updateLockButton() void Track::updateName() { - REQUIRE(model_track); - titleMenuButton.set_label(model_track->get_name()); + REQUIRE(modelTrack); + titleMenuButton.set_label(modelTrack->get_name()); } } // namespace timeline diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 4707cb436..7e0668f31 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -75,7 +75,7 @@ public: Gtk::Widget& get_header_widget(); boost::shared_ptr - get_model_track() const; + get_modelTrack() const; /** * Return the visual height of the track in pixels. @@ -190,7 +190,7 @@ private: protected: TimelineWidget &timelineWidget; - boost::shared_ptr model_track; + boost::shared_ptr modelTrack; private: /** From 7f615d734f0380447be3868c93c9c234825dbb2e Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Sun, 2 Jan 2011 16:57:14 +0100 Subject: [PATCH 058/140] WIP: make clips selectable in timeline --- src/gui/model/clip.cpp | 6 ++ src/gui/model/clip.hpp | 8 ++ src/gui/widgets/timeline-widget.cpp | 2 +- src/gui/widgets/timeline-widget.hpp | 3 +- .../widgets/timeline/timeline-arrow-tool.cpp | 92 +++++++++++++------ .../widgets/timeline/timeline-arrow-tool.hpp | 75 ++++++++------- src/gui/widgets/timeline/timeline-body.cpp | 6 ++ src/gui/widgets/timeline/timeline-body.hpp | 3 + .../widgets/timeline/timeline-clip-track.cpp | 15 +++ .../widgets/timeline/timeline-clip-track.hpp | 15 ++- src/gui/widgets/timeline/timeline-clip.cpp | 82 +++++++++-------- src/gui/widgets/timeline/timeline-clip.hpp | 34 +++---- .../timeline/timeline-header-widget.cpp | 2 +- .../timeline/timeline-layout-helper.cpp | 2 +- src/gui/widgets/timeline/timeline-track.cpp | 9 +- src/gui/widgets/timeline/timeline-track.hpp | 21 +++-- 16 files changed, 246 insertions(+), 129 deletions(-) diff --git a/src/gui/model/clip.cpp b/src/gui/model/clip.cpp index 95233b20f..1dc0f2e00 100644 --- a/src/gui/model/clip.cpp +++ b/src/gui/model/clip.cpp @@ -48,6 +48,12 @@ namespace model { return name; } + bool + Clip::isPlayingAt(lumiera::Time position) const + { + return (begin <= position && end >= position); + } + void Clip::setBegin(gavl_time_t begin) { diff --git a/src/gui/model/clip.hpp b/src/gui/model/clip.hpp index a024cfb67..2c8fd5add 100644 --- a/src/gui/model/clip.hpp +++ b/src/gui/model/clip.hpp @@ -26,6 +26,8 @@ #include #include "gui/gtk-lumiera.hpp" +#include "lib/lumitime.hpp" + // TODO: Remove once we get real measure of duration. // This is here *only* for purposes of testing the GUI. extern "C" { @@ -67,6 +69,12 @@ namespace model { const std::string getName() const; + /** + * Check whether or not the clip will be playing during the given time. + **/ + bool + isPlayingAt(lumiera::Time position) const; + /** * Sets the begin time of this clip. * @param[in] begin The new begin time to set this clip to. diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 89c098783..0e1b975a9 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -326,7 +326,7 @@ TimelineWidget::create_timeline_track_from_modelTrack( void TimelineWidget::remove_orphaned_tracks() -{ +{ std::map, boost::shared_ptr > orphan_track_map(trackMap); diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index b409d7fe1..7f45c0e17 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -102,7 +102,8 @@ public: */ void set_tool(timeline::ToolType tool_type); - boost::shared_ptr get_hovering_track() const; + boost::shared_ptr + get_hovering_track() const; public: /* ===== Signals ===== */ diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.cpp b/src/gui/widgets/timeline/timeline-arrow-tool.cpp index 67765288f..f0c109189 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.cpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.cpp @@ -26,41 +26,75 @@ namespace gui { namespace widgets { namespace timeline { -ArrowTool::ArrowTool(TimelineBody &timeline_body) : - Tool(timeline_body) -{ + ArrowTool::ArrowTool(TimelineBody &timelineBody) : + Tool(timelineBody) + { -} + } -ToolType -ArrowTool::get_type() const -{ - return Arrow; -} + ToolType + ArrowTool::get_type() const + { + return Arrow; + } -Gdk::Cursor -ArrowTool::get_cursor() const -{ - return Gdk::Cursor(Gdk::LEFT_PTR); -} + Gdk::Cursor + ArrowTool::get_cursor() const + { + return Gdk::Cursor(Gdk::LEFT_PTR); + } -void -ArrowTool::on_button_press_event(GdkEventButton* event) -{ - Tool::on_button_press_event(event); -} + void + ArrowTool::on_button_press_event(GdkEventButton* event) + { + REQUIRE (event != NULL); + Tool::on_button_press_event(event); -void -ArrowTool::on_button_release_event(GdkEventButton* event) -{ - Tool::on_button_release_event(event); -} + // Convert the mouse click position to a Time + boost::shared_ptr state = timelineBody.getTimelineWidget().get_state(); + REQUIRE(state); + const TimelineViewWindow &window = state->get_view_window(); + lumiera::Time tpoint = window.x_to_time(mousePoint.get_x()); -void -ArrowTool::on_motion_notify_event(GdkEventMotion *event) -{ - Tool::on_motion_notify_event(event); -} + // Get the clip, if any + boost::shared_ptr track = getHoveringTrack(); + boost::shared_ptr clip = track->getClipAt(tpoint); + + // Nothing to do if there is no clip + if (clip == boost::shared_ptr()) + return; + + clip->setSelected(true); + } + + void + ArrowTool::on_button_release_event(GdkEventButton* event) + { + REQUIRE (event != NULL); + Tool::on_button_release_event(event); + + boost::shared_ptr track = + getHoveringTrack(); + } + + void + ArrowTool::on_motion_notify_event(GdkEventMotion *event) + { + REQUIRE (event != NULL); + Tool::on_motion_notify_event(event); + + // We do not need to do anything if we are not dragging + if (!isDragging) + return; + } + + boost::shared_ptr + ArrowTool::getHoveringTrack () + { + boost::shared_ptr track( + timelineBody.getTimelineWidget().get_hovering_track()); + return track; + } } // namespace timeline } // namespace widgets diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.hpp b/src/gui/widgets/timeline/timeline-arrow-tool.hpp index be21b19c5..c9b7decab 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.hpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.hpp @@ -27,52 +27,63 @@ #define TIMELINE_ARROW_TOOL_HPP #include + #include "timeline-tool.hpp" +#include "gui/widgets/timeline-widget.hpp" +#include "timeline-body.hpp" +#include "timeline-track.hpp" + namespace gui { namespace widgets { namespace timeline { -/** - * A helper class to implement the timeline i-beam tool - */ -class ArrowTool : public Tool -{ -public: /** - * Constructor - * @param timeline_body The owner timeline body object + * A helper class to implement the timeline arrow tool */ - ArrowTool(TimelineBody &timeline_body); + class ArrowTool : public Tool + { + public: + /** + * Constructor + * @param timelineBody The owner timeline body object + */ + ArrowTool(TimelineBody &timelineBody); - /** - * Gets the type of tool represented by this class - */ - ToolType get_type() const; + /** + * Gets the type of tool represented by this class + */ + ToolType get_type() const; -protected: + protected: - /** - * Gets the cursor to display for this tool at this moment. - */ - Gdk::Cursor get_cursor() const; + /** + * Gets the cursor to display for this tool at this moment. + */ + Gdk::Cursor get_cursor() const; -protected: - /** - * The event handler for button press events. - */ - void on_button_press_event(GdkEventButton* event); + /** + * The event handler for button press events. + */ + void on_button_press_event(GdkEventButton* event); - /** - * The event handler for button release events. - */ - void on_button_release_event(GdkEventButton* event); + /** + * The event handler for button release events. + */ + void on_button_release_event(GdkEventButton* event); - /** - * The event handler for mouse move events. - */ - void on_motion_notify_event(GdkEventMotion *event); -}; + /** + * The event handler for mouse move events. + */ + void on_motion_notify_event(GdkEventMotion *event); + + private: + + boost::shared_ptr + getHoveringTrack (); + + bool selectionRectangleActive; + }; } // namespace timeline } // namespace widgets diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index f78f99ec1..7d5873f34 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -69,6 +69,12 @@ TimelineBody::~TimelineBody() WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here"); } +TimelineWidget& +TimelineBody::getTimelineWidget () const +{ + return timelineWidget; +} + ToolType TimelineBody::get_tool() const { diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index b6e6a3c29..9f6162beb 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -65,6 +65,9 @@ public: */ ~TimelineBody(); + TimelineWidget& + getTimelineWidget () const; + /** * Returns the type of the currently selected timeline tool. */ diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index 55b7169b8..6c41ddf83 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -76,6 +76,21 @@ namespace timeline { } } + boost::shared_ptr + ClipTrack::getClipAt(lumiera::Time position) const + { + std::pair, shared_ptr > + pair; + BOOST_FOREACH (pair, clipMap) + { + if (pair.first->isPlayingAt(position)) + return pair.second; + } + + // Nothing found + return boost::shared_ptr(); + } + //// private methods void diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index 49a29bb19..72f0f4040 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -50,10 +50,19 @@ namespace timeline { boost::shared_ptr track); /** - * + * Draw the track in the timeline. **/ - void draw_track(Cairo::RefPtr cairo, - TimelineViewWindow* const window) const; + void + draw_track(Cairo::RefPtr cairo, + TimelineViewWindow* const window) const; + + /** + * Gets the clip that is occupying the given time. If there is no track, return a NULL + * pointer. + * @param the given time + **/ + boost::shared_ptr + getClipAt(lumiera::Time position) const; private: diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index b09dd7b1b..a3f847cc8 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -26,54 +26,60 @@ namespace gui { namespace widgets { namespace timeline { -Clip::Clip(boost::shared_ptr clip) - : modelClip(clip), - selected(false) -{ - REQUIRE(modelClip); + Clip::Clip(boost::shared_ptr clip) + : modelClip(clip), + selected(false) + { + REQUIRE(modelClip); - // TODO: Connect signals - //modelClip->signalNameChanged().connect(mem_fun(this, - // &Clip::onNameChanged); -} + // TODO: Connect signals + //modelClip->signalNameChanged().connect(mem_fun(this, + // &Clip::onNameChanged); + } -void -Clip::draw_clip(Cairo::RefPtr cr, - TimelineViewWindow* const window) const -{ - REQUIRE(cr); - REQUIRE(window); - REQUIRE(modelClip); + void + Clip::draw_clip(Cairo::RefPtr cr, + TimelineViewWindow* const window) const + { + REQUIRE(cr); + REQUIRE(window); + REQUIRE(modelClip); - int x = window->time_to_x(modelClip->getBegin()); - int width = window->time_to_x( - modelClip->getEnd()) - window->time_to_x(modelClip->getBegin()); + int x = window->time_to_x(modelClip->getBegin()); + int width = window->time_to_x( + modelClip->getEnd()) - window->time_to_x(modelClip->getBegin()); - // Draw a rectangle for the clip - cr->rectangle(x, 1, width, 100-2); - // TODO: get height from the Timeline::Track + // Draw a rectangle for the clip + cr->rectangle(x, 1, width, 100-2); + // TODO: get height from the Timeline::Track - if (selected) - cr->set_source(Cairo::SolidPattern::create_rgb (0.3, 0.3, 0.3)); - else - cr->set_source(Cairo::SolidPattern::create_rgb (0.4, 0.4, 0.4)); - cr->fill_preserve(); + if (selected) + cr->set_source(Cairo::SolidPattern::create_rgb (0.4, 0.4, 0.8)); + else + cr->set_source(Cairo::SolidPattern::create_rgb (0.4, 0.4, 0.4)); + cr->fill_preserve(); - cr->set_source_rgb(0.25, 0.25, 0.25); - cr->stroke(); + cr->set_source_rgb(0.25, 0.25, 0.25); + cr->stroke(); - // Show the clip name - cr->rectangle(x, 1, width, 100-2); - cr->clip(); + // Show the clip name + cr->rectangle(x, 1, width, 100-2); + cr->clip(); - cr->move_to (x + 3, 12); - cr->set_source_rgb (1.0, 1.0, 1.0); + cr->move_to (x + 3, 12); + cr->set_source_rgb (1.0, 1.0, 1.0); - cr->set_font_size (9); - cr->show_text (modelClip->getName()); + cr->set_font_size (9); + cr->show_text (modelClip->getName()); - // TODO: Show thumbnails for clip -} + // TODO: Show thumbnails for clip + } + + void + Clip::setSelected(bool selected) + { + this->selected = selected; + } } // namespace timeline } // namespace widgets diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index 6ac571d06..4937e38bc 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -37,27 +37,29 @@ namespace gui { namespace widgets { namespace timeline { -class Clip : public model::Clip -{ -public: - Clip(boost::shared_ptr clip); + class Clip : public model::Clip + { + public: + Clip(boost::shared_ptr clip); - void draw_clip(Cairo::RefPtr cairo, - TimelineViewWindow* const window) const; + void draw_clip(Cairo::RefPtr cairo, + TimelineViewWindow* const window) const; - void - setSelected(bool state); + /** + * Sets the selected status of the clip. + **/ + void + setSelected (bool state); -private: + private: - boost::shared_ptr modelClip; + boost::shared_ptr modelClip; - /** - * True when this clip is selected in the GUI. - */ - bool selected; - -}; + /** + * True when this clip is selected in the GUI. + */ + bool selected; + }; } // namespace timeline } // namespace widgets diff --git a/src/gui/widgets/timeline/timeline-header-widget.cpp b/src/gui/widgets/timeline/timeline-header-widget.cpp index 1eac796d0..734e42dfd 100644 --- a/src/gui/widgets/timeline/timeline-header-widget.cpp +++ b/src/gui/widgets/timeline/timeline-header-widget.cpp @@ -160,7 +160,7 @@ TimelineHeaderWidget::on_expose_event(GdkEventExpose *event) REQUIRE(style); REQUIRE(gdkWindow); - shared_ptr modelTrack = track.get_modelTrack(); + shared_ptr modelTrack = track.getModelTrack(); REQUIRE(modelTrack); // Get the header box diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index ec653ad81..0e311cd7d 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -150,7 +150,7 @@ TimelineLayoutHelper::begin_dragging_track( dragPoint.get_y() - rect.get_y()); const shared_ptr modelTrack = - dragging_track->get_modelTrack(); + dragging_track->getModelTrack(); draggingTrackIter = iterator_from_track(modelTrack); dragBranchHeight = measure_branch_height(draggingTrackIter); diff --git a/src/gui/widgets/timeline/timeline-track.cpp b/src/gui/widgets/timeline/timeline-track.cpp index e2b22ce2e..2ac324f1f 100644 --- a/src/gui/widgets/timeline/timeline-track.cpp +++ b/src/gui/widgets/timeline/timeline-track.cpp @@ -103,7 +103,7 @@ Track::get_header_widget() } shared_ptr -Track::get_modelTrack() const +Track::getModelTrack() const { return modelTrack; } @@ -120,6 +120,13 @@ Track::get_expanded() const return expanded; } +boost::shared_ptr +Track::getClipAt(lumiera::Time) const +{ + // Default implementation returns empty pointer + return boost::shared_ptr(); +} + void Track::expand_collapse(ExpandDirection direction) { diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 7e0668f31..663327494 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -28,6 +28,7 @@ #include "gui/widgets/menu-button.hpp" #include "gui/widgets/mini-button.hpp" #include "gui/widgets/button-bar.hpp" +#include "timeline-clip.hpp" #include "timeline-header-container.hpp" #include "timeline-header-widget.hpp" @@ -75,7 +76,7 @@ public: Gtk::Widget& get_header_widget(); boost::shared_ptr - get_modelTrack() const; + getModelTrack() const; /** * Return the visual height of the track in pixels. @@ -109,24 +110,24 @@ public: * @see tick_expand_animation **/ float get_expand_animation_state() const; - + /** * Gets whether the branch is animation. * @return Returns true if the branch is animating, false if not. **/ bool is_expand_animating() const; - + /** * When this track is being animated, tick_expand_animation must be * called repeatedly to cause the animation to progress. - **/ + **/ void tick_expand_animation(); - + /** * Calculates the expander style, given the animation state. **/ Gtk::ExpanderStyle get_expander_style() const; - + /** * **/ @@ -139,6 +140,14 @@ public: TimelineViewWindow* const window) const = 0; + /** + * Gets the clip that is occupying the given time. + * The default implementation simply returns an empty pointer. + * @param the given time + **/ + virtual boost::shared_ptr + getClipAt(lumiera::Time position) const; + private: /** From 62a05d003c9901664830bb2a6ca752640aba1c5e Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Mon, 3 Jan 2011 10:24:08 +0100 Subject: [PATCH 059/140] add new baseclass Entity for things placed on timeline --- src/gui/Makefile.am | 2 + src/gui/widgets/timeline/timeline-clip.cpp | 3 +- src/gui/widgets/timeline/timeline-clip.hpp | 5 +- src/gui/widgets/timeline/timeline-entity.cpp | 36 ++++++++++ src/gui/widgets/timeline/timeline-entity.hpp | 71 ++++++++++++++++++++ 5 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/gui/widgets/timeline/timeline-entity.cpp create mode 100644 src/gui/widgets/timeline/timeline-entity.hpp diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index a63f5d1db..88b8ae341 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -120,6 +120,8 @@ gtk_gui_la_SOURCES = \ $(lumigui_srcdir)/widgets/timeline/timeline-clip-track.hpp \ $(lumigui_srcdir)/widgets/timeline/timeline-clip.cpp \ $(lumigui_srcdir)/widgets/timeline/timeline-clip.hpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-entity.cpp \ + $(lumigui_srcdir)/widgets/timeline/timeline-entity.hpp \ $(lumigui_srcdir)/widgets/timeline/timeline-group-track.cpp \ $(lumigui_srcdir)/widgets/timeline/timeline-group-track.hpp \ $(lumigui_srcdir)/widgets/timeline/timeline-header-container.cpp \ diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index a3f847cc8..e7d95fb3a 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -27,7 +27,8 @@ namespace widgets { namespace timeline { Clip::Clip(boost::shared_ptr clip) - : modelClip(clip), + : Entity(), + modelClip(clip), selected(false) { REQUIRE(modelClip); diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index 4937e38bc..9a8cceff9 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -27,8 +27,9 @@ #include "gui/gtk-lumiera.hpp" #include "gui/model/clip.hpp" -#include "timeline-view-window.hpp" #include "include/logging.h" +#include "timeline-entity.hpp" +#include "timeline-view-window.hpp" #ifndef TIMELINE_CLIP_HPP #define TIMELINE_CLIP_HPP @@ -37,7 +38,7 @@ namespace gui { namespace widgets { namespace timeline { - class Clip : public model::Clip + class Clip : public Entity { public: Clip(boost::shared_ptr clip); diff --git a/src/gui/widgets/timeline/timeline-entity.cpp b/src/gui/widgets/timeline/timeline-entity.cpp new file mode 100644 index 000000000..55229b85b --- /dev/null +++ b/src/gui/widgets/timeline/timeline-entity.cpp @@ -0,0 +1,36 @@ +/* + timeline-entity.cpp - Implementation of the timeline entity object + + Copyright (C) Lumiera.org + 2010, Stefan Kangas + + 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-entity.hpp + ** Declares the Timeline Entity class. + */ + +#ifndef TIMELINE_ENTITY_HPP +#define TIMELINE_ENTITY_HPP + +namespace gui { +namespace widgets { +namespace timeline { + + /** + * Base class for timeline entities. + * Everything that can be placed on the timeline is a timeline Entity. + */ + class Entity { + protected: + + /** + * Constructor + */ + Entity(); + + public: + + /** + * + */ + bool + getEnabled(); + + /** + * + */ + void + setEnabled(bool selected); + + private: + + /** + * True when this entity is enabled. + */ + bool enabled; + }; + +} // namespace timeline +} // namespace widgets +} // namespace gui + +#endif // TIMELINE_ENTITY_HPP From 944406f4a3c1860bddbf38dadb1cff86246c73c8 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Mon, 3 Jan 2011 10:30:45 +0100 Subject: [PATCH 060/140] Remove redundant '*' in many doxygen style comments --- src/gui/dialogs/dialog.hpp | 4 +- src/gui/dialogs/name-chooser.hpp | 4 +- src/gui/gtk-lumiera.hpp | 12 +-- src/gui/model/clip-track.hpp | 6 +- src/gui/model/clip.hpp | 22 ++--- src/gui/model/group-track.hpp | 6 +- src/gui/model/parent-track.hpp | 16 ++-- src/gui/model/sequence.hpp | 6 +- src/gui/model/track.hpp | 44 ++++----- src/gui/output/displayer.hpp | 2 +- src/gui/output/gdkdisplayer.hpp | 6 +- src/gui/output/xvdisplayer.hpp | 22 ++--- src/gui/panels/assets-panel.hpp | 6 +- src/gui/panels/panel.hpp | 36 ++++---- src/gui/panels/timeline-panel.hpp | 20 ++-- src/gui/panels/viewer-panel.hpp | 10 +- src/gui/util/rectangle.hpp | 4 +- src/gui/widgets/button-bar.hpp | 8 +- src/gui/widgets/menu-button.hpp | 30 +++--- src/gui/widgets/mini-button.hpp | 12 +-- src/gui/widgets/panel-bar.hpp | 26 +++--- src/gui/widgets/timeline-widget.hpp | 26 +++--- src/gui/widgets/timeline/timeline-body.hpp | 4 +- .../widgets/timeline/timeline-clip-track.hpp | 18 ++-- src/gui/widgets/timeline/timeline-clip.hpp | 2 +- .../timeline/timeline-header-container.hpp | 12 +-- .../timeline/timeline-header-widget.hpp | 26 +++--- .../timeline/timeline-layout-helper.hpp | 92 +++++++++---------- src/gui/widgets/timeline/timeline-ruler.hpp | 4 +- src/gui/widgets/timeline/timeline-state.hpp | 30 +++--- src/gui/widgets/timeline/timeline-tool.hpp | 4 +- src/gui/widgets/timeline/timeline-track.hpp | 44 ++++----- .../widgets/timeline/timeline-view-window.hpp | 14 +-- src/gui/window-manager.hpp | 32 +++---- src/gui/workspace/actions.hpp | 6 +- src/gui/workspace/panel-manager.hpp | 88 +++++++++--------- src/lib/observable-list.hpp | 6 +- src/lib/tree.hpp | 2 +- 38 files changed, 356 insertions(+), 356 deletions(-) diff --git a/src/gui/dialogs/dialog.hpp b/src/gui/dialogs/dialog.hpp index c72949b3b..049ea19c2 100644 --- a/src/gui/dialogs/dialog.hpp +++ b/src/gui/dialogs/dialog.hpp @@ -32,12 +32,12 @@ namespace dialogs { /** * The space in pixels to pad the border of Lumiera dialog boxes. - **/ + */ static const int BorderPadding = 8; /** * The spacing for VBoxes and HBoxes in Lumiera dialogs. - **/ + */ static const int BoxSpacing = 6; } // namespace dialogs diff --git a/src/gui/dialogs/name-chooser.hpp b/src/gui/dialogs/name-chooser.hpp index 52a156c03..8b020c51f 100644 --- a/src/gui/dialogs/name-chooser.hpp +++ b/src/gui/dialogs/name-chooser.hpp @@ -45,7 +45,7 @@ public: * @param title The string for the title of this dialog. * @param default_name The name that will be shown by default in the * edit box of the dialog. - **/ + */ NameChooser(Gtk::Window &parent, const Glib::ustring title, const Glib::ustring default_name); @@ -53,7 +53,7 @@ public: * Gets the current name of the chosen in the dialog. * @return Returns the name currently typed into the edit box of the * dialog. - **/ + */ const Glib::ustring get_name() const; private: diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index bc26c1d96..de7c509bf 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -82,33 +82,33 @@ public: /** * Returns the name of the application - **/ + */ static const Glib::ustring get_app_title(); /** * Returns the version number of the application - **/ + */ static const Glib::ustring get_app_version(); /** * Returns the copyright of the application - **/ + */ static const Glib::ustring get_app_copyright(); /** * Returns the website of the application - **/ + */ static const Glib::ustring get_app_website(); /** * Returns tn alphabetical list of the application's authors - **/ + */ static const std::vector get_app_authors(); protected: /** * The application window manager object - **/ + */ WindowManager windowManager; }; diff --git a/src/gui/model/clip-track.hpp b/src/gui/model/clip-track.hpp index ed4957581..288b41bf4 100644 --- a/src/gui/model/clip-track.hpp +++ b/src/gui/model/clip-track.hpp @@ -41,18 +41,18 @@ namespace model { public: /** * Constructor - **/ + */ ClipTrack(); /** * Gets a string representation of the track that is suitable for debugging - **/ + */ std::string print_track(); /** * Gets the list of clips associated with this track. - **/ + */ lumiera::observable_list< boost::shared_ptr >& getClipList(void); diff --git a/src/gui/model/clip.hpp b/src/gui/model/clip.hpp index 2c8fd5add..ab3e7d2a3 100644 --- a/src/gui/model/clip.hpp +++ b/src/gui/model/clip.hpp @@ -48,58 +48,58 @@ namespace model { public: /** * Constructor - **/ + */ Clip(); /** * Gets the begin time of this clip. - **/ + */ gavl_time_t getBegin() const; /** * Gets the end time of this clip. - **/ + */ gavl_time_t getEnd() const; /** * Gets the name of this clip. - **/ + */ const std::string getName() const; /** * Check whether or not the clip will be playing during the given time. - **/ + */ bool isPlayingAt(lumiera::Time position) const; /** * Sets the begin time of this clip. * @param[in] begin The new begin time to set this clip to. - **/ + */ void setBegin(gavl_time_t begin); /** * Sets the end time of this clip. * @param[in] end The new end time to set this clip to. - **/ + */ void setEnd(gavl_time_t end); /** * Sets the name of this clip. * @param[in] name The new name to set this clip to. - **/ + */ void setName(const std::string &name); /** * A signal which fires when the name changes. * @return Returns the signal. The signal sends the new name for the clip. - **/ + */ sigc::signal signalNameChanged() const; @@ -107,12 +107,12 @@ namespace model { /** * The name of this clip. - **/ + */ std::string name; /** * A signal which fires when the name changes. - **/ + */ sigc::signal nameChangedSignal; // TODO: Use a good measure of duration, probably TimeSpan. diff --git a/src/gui/model/group-track.hpp b/src/gui/model/group-track.hpp index 6dc15db31..8f729ec59 100644 --- a/src/gui/model/group-track.hpp +++ b/src/gui/model/group-track.hpp @@ -33,20 +33,20 @@ namespace model { /** * A class representation of a grouping of tracks. - **/ + */ class GroupTrack : public ParentTrack { public: /** * Constructor - **/ + */ GroupTrack(); /** * Produces a human readable debug string representation of this * track. * @return Returns the human readable string. - **/ + */ std::string print_track(); }; diff --git a/src/gui/model/parent-track.hpp b/src/gui/model/parent-track.hpp index 89967eccc..9112c86f7 100644 --- a/src/gui/model/parent-track.hpp +++ b/src/gui/model/parent-track.hpp @@ -37,7 +37,7 @@ namespace model { /** * ParentTrack is the abstract base class of all tracks that can parent * children. - **/ + */ class ParentTrack : public Track, public boost::enable_shared_from_this @@ -45,20 +45,20 @@ class ParentTrack : protected: /** * Constructor - **/ + */ ParentTrack(); public: /** * Gets a read-only reference to the the list of child tracks. - **/ + */ const std::list< boost::shared_ptr >& get_child_tracks() const; /** * Gets read-write access to the list of child tracks. - **/ + */ lumiera::observable_list< boost::shared_ptr >& get_child_track_list(); @@ -66,7 +66,7 @@ public: * Returns true if this track can own any child tracks. * @return Returns true because all classed derrived from ParentTrack * can. - **/ + */ bool can_host_children() const; /** @@ -74,7 +74,7 @@ public: * track. * @param The model track to try and remove. * @return Returns true if the track was successfully removed. - **/ + */ bool remove_descendant_track(const boost::shared_ptr track); /** @@ -83,14 +83,14 @@ public: * @param child The child track to find the parent of. * @return Returns the parent track if one was found, or an empty * shared_ptr if none was found. - **/ + */ boost::shared_ptr find_descendant_track_parent(boost::shared_ptr child); protected: /** * The internal list of child tracks of this parent. - **/ + */ lumiera::observable_list< boost::shared_ptr > tracks; }; diff --git a/src/gui/model/sequence.hpp b/src/gui/model/sequence.hpp index ae4f59a2f..45b9a43bf 100644 --- a/src/gui/model/sequence.hpp +++ b/src/gui/model/sequence.hpp @@ -37,20 +37,20 @@ class Track; /** * A class representation of a sequence. * @remarks Sequence objects are also the roots of track trees. - **/ + */ class Sequence : public ParentTrack { public: /** * Constructor - **/ + */ Sequence(); /** * Produces a human readable debug string representation of this * track. * @return Returns the human readable string. - **/ + */ std::string print_track(); }; diff --git a/src/gui/model/track.hpp b/src/gui/model/track.hpp index 4aebc95ee..7e6b9085a 100644 --- a/src/gui/model/track.hpp +++ b/src/gui/model/track.hpp @@ -37,65 +37,65 @@ class ParentTrack; /** * The model representation of a track. This is the abstract base class * for all types of track that are implemented. - **/ + */ class Track { protected: /** * Constructor - **/ + */ Track(); public: /** * Returns true if this track can own any child tracks. - **/ + */ virtual bool can_host_children () const; /** * Gets the list of child tracks. - **/ + */ virtual const std::list< boost::shared_ptr >& get_child_tracks () const; /** * Gets the enabled status of this track, i.e. if the track is to be rendered. - **/ + */ bool getEnabled () const; /** * Gets the locked status of this track, i.e. if the track can be edited. - **/ + */ bool getLocked () const; /** * Gets the name of this track. - **/ + */ const std::string get_name () const; /** * Sets the enabled status of this track, i.e. if the track is to be rendered. * @param[in] name The new enabled status. - **/ + */ void setEnabled (bool enabled); /** * Gets the locked status of this track, i.e. if the track can be edited. * @param[in] name The new locked status. - **/ + */ void setLocked (bool locked); /** * Sets the name of this track. * @param[in] name The new name to set this track to. - **/ + */ void set_name (const std::string &name); @@ -105,7 +105,7 @@ public: * @param child The child track to find the parent of. * @return Returns the parent track if one was found, or an empty * shared_ptr if none was found. - **/ + */ virtual boost::shared_ptr find_descendant_track_parent (boost::shared_ptr child); @@ -113,7 +113,7 @@ public: * A signal which fires when the enabled status changes. * @return Returns the signal. The signal sends the new name for the * track. - **/ + */ sigc::signal signalEnabledChanged () const; @@ -121,7 +121,7 @@ public: * A signal which fires when the locked status changes changes. * @return Returns the signal. The signal sends the new name for the * track. - **/ + */ sigc::signal signalLockedChanged () const; @@ -129,7 +129,7 @@ public: * A signal which fires when the name changes. * @return Returns the signal. The signal sends the new name for the * track. - **/ + */ sigc::signal signalNameChanged () const; @@ -137,7 +137,7 @@ public: * A debugging helper function that prints this track, and all it's * child tracks in a human-readable form. * @return Returns the human readable string. - **/ + */ std::string print_branch (); @@ -145,7 +145,7 @@ public: * A pure-virtual function which is the base of functions that print * this track in human readable form. * @return Returns the human readable string. - **/ + */ virtual std::string print_track () = 0; @@ -153,7 +153,7 @@ protected: /** * An object used internally as a return value for when there's no * children. - **/ + */ static const std::list< boost::shared_ptr > NoChildren; /** @@ -161,14 +161,14 @@ protected: * @param indentation The level of recursion into the tree. This value * is used to specify the width of indentation to print with. * @return Returns the human readable string. - **/ + */ std::string print_branch_recursive (const unsigned int indentation); private: /** * The name of this track. - **/ + */ std::string name; /** @@ -183,17 +183,17 @@ private: /** * A signal which fires when the enabled status changes. - **/ + */ sigc::signal enabledChangedSignal; /** * A signal which fires when the locked status changes. - **/ + */ sigc::signal lockedChangedSignal; /** * A signal which fires when the name changes. - **/ + */ sigc::signal nameChangedSignal; }; diff --git a/src/gui/output/displayer.hpp b/src/gui/output/displayer.hpp index 1ec3ed6be..5c7fcc796 100644 --- a/src/gui/output/displayer.hpp +++ b/src/gui/output/displayer.hpp @@ -35,7 +35,7 @@ namespace output { /** * Supported Displayer formats - **/ + */ typedef enum { DISPLAY_NONE, DISPLAY_YUV, diff --git a/src/gui/output/gdkdisplayer.hpp b/src/gui/output/gdkdisplayer.hpp index 291a20503..ed7df23b1 100644 --- a/src/gui/output/gdkdisplayer.hpp +++ b/src/gui/output/gdkdisplayer.hpp @@ -43,7 +43,7 @@ namespace output { /** * GdkDisplayer is a class which is responsible for rendering a video * image via GDK. - **/ + */ class GdkDisplayer : public Displayer { public: @@ -56,7 +56,7 @@ public: * must be greater than zero. * @param[in] height The height of the video image in pixels. This * value must be greater than zero. - **/ + */ GdkDisplayer( Gtk::Widget *drawing_area, int width, int height ); /** @@ -79,7 +79,7 @@ private: /** * The widget that video will be drawn into. * @remarks This value must be a valid pointer. - **/ + */ Gtk::Widget *drawingArea; }; diff --git a/src/gui/output/xvdisplayer.hpp b/src/gui/output/xvdisplayer.hpp index 9c7e58208..362928717 100644 --- a/src/gui/output/xvdisplayer.hpp +++ b/src/gui/output/xvdisplayer.hpp @@ -49,7 +49,7 @@ namespace output { /** * XvDisplayer is a class which is responsible for rendering a video * image via XVideo. - **/ + */ class XvDisplayer : public Displayer { public: @@ -61,12 +61,12 @@ public: * must be greater than zero. * @param height The height of the video image in pixels. This value * must be greater than zero. - **/ + */ XvDisplayer( Gtk::Widget *drawing_area, int width, int height ); /** * Destructor - **/ + */ ~XvDisplayer(); /** @@ -89,45 +89,45 @@ private: * port. * @remarks This value is false until the constructor has finished * successfully. - **/ + */ bool gotPort; /** * The current port being used. * @remarks This value is meaninless unless gotPort is true. - **/ + */ unsigned int grabbedPort; /** * The widget that video will be drawn into. * @remarks This value must be a valid pointer. - **/ + */ Gtk::Widget *drawingArea; /** * The display that video will be drawn into. - **/ + */ Display *display; /** * The X11 window that video will be drawn into. - **/ + */ Window window; /** * The graphics context which will be used when rednering video. - **/ + */ GC gc; /** * The shared memory image object which video will be written into. - **/ + */ XvImage *xvImage; /** * Info about the shared memory segment. * @remarks shmInfo.shmaddr is set to NULL, when the SHM is detached. - **/ + */ XShmSegmentInfo shmInfo; }; diff --git a/src/gui/panels/assets-panel.hpp b/src/gui/panels/assets-panel.hpp index e293c1f36..18f8093d6 100644 --- a/src/gui/panels/assets-panel.hpp +++ b/src/gui/panels/assets-panel.hpp @@ -38,20 +38,20 @@ public: * Contructor. * @param panel_manager The owner panel manager widget. * @param dock_item The GdlDockItem that will host this panel. - **/ + */ AssetsPanel(workspace::PanelManager &panel_manager, GdlDockItem *dock_item); /** * Get the title of the panel. * @return Returns a pointer to the string title of the panel. - **/ + */ static const char* get_title(); /** * Get the stock id for this type panel. * @return Returns a pointer to the string stock id of the panel. - **/ + */ static const gchar* get_stock_id(); protected: diff --git a/src/gui/panels/panel.hpp b/src/gui/panels/panel.hpp index 0dd39e1e5..ef287d8f6 100644 --- a/src/gui/panels/panel.hpp +++ b/src/gui/panels/panel.hpp @@ -53,7 +53,7 @@ protected: * @param dock_item The GdlDockItem that will host this panel. * @param long_name This title of the panel * @param stock_id The stock_id of this panel. - **/ + */ Panel(workspace::PanelManager &panel_manager, GdlDockItem *dock_item, const gchar* long_name, const gchar *stock_id); @@ -61,7 +61,7 @@ protected: public: /** * Destructor - **/ + */ ~Panel(); /** @@ -73,7 +73,7 @@ public: * Shows or hides the panel. * @param show A value of true will show the panel, false will hide * it. - **/ + */ void show(bool show = true); /** @@ -83,53 +83,53 @@ public: /** * Iconifys the panel. - **/ + */ void iconify(); /** * Returns true if the panel is currently iconified. - **/ + */ bool is_iconified() const; /** * Locks or unlocks the panel. * @param show A value of true will lock the panel, false will unlock * it. - **/ + */ void lock(bool show = true); /** * Returns true if the panel is currently locked. - **/ + */ bool is_locked() const; /** * Returns a reference to the owner panel manager object. - **/ + */ workspace::PanelManager& get_panel_manager(); public: /** * A signal that fires when the dock item is hidden. - **/ + */ sigc::signal& signal_hide_panel(); protected: /** * Returns a reference to the owner workspace window object. - **/ + */ workspace::WorkspaceWindow& get_workspace_window(); /** * Returns a reference to the project - **/ + */ model::Project& get_project(); /** * Returns a reference to the controller - **/ + */ controller::Controller& get_controller(); private: @@ -137,35 +137,35 @@ private: /** * An event handler for when dockItem is hidden. * @param func_data A pointer to the panel that owns dock_item - **/ + */ static void on_item_hidden(GdlDockItem*, Panel *panel); protected: /** * The owner panel manager object. - **/ + */ workspace::PanelManager &panelManager; /** * The owner dock item widget that will host the widgets in this * panel. - **/ + */ GdlDockItem* dockItem; /** * A signal that fires when the dock item is hidden. - **/ + */ sigc::signal hidePanelSignal; /** * The id of the hide panel handler. - **/ + */ gulong hide_panel_handler_id; /** * The panel bar to attach to the panel grip. - **/ + */ widgets::PanelBar panelBar; }; diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index afd3b9faa..aef78f9e3 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -51,25 +51,25 @@ public: * Constructor. * @param panel_manager The owner panel manager widget. * @param dock_item The GdlDockItem that will host this panel. - **/ + */ TimelinePanel(workspace::PanelManager &panel_manager, GdlDockItem *dock_item); /** * Destructor - **/ + */ ~TimelinePanel(); /** * Get the title of the panel. * @return Returns a pointer to the string title of the panel. - **/ + */ static const char* get_title(); /** * Get the stock id for this type panel. * @return Returns a pointer to the string stock id of the panel. - **/ + */ static const gchar* get_stock_id(); private: @@ -91,13 +91,13 @@ private: /** * An event handler for when the list of sequences changes. - **/ + */ void on_sequence_list_changed(); /** * An event handler for when a new sequence is chosen in the * sequenceChooser. - **/ + */ void on_sequence_chosen(); private: @@ -125,26 +125,26 @@ private: /** * The definition of the sequence chooser combo box columns - **/ + */ class SequenceChooserColumns : public Gtk::TreeModel::ColumnRecord { public: /** * Constructor - **/ + */ SequenceChooserColumns() { add(nameColumn); add(sequenceColumn); } /** * An invisible column which will be used to identify the sequence * of a row. - **/ + */ Gtk::TreeModelColumn< boost::weak_ptr > sequenceColumn; /** * The column to use as the label for the combo box widget items. - **/ + */ Gtk::TreeModelColumn< Glib::ustring > nameColumn; }; diff --git a/src/gui/panels/viewer-panel.hpp b/src/gui/panels/viewer-panel.hpp index 3a8e23b28..ae41516c1 100644 --- a/src/gui/panels/viewer-panel.hpp +++ b/src/gui/panels/viewer-panel.hpp @@ -36,7 +36,7 @@ namespace panels { /** * A panel to display the video output. - **/ + */ class ViewerPanel : public Panel { public: @@ -44,20 +44,20 @@ public: * Contructor. * @param panel_manager The owner panel manager widget. * @param dock_item The GdlDockItem that will host this panel. - **/ + */ ViewerPanel(workspace::PanelManager &panel_manager, GdlDockItem *dock_item); /** * Get the title of the panel. * @return Returns a pointer to the string title of the panel. - **/ + */ static const char* get_title(); /** * Get the stock id for this type panel. * @return Returns a pointer to the string stock id of the panel. - **/ + */ static const gchar* get_stock_id(); protected: @@ -68,7 +68,7 @@ protected: /** * The video display widget, which will display the video. - **/ + */ widgets::VideoDisplayWidget display; }; diff --git a/src/gui/util/rectangle.hpp b/src/gui/util/rectangle.hpp index eb4110e83..eaf9ea279 100644 --- a/src/gui/util/rectangle.hpp +++ b/src/gui/util/rectangle.hpp @@ -38,14 +38,14 @@ namespace util { * @param rect The rect to test. * @return Returns true if the point is within the rectangle, false if * not. - **/ + */ bool pt_in_rect(const Gdk::Point &point, const Gdk::Rectangle &rect); /** * Tests whether two rectangles overlap. * @param a The first rectangle. * @param b The second rectangle. - **/ + */ bool rects_overlap(const Gdk::Rectangle &a, const Gdk::Rectangle &b); } // util diff --git a/src/gui/widgets/button-bar.hpp b/src/gui/widgets/button-bar.hpp index 237f0270c..85926ba1c 100644 --- a/src/gui/widgets/button-bar.hpp +++ b/src/gui/widgets/button-bar.hpp @@ -33,26 +33,26 @@ namespace widgets { /** * A modified toolbar widget for use in dialogs. - **/ + */ class ButtonBar : public Gtk::Box { public: /** * Constructor - **/ + */ ButtonBar(); /** * Append a widget to the button bar. * @param widget The button to append. - **/ + */ void append(Widget &widget); /** * Append a button to the button bar, and connect a click event. * @param button The button to append. * @param clicked_slot The slot to connect. - **/ + */ template void append(MiniWrapper& button, const sigc::slot& clicked_slot) { diff --git a/src/gui/widgets/menu-button.hpp b/src/gui/widgets/menu-button.hpp index fe32f677d..422a93adb 100644 --- a/src/gui/widgets/menu-button.hpp +++ b/src/gui/widgets/menu-button.hpp @@ -33,7 +33,7 @@ namespace widgets { /** * A button that display a menu when clicked on. - **/ + */ class MenuButton : public Gtk::ToggleButton { public: @@ -44,7 +44,7 @@ public: * such as a Gtk::Pixmap or Gtk::Box. If you just wish to add a * Gtk::Label, you may want to use the * Gtk::MenuButton(const Glib::ustring& label) ctor directly instead. - **/ + */ MenuButton(); /** @@ -52,7 +52,7 @@ public: * item. * @remarks Stock ids have identifiers like Gtk::Stock::OK and * Gtk::Stock::APPLY. - **/ + */ MenuButton(const Gtk::StockID& stock_id); /** @@ -60,7 +60,7 @@ public: * @remarks Create a button with the given label inside. You won't be * able to add a widget in this button since it already has a * Gtk::Label in it - **/ + */ MenuButton(const Glib::ustring& label, bool mnemonic=false); /** @@ -68,29 +68,29 @@ public: * on. * @return Returns a reference to the menu that will be clicked on. * This reference can be used to populate the menu with items. - **/ + */ Gtk::Menu& get_menu(); /** * Pops up the menu. - **/ + */ void popup(); protected: /** * An internal method which sets up the button at creat time. - **/ + */ void setup_button(); /** * An event handler for when the button is pressed. - **/ + */ void on_pressed(); /** * An event handler for when the menu is closed. - **/ + */ void on_menu_deactivated(); private: @@ -104,35 +104,35 @@ private: * coordinates. * @param push_in This value is set to true if the menu should be * pushed in if it collides with the edge of the screen. - **/ + */ void on_menu_position(int& x, int& y, bool& push_in); private: /** * The hBox for the layout of image, caption and arrow. - **/ + */ Gtk::HBox hBox; /** * The image that will optionally display an icon. - **/ + */ Gtk::Image image; /** * The caption text label to show on the button. - **/ + */ Gtk::Label caption; /** * The arrow widget that will be displayed to hint the user that this * button is a drop-down. - **/ + */ Gtk::Arrow arrow; /** * The internal menu object which is the popup menu of this widget. - **/ + */ Gtk::Menu menu; }; diff --git a/src/gui/widgets/mini-button.hpp b/src/gui/widgets/mini-button.hpp index 9b6c39565..5e09d48f0 100644 --- a/src/gui/widgets/mini-button.hpp +++ b/src/gui/widgets/mini-button.hpp @@ -33,7 +33,7 @@ namespace widgets { /** * A wrapper for ToolButton-like Button widgets - **/ + */ template class MiniWrapper : public T { @@ -46,7 +46,7 @@ public: * @param icon_size The size of the image to show. * @remarks Stock ids have identifiers like Gtk::Stock::OK and * Gtk::Stock::APPLY. - **/ + */ MiniWrapper(const Gtk::StockID& stock_id, const Gtk::IconSize icon_size = Gtk::ICON_SIZE_LARGE_TOOLBAR) : image(stock_id, icon_size) @@ -60,7 +60,7 @@ public: * Sets a new image from a stock-id for this button. * @param stock_id The stock_id of the image. * @param icon_size The size of the image to show. - **/ + */ void set_stock_id(const Gtk::StockID& stock_id, const Gtk::IconSize icon_size = Gtk::ICON_SIZE_LARGE_TOOLBAR) { @@ -71,18 +71,18 @@ private: /** * The image widget for the button. - **/ + */ Gtk::Image image; }; /** * A ToolButton-like widget - **/ + */ typedef MiniWrapper MiniButton; /** * A ToggleToolButton-like widget - **/ + */ typedef MiniWrapper MiniToggleButton; } // gui diff --git a/src/gui/widgets/panel-bar.hpp b/src/gui/widgets/panel-bar.hpp index d9f9d99e1..507cb5063 100644 --- a/src/gui/widgets/panel-bar.hpp +++ b/src/gui/widgets/panel-bar.hpp @@ -39,7 +39,7 @@ namespace widgets { /** * A container widget for widgets to be displayed on GDL panels grips. - **/ + */ class PanelBar : public Gtk::Box { public: @@ -49,21 +49,21 @@ public: * @param owner_panel The panel that is the parent of this panel bar. * @param stock_id The stock id with a name and an icon for this * panel. - **/ + */ PanelBar(panels::Panel &owner_panel, const gchar *stock_id); private: /** * Sets up panelButton, populating it with menu items. - **/ + */ void setup_panel_button(); private: /** * An override to intercept realize events. - **/ + */ void on_realize(); /** @@ -75,7 +75,7 @@ private: /** * An override to intercept size allocate events. - **/ + */ void on_size_allocate(Gtk::Allocation& allocation); private: @@ -84,50 +84,50 @@ private: * An event handler for when a panel type is chosen. * @param type_index The index of the panel description that will be * instantiated. - **/ + */ void on_panel_type(int type_index); /** * An event handler for when the "Hide" menu item is clicked - **/ + */ void on_hide(); /** * Event handler for when the "Lock" menu item is clicked - **/ + */ void on_lock(); /** * Event handler for when the split panel menu item is clicked * @param split_direction The direction to split in. - **/ + */ void on_split_panel(Gtk::Orientation split_direction); private: /** * A reference to the owner panel object. - **/ + */ panels::Panel &panel; /** * The panel menu drop-down button widget, that will be displayed in * the corner of the bar. - **/ + */ MenuButton panelButton; /** * A pointer to the lock menu item. * @remarks This value will remain NULL until after setup_panel_button * has been called. - **/ + */ Gtk::CheckMenuItem *lockItem; /** * The bar window. * @remarks This window is used only to set the cursor as an arrow for * any child widgets. - **/ + */ Glib::RefPtr window; }; diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index 7f45c0e17..f27479d60 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -75,13 +75,13 @@ public: * Gets a pointer to the current state object. * @return The state object that the timeline widget is currently * working with. - **/ + */ boost::shared_ptr get_state(); /** * Replaces the current TimelineState object with another. * @param new_state The new state to swap in. - **/ + */ void set_state(boost::shared_ptr new_state); /** @@ -89,7 +89,7 @@ public: * given point on the timeline still. * @param zoom_size The number of steps to zoom by. The scale factor * is 1.25^(-zoom_size). - **/ + */ void zoom_view(int zoom_size); /** @@ -140,7 +140,7 @@ private: /** * Updates the timeline widget to match the state of the track tree. - **/ + */ void update_tracks(); void freeze_update_tracks(); @@ -150,7 +150,7 @@ private: /** * Ensures timeline UI tracks have been created for every model track * present in sequence. - **/ + */ void create_timeline_tracks(); /** @@ -158,7 +158,7 @@ private: * creating UI timeline tracks for each model track if they don't * already exist in trackMap. * @param list The parent track of the branch. - **/ + */ void create_timeline_tracks_from_branch( boost::shared_ptr modelTrack); @@ -167,7 +167,7 @@ private: * @param modelTrack The model track to create a timeline track from. * @return The timeline track created, or an empty shared_ptr if * modelTrack has an unreckognised type (this is an error condition). - **/ + */ boost::shared_ptr create_timeline_track_from_modelTrack( boost::shared_ptr modelTrack); @@ -175,7 +175,7 @@ private: /** * Removes any UI tracks which no longer have corresponding model * tracks present in the sequence. - **/ + */ void remove_orphaned_tracks(); void search_orphaned_tracks_in_branch( @@ -190,7 +190,7 @@ private: * @returns The timeline UI track found, or an empty shared_ptr if * modelTrack has no corresponding timeline UI track (this is an * error condition). - **/ + */ boost::shared_ptr lookup_timeline_track( boost::shared_ptr modelTrack) const; @@ -209,7 +209,7 @@ private: /** * An event handler that receives notifications for when the * sequence's track tree has been changed. - **/ + */ void on_track_list_changed(); void on_playback_period_drag_released(); @@ -221,7 +221,7 @@ private: /** * Helper to get the sequence object from the state. * @return Returns a shared pointer to the sequence. - **/ + */ boost::shared_ptr sequence() const; // ----- Other Functions ----- // @@ -234,7 +234,7 @@ protected: /** * The state that will be used as the data source for this timeline * widget. - **/ + */ boost::shared_ptr state; // Model Data @@ -245,7 +245,7 @@ protected: * @remarks The tree structure is maintained by the model, and as the * widget is updated with update_tracks, timeline tracks are added and * removed from the map in correspondence with the tree. - **/ + */ std::map, boost::shared_ptr > trackMap; diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 9f6162beb..6a4bb0b28 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -119,7 +119,7 @@ protected: /** * The event handler for when the TimelineWidget's state object is * replaced. - **/ + */ void on_state_changed(); /* ===== Internals ===== */ @@ -157,7 +157,7 @@ private: * A helper function to get the view window * @remarks This function must not be called unless the TimlineWidget * has a valid state. - **/ + */ TimelineViewWindow& view_window() const; /** diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index 72f0f4040..9fef82512 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -45,13 +45,13 @@ namespace timeline { public: /** * Constructor. - **/ + */ ClipTrack(TimelineWidget &timelineWidget, boost::shared_ptr track); /** * Draw the track in the timeline. - **/ + */ void draw_track(Cairo::RefPtr cairo, TimelineViewWindow* const window) const; @@ -60,7 +60,7 @@ namespace timeline { * Gets the clip that is occupying the given time. If there is no track, return a NULL * pointer. * @param the given time - **/ + */ boost::shared_ptr getClipAt(lumiera::Time position) const; @@ -68,40 +68,40 @@ namespace timeline { /** * Ensures timeline UI clips have been created for every model clip in track. - **/ + */ void createTimelineClips(); /** * Gets the modelTrack as a ClipTrack. - **/ + */ boost::shared_ptr getModelTrack (); /** * An event handler that receives notifications for when the models clip list has been * changed. - **/ + */ void onClipListChanged(); /** * Removes any UI clips which no longer have corresponding model clips present in the * sequence. - **/ + */ void removeOrphanedClips(); /** * Update the attached timeline clips. - **/ + */ void updateClips(); /** * The clipMap maps model clips to timeline widget clips which are responsible for the * UI representation of a clip. - **/ + */ std::map, boost::shared_ptr > clipMap; diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index 9a8cceff9..c8e6d7151 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -48,7 +48,7 @@ namespace timeline { /** * Sets the selected status of the clip. - **/ + */ void setSelected (bool state); diff --git a/src/gui/widgets/timeline/timeline-header-container.hpp b/src/gui/widgets/timeline/timeline-header-container.hpp index dc1218cbd..094619ecc 100644 --- a/src/gui/widgets/timeline/timeline-header-container.hpp +++ b/src/gui/widgets/timeline/timeline-header-container.hpp @@ -119,14 +119,14 @@ private: /** * Applies a given function to all the widgets in the container. - **/ + */ void forall_vfunc(gboolean include_internals, GtkCallback callback, gpointer callback_data); /** * An event handler that is called when a widget is removed from the * container. - **/ + */ void on_remove(Widget* widget); /* ===== Events ===== */ @@ -167,7 +167,7 @@ private: * @param depth The depth within the tree of this track. This is used * to control the amount of indention. * @param offset The vertical offset of the headers in pixels. - **/ + */ void draw_header_decoration( boost::shared_ptr modelTrack, const Gdk::Rectangle &clip_rect); @@ -181,7 +181,7 @@ private: * track was found. * @remarks If the return value is going to be NULL, an ENSURE will * fail. - **/ + */ boost::shared_ptr lookup_timeline_track( boost::shared_ptr modelTrack); @@ -194,7 +194,7 @@ private: * top of the Z-order. * @param node The window of node's track header will be raised, as * well as all it's descendant nodes. - **/ + */ void raise_recursive( TimelineLayoutHelper::TrackTree::iterator_base node); @@ -230,7 +230,7 @@ private: * the user right clicks on the header container. * @remarks The context menu will be displayed when the user's right * click is not processed by track headers. - **/ + */ Gtk::Menu contextMenu; /** diff --git a/src/gui/widgets/timeline/timeline-header-widget.hpp b/src/gui/widgets/timeline/timeline-header-widget.hpp index b9c6b9bf3..f4ca7a1dd 100644 --- a/src/gui/widgets/timeline/timeline-header-widget.hpp +++ b/src/gui/widgets/timeline/timeline-header-widget.hpp @@ -38,7 +38,7 @@ class Track; /** * TimelineHeaderWidget is the base implementation of all header widgets * and acts as a containers for the header controls. - **/ + */ class TimelineHeaderWidget : public Gtk::Container { public: @@ -46,7 +46,7 @@ public: * Constructor * @param timeline_track The timeline track that owns this header * widget - **/ + */ TimelineHeaderWidget(timeline::Track &timeline_track); void set_child_widget(Widget& child); @@ -103,24 +103,24 @@ private: /** * Applies a given function to all the widgets in the container. - **/ + */ void forall_vfunc(gboolean include_internals, GtkCallback callback, gpointer callback_data); /** * A notification of when a widget is added to this container. - **/ + */ void on_add(Gtk::Widget* child); /** * A notification of when a widget is removed to this container. - **/ + */ void on_remove(Gtk::Widget* child); /** * An implementation of the a container function that specifies the * types of child widget that this widget will accept. - **/ + */ GtkType child_type_vfunc() const; /** @@ -137,19 +137,19 @@ private: /** * A reference to the timeline track that owns this widget. - **/ + */ timeline::Track &track; /** * The widget placed inside this container. * @remarks This value is set to NULL if the container is empty - **/ + */ Gtk::Widget* widget; /** * This value is true if the mouse hovering over the expander. * @remarks This value is updated by on_motion_notify_event - **/ + */ bool hoveringExpander; /** @@ -157,12 +157,12 @@ private: * expander. * @remarks This value is updated by on_button_press_event and * on_button_release_event - **/ + */ bool clickedExpander; /** * The widget's window object. - **/ + */ Glib::RefPtr gdkWindow; //----- Style Values -----// @@ -170,13 +170,13 @@ private: /** * The style value which indicates the amount of padding around each * header pixels. - **/ + */ int margin; /** * The style value which indicates the size to draw the expand button * in pixels. - **/ + */ int expand_button_size; }; diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index 4614d373a..dbd588a34 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -56,7 +56,7 @@ class TimelineLayoutHelper : public boost::noncopyable public: /** * Definition of the layout track tree type. - **/ + */ typedef lumiera::tree< boost::shared_ptr > TrackTree; public: @@ -64,7 +64,7 @@ public: * Constructor. * @param owner The timeline widget which is the owner of this helper * class. - **/ + */ TimelineLayoutHelper(TimelineWidget &owner); /** @@ -73,19 +73,19 @@ public: * @remarks The current layout tree will be deleted and replaced with * the clone. * @see add_branch - **/ + */ void clone_tree_from_sequence(); /** * Gets a reference to the helper's layout tree. * @return Returns a reference to the helper's layout tree. - **/ + */ TrackTree& get_layout_tree(); /** * Recalculates the track layout from layoutTree. * @see layout_headers_recursive - **/ + */ void update_layout(); /** @@ -97,7 +97,7 @@ public: * @remarks This function is only usable after update_layout() has * been called on a valid tree of tracks. * @see update_layout() - **/ + */ boost::optional get_track_header_rect( boost::weak_ptr track); @@ -111,7 +111,7 @@ public: * usable after update_layout() has been called on a valid tree of * tracks. * @see update_layout() - **/ + */ boost::shared_ptr header_from_point( Gdk::Point point); @@ -125,31 +125,31 @@ public: * usable after update_layout() has been called on a valid tree of * tracks. * @see update_layout() - **/ + */ boost::shared_ptr track_from_y(int y); /** * Begins to drag the track under mouse_point, if there is one. * @param mouse_point The mouse point to begin dragging from, measured * in pixels from the top left of the header container widget. - **/ + */ boost::shared_ptr begin_dragging_track(const Gdk::Point &mouse_point); /** * Drops the dragging track. * @param apply true if the model tree should be modified. - **/ + */ void end_dragging_track(bool apply); /** * Returns true if a track is being dragged. - **/ + */ bool is_dragging_track() const; /** * Gets the iterator of the layout tree node that is being dragged. - **/ + */ TrackTree::pre_order_iterator get_dragging_track_iter() const; /** @@ -160,7 +160,7 @@ public: * @remarks drag_to_point may only be called after * begin_dragging_track and before end_dragging_point have been * called. - **/ + */ void drag_to_point(const Gdk::Point &mouse_point); /** @@ -168,12 +168,12 @@ public: * @remarks This function is only on returns a valid value fter * update_layout() has been called on a valid tree of tracks. * @see update_layout() - **/ + */ int get_total_height() const; /** * Returns true if the layout is currently animating. - **/ + */ bool is_animating() const; /** @@ -182,7 +182,7 @@ public: * @param modelTrack The model track to look for. * @return Returns the model iterator of layoutTree.end() if no * iterator was found. - **/ + */ TrackTree::pre_order_iterator iterator_from_track( boost::shared_ptr modelTrack); @@ -192,7 +192,7 @@ public: * @param parent_iterator The parent of the branch to measure. This * node and all the child nodes will be included in the measurement. * @return Returns the height of the branch in pixels. - **/ + */ int measure_branch_height(TrackTree::iterator_base parent_iterator); protected: @@ -200,50 +200,50 @@ protected: /** * An enum to specify the relationship between a tree node, and * another node which is going to be inserted adjacent. - **/ + */ enum TreeRelation { /** * No relation - **/ + */ None, /** * The node will be inserted immediately before this one. - **/ + */ Before, /** * The node will be inserted immediately after this one. - **/ + */ After, /** * The node will be inserted as the first child of this one. - **/ + */ FirstChild, /** * The node will be inserted as the last child of this one. - **/ + */ LastChild }; /** * A structure used to specify where a track will be dropped when * dragging ends. - **/ + */ struct DropPoint { /** * Specifies the target node onto which the dragging track will be * dropped. - **/ + */ TrackTree::pre_order_iterator target; /** * The where to drop the dragging track in relation to target. - **/ + */ TreeRelation relation; }; @@ -258,7 +258,7 @@ protected: * @param[in] parent A pointer to the model track whose children * will be added to the layout tree branch. * @see clone_tree_from_sequence() - **/ + */ void add_branch(TrackTree::iterator_base parent_iterator, boost::shared_ptr parent); @@ -280,7 +280,7 @@ protected: * false if any of them are collapsed. * @return Returns the height of the branch in pixels. * @see update_layout() - **/ + */ int layout_headers_recursive( TrackTree::iterator_base parent_iterator, const int branch_offset, const int header_width, const int indent_width, const int depth, @@ -295,18 +295,18 @@ protected: * track was found. * @remarks If the return value is going to be NULL, an ENSURE will * fail. - **/ + */ boost::shared_ptr lookup_timeline_track( boost::shared_ptr modelTrack); /** * A helper function which kicks off the animation timer. - **/ + */ void begin_animation(); /** * The animation timer tick callback. - **/ + */ bool on_animation_tick(); /** @@ -318,7 +318,7 @@ protected: * point is hovering over it, and if it is, it works out what part of * the header, and therefore what drop location the user us gesturally * pointing to. - **/ + */ TimelineLayoutHelper::DropPoint attempt_drop(TrackTree::pre_order_iterator target, const Gdk::Point &point); @@ -327,31 +327,31 @@ protected: * Drops the dragging track to a new location in the layout tree as * specified by drop. * @param[in] drop The point in the tree to drop onto. - **/ + */ void apply_drop_to_layout_tree(const DropPoint &drop); /** * Drops the dragging track to a new location in the model tree as * specified by drop. * @param[in] drop The point in the tree to drop onto. - **/ + */ void apply_drop_to_modelTree(const DropPoint &drop); /** * Helper to get the sequence object from the state. * @return Returns a shared pointer to the sequence. - **/ + */ boost::shared_ptr get_sequence() const; protected: /** * The owner timeline widget as provided to the constructor. - **/ + */ TimelineWidget &timelineWidget; /** * The layout tree. - **/ + */ TrackTree layoutTree; /** @@ -360,7 +360,7 @@ protected: * need to be perpetually recalculated. This cache is regenerated by * the update_layout method. * @see update_layout() - **/ + */ std::map, Gdk::Rectangle> headerBoxes; @@ -368,7 +368,7 @@ protected: * The total height of the track tree layout in pixels. This value * is only valid after layout_headers has been called. * @see update_layout() - **/ + */ int totalHeight; /** @@ -376,13 +376,13 @@ protected: * dragged. * @remarks draggingTrackIter.node is set to NULL when no drag is * taking place. - **/ + */ TrackTree::pre_order_iterator draggingTrackIter; /** * The offset of the mouse relative to the top-left corner of the * dragging track. - **/ + */ Gdk::Point dragStartOffset; /** @@ -390,26 +390,26 @@ protected: * top left of the whole layout. * @remarks This value is changed by begin_dragging_track and * drag_to_point - **/ + */ Gdk::Point dragPoint; /** * The total height of the dragging branch in pixels. * @remarks This value is updated by begin_dragging_track - **/ + */ int dragBranchHeight; /** * The tree point the the user is currently hovering on. * @remarks This value is updated by drag_to_point. - **/ + */ DropPoint dropPoint; /** * The connection to the animation timer. * @see begin_animation() * @see on_animation_tick() - **/ + */ sigc::connection animationTimer; /** @@ -417,7 +417,7 @@ protected: * @remarks This value is recalculated by update_layout() * @see update_layout() * @see on_animation_tick() - **/ + */ bool animating; }; diff --git a/src/gui/widgets/timeline/timeline-ruler.hpp b/src/gui/widgets/timeline/timeline-ruler.hpp index bad7625ca..fe50669b9 100644 --- a/src/gui/widgets/timeline/timeline-ruler.hpp +++ b/src/gui/widgets/timeline/timeline-ruler.hpp @@ -109,7 +109,7 @@ private: /** * The event handler for when the TimelineWidget's state object is * replaced. - **/ + */ void on_state_changed(); private: @@ -170,7 +170,7 @@ private: /** * A helper function to get the view window - **/ + */ TimelineViewWindow& view_window() const; /** diff --git a/src/gui/widgets/timeline/timeline-state.hpp b/src/gui/widgets/timeline/timeline-state.hpp index 2d9a11ea3..22fb12cfd 100644 --- a/src/gui/widgets/timeline/timeline-state.hpp +++ b/src/gui/widgets/timeline/timeline-state.hpp @@ -41,7 +41,7 @@ namespace timeline { * TimelineState is a container for the state data for TimelineWidget. * @remarks TimelineState s can be swapped out so that TimelineWidget * can flip between different views. - **/ + */ class TimelineState { public: @@ -50,7 +50,7 @@ public: * Constructor * @param source_sequence The sequence on which the TimelineWidget * will operate when this TimelineState is attached. - **/ + */ TimelineState(boost::shared_ptr source_sequence); public: @@ -58,13 +58,13 @@ public: /** * Gets the sequence that is attached to this timeline state object. * @return Returns a shared_ptr to the sequence object. - **/ + */ boost::shared_ptr get_sequence() const; /** * Gets a reference to the timeline view window object. * @return Returns the timeline view window object. - **/ + */ timeline::TimelineViewWindow& get_view_window(); /** @@ -120,13 +120,13 @@ public: /** * A signal to notify when the selected period has changed. - **/ + */ sigc::signal selection_changed_signal() const; /** * A signal to notify when the playback point or playback periods have * changed. - **/ + */ sigc::signal playback_changed_signal() const; private: @@ -136,53 +136,53 @@ private: * represent. * @remarks This pointer is set by the constructor and is constant, so * will not change in value during the lifetime of the class. - **/ + */ boost::shared_ptr sequence; // View State /** * The ViewWindow for the TimelineWidget display with. - **/ + */ timeline::TimelineViewWindow viewWindow; // Selection State /** * The start time of the selection period. - **/ + */ lumiera::Time selectionStart; /** * The end time of the selection period. - **/ + */ lumiera::Time selectionEnd; /** * The start time of the playback period. - **/ + */ lumiera::Time playbackPeriodStart; /** * The end time of the playback period. - **/ + */ lumiera::Time playbackPeriodEnd; /** * The time of the playback point. - **/ + */ lumiera::Time playbackPoint; // Signals /** * A signal to notify when the selected period has changed. - **/ + */ sigc::signal selectionChangedSignal; /** * A signal to notify when the playback point or playback periods have * changed. - **/ + */ sigc::signal playbackChangedSignal; }; diff --git a/src/gui/widgets/timeline/timeline-tool.hpp b/src/gui/widgets/timeline/timeline-tool.hpp index 6b5ca5398..e63796d56 100644 --- a/src/gui/widgets/timeline/timeline-tool.hpp +++ b/src/gui/widgets/timeline/timeline-tool.hpp @@ -132,12 +132,12 @@ protected: /** * A helper function to get the state - **/ + */ boost::shared_ptr get_state() const; /** * A helper function to get the view window - **/ + */ TimelineViewWindow& view_window() const; protected: diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 663327494..21c5be6da 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -45,7 +45,7 @@ class TimelineViewWindow; * Timeline tracks are created by the timeline widget to correspond to * model tracks. Timeline tracks are used to store UI specific state * data. - **/ + */ class Track : public sigc::trackable { public: @@ -54,7 +54,7 @@ public: * An enum used by the branch expand/collapse animation. * ExpandDirection represents whether the branch us being expanded or * collapsed, or neither. - **/ + */ enum ExpandDirection { None, @@ -70,7 +70,7 @@ public: /** * Destructor - **/ + */ ~Track(); Gtk::Widget& get_header_widget(); @@ -89,14 +89,14 @@ public: * @return Returns true if the branch is expanded, false if it's * collapsed. * @see expand_collapse - **/ + */ bool get_expanded() const; /** * Expands or collapses this branch. * @param direction Specifies whether this branch should be expanded * or collapse. direction must not equal None - **/ + */ void expand_collapse(ExpandDirection direction); /** @@ -108,34 +108,34 @@ public: * (and animating). When the branch is not animating this value has * an indeterminate value. * @see tick_expand_animation - **/ + */ float get_expand_animation_state() const; /** * Gets whether the branch is animation. * @return Returns true if the branch is animating, false if not. - **/ + */ bool is_expand_animating() const; /** * When this track is being animated, tick_expand_animation must be * called repeatedly to cause the animation to progress. - **/ + */ void tick_expand_animation(); /** * Calculates the expander style, given the animation state. - **/ + */ Gtk::ExpanderStyle get_expander_style() const; /** * - **/ + */ void show_header_context_menu(guint button, guint32 time); /** * Draw the track - **/ + */ virtual void draw_track(Cairo::RefPtr cairo, TimelineViewWindow* const window) const = 0; @@ -144,7 +144,7 @@ public: * Gets the clip that is occupying the given time. * The default implementation simply returns an empty pointer. * @param the given time - **/ + */ virtual boost::shared_ptr getClipAt(lumiera::Time position) const; @@ -152,32 +152,32 @@ private: /** * Specifies the period of the expand animation in seconds. - **/ + */ static const float ExpandAnimationPeriod; /** * Event handler for when the enabled status changes. - **/ + */ void onEnabledChanged(bool); /** * Event handler for when the track name changes. - **/ + */ void onNameChanged(std::string); /** * Event handler for when the user requested to remove the track. - **/ + */ void on_remove_track(); /** * Event handler for when the locked status changes. - **/ + */ void onLockedChanged(bool); /** * Event handler for when the user requested a name change. - **/ + */ void on_set_name(); /** @@ -205,7 +205,7 @@ private: /** * This bool is true if this branch is expanded. false if it is * collapsed. - **/ + */ bool expanded; /** @@ -213,7 +213,7 @@ private: * is moving - if any. * @remarks If no animation is occuring, expandDirection is set to * None. - **/ + */ ExpandDirection expandDirection; /** @@ -224,12 +224,12 @@ private: * 0.0 when the branch is fully collapsed (and animating). When the * branch is not animating this value has an indeterminate value. * @see tick_expand_animation - **/ + */ double expandAnimationState; /** * An internal timer used for the expand/collapse animation. - **/ + */ boost::scoped_ptr expand_timer; //----- Header Widgets ------// diff --git a/src/gui/widgets/timeline/timeline-view-window.hpp b/src/gui/widgets/timeline/timeline-view-window.hpp index e0513bd15..ebd476b66 100644 --- a/src/gui/widgets/timeline/timeline-view-window.hpp +++ b/src/gui/widgets/timeline/timeline-view-window.hpp @@ -40,7 +40,7 @@ namespace timeline { * TimelineViewWindow is a helper class for TimelineWidget which manages * the view window of the timeline: the zoom and shift. The class also * provides utility functions for handling time in the timeline. - **/ + */ class TimelineViewWindow { public: @@ -49,7 +49,7 @@ public: * Constructor * @param offset The initial view offset. * @param scale The initial scale. - **/ + */ TimelineViewWindow(lumiera::Time offset, int64_t scale); /** @@ -84,14 +84,14 @@ public: * given point on the timeline still. * @param zoom_size The number of steps to zoom by. The scale factor * is 1.25^(-zoom_size). - **/ + */ void zoom_view(int point, int zoom_size); /** * Scrolls the view horizontally as a proportion of the view area. * @param shift_size The size of the shift in 1/256ths of the view * width. - **/ + */ void shift_view(int view_width, int shift_size); /** @@ -99,7 +99,7 @@ public: * @param time The time value to convert. * @return Returns the x-value as pixels from the left hand edge of * the timeline body. - **/ + */ int time_to_x(int64_t time) const; /** @@ -107,12 +107,12 @@ public: * @param x The x coordinte (as pixels from the left hand edge of * the timeline body) to convert. * @return Returns the time at the coordinate. - **/ + */ lumiera::Time x_to_time(int x) const; /** * A signal to indicate that the scale or offset have been changed. - **/ + */ sigc::signal changed_signal() const; private: diff --git a/src/gui/window-manager.hpp b/src/gui/window-manager.hpp index 29ead7005..4f23ac6e6 100644 --- a/src/gui/window-manager.hpp +++ b/src/gui/window-manager.hpp @@ -49,13 +49,13 @@ namespace workspace { /** * The centralised manager of all lumiera-gui's windows. - **/ + */ class WindowManager : private boost::noncopyable { public: /** * Initializes the window manager object - **/ + */ void init(); /** @@ -63,7 +63,7 @@ public: * controller * @param source_project The project to connect the window to. * @param source_controller The controller to connect the window to. - **/ + */ void new_window(gui::model::Project &source_project, gui::controller::Controller &source_controller); @@ -71,7 +71,7 @@ public: * Sets the theme of the lumiera-gui's. * @param path This string must specify a path where a GTK stylesheet * will be found. - **/ + */ bool set_theme(Glib::ustring path); /** @@ -82,7 +82,7 @@ public: * @param green The fallback green intensity. * @param blue The fallback blue intensity. * @return The loaded colour. - **/ + */ static Cairo::RefPtr read_style_colour_property (Gtk::Widget &widget, const gchar *property_name, guint16 red, guint16 green, guint16 blue); @@ -91,7 +91,7 @@ private: /** * An event handler for when a window has been closed. - **/ + */ bool on_window_closed(GdkEventAny* event); private: @@ -102,18 +102,18 @@ private: * * It should be enabled when there is more than one window and disabled * otherwise. - **/ + */ void update_close_window_in_menus(); /** * Registers the custom icon sizes. - **/ + */ static void register_app_icon_sizes(); /** * Registers application stock items: icons and * labels associated with IDs - **/ + */ static void register_stock_items(); /** @@ -124,7 +124,7 @@ private: * @param label The user readable icon name for this icon. * @return Returns true if the icon was successfully loaded, returns * false otherwise. - **/ + */ static bool add_stock_icon_set( const Glib::RefPtr& factory, const Glib::ustring& icon_name, @@ -140,7 +140,7 @@ private: * @param wildcard This value is set to true if this icon is * wildcarded. * @return Returns true if the icon was loaded successfully. - **/ + */ static bool add_stock_icon(Gtk::IconSet &icon_set, const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard); @@ -152,7 +152,7 @@ private: * @param wildcard This value is set to true if this icon is * wildcarded. * @return Returns true if the icon was loaded successfully. - **/ + */ static bool add_theme_icon_source(Gtk::IconSet &icon_set, const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard); @@ -165,7 +165,7 @@ private: * @param wildcard This value is set to true if this icon is * wildcarded. * @return Returns true if the icon was loaded successfully. - **/ + */ static bool add_non_theme_icon_source(Gtk::IconSet &icon_set, const Glib::ustring& base_dir, const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard); @@ -178,7 +178,7 @@ private: * @param wildcard This value is set to true if this icon is * wildcarded. * @return Returns true if the icon was loaded successfully. - **/ + */ static bool add_stock_icon_from_path(Glib::ustring path, Gtk::IconSet &icon_set, Gtk::IconSize size, bool wildcard); @@ -192,14 +192,14 @@ public: * The registered icon size for giant 48x48 px icons. * @remarks This value is set to BuiltinIconSize::ICON_SIZE_INVALID * until register_giant_icon_size is called. - **/ + */ static Gtk::IconSize GiantIconSize; /** * The registered icon size for giant 16x16 px icons. * @remarks This value is set to BuiltinIconSize::ICON_SIZE_INVALID * until register_app_icon_sizes is called. - **/ + */ static Gtk::IconSize MenuIconSize; }; diff --git a/src/gui/workspace/actions.hpp b/src/gui/workspace/actions.hpp index 3076fedcb..06248d10f 100644 --- a/src/gui/workspace/actions.hpp +++ b/src/gui/workspace/actions.hpp @@ -46,13 +46,13 @@ public: /** * Constructor * @param workspace_window The owner workspace window. - **/ + */ Actions(WorkspaceWindow &workspace_window); /** * Populates a uiManager with the main set of actions. * @param uiManager A pointer to the uiManager to populate. - **/ + */ void populate_main_actions(Glib::RefPtr uiManager); /* ===== Internals ===== */ @@ -61,7 +61,7 @@ private: /** * Populates a uiManager with actions for the Show Panel menu. * @param uiManager A pointer to the uiManager to populate. - **/ + */ void populate_show_panel_actions( Glib::RefPtr uiManager); diff --git a/src/gui/workspace/panel-manager.hpp b/src/gui/workspace/panel-manager.hpp index 9e3687513..d21fce950 100644 --- a/src/gui/workspace/panel-manager.hpp +++ b/src/gui/workspace/panel-manager.hpp @@ -40,7 +40,7 @@ namespace workspace { /** * A class to managers DockItem objects for WorkspaceWindow. - **/ + */ class PanelManager { public: @@ -49,12 +49,12 @@ public: * Constructor * @param workspace_window A reference to the owner WorkspaceWindow * object. - **/ + */ PanelManager(WorkspaceWindow &workspace_window); /** * Destructor. - **/ + */ ~PanelManager(); /** @@ -62,30 +62,30 @@ public: * widgets. * @remarks This function must be called only once as the first call * after construction. - **/ + */ void setup_dock(); /** * Gets a pointer to the dock object. * @remarks Note that this must not be called before setup_dock. - **/ + */ GdlDock* get_dock() const; /** * Gets a pointer to the dock bar. * @remarks Note that this must not be called before setup_dock. - **/ + */ GdlDockBar* get_dock_bar() const; /** * Returns a reference to the owner workspace window. - **/ + */ WorkspaceWindow& get_workspace_window(); /** * Shows a panel given a description index. * @param description_index The index of the panel type to show. - **/ + */ void show_panel(const int description_index); /** @@ -94,7 +94,7 @@ public: * @param old_panel The panel which will be transofrmed to a new type. * @param description_index The index of the panel description that * will be instantiated. - **/ + */ void switch_panel(panels::Panel &old_panel, const int description_index); @@ -102,7 +102,7 @@ public: * Splits a panel into two panels of the same type. * @param panel The panel to split. * @param split_direction The direction to split the panel in. - **/ + */ void split_panel(panels::Panel &panel, Gtk::Orientation split_direction); @@ -110,28 +110,28 @@ public: /** * Gets the number of panel descriptions. - **/ + */ static int get_panel_description_count(); /** * Gets a panel description's stock id. * @param index The index of the panel to retrieve. * @return Returns the stock id of a panel at this index. - **/ + */ static const gchar* get_panel_stock_id(const int index); /** * Gets a panel description's title. * @param index The index of the panel to retrieve. * @return Returns the title of a panel at this index. - **/ + */ static const char* get_panel_title(int index); private: /** * Creates the standard panel layout. - **/ + */ void create_panels(); /** @@ -139,14 +139,14 @@ private: * @param class_name The name of the object class to search for. * @return Returns the index of the panel description found, or -1 * if no description was found for this type. - **/ + */ int find_panel_description(const char* class_name) const; /** * Creates a panel by description index. * @param index The index of the description to instantiate. * @return Returns a pointer to the new instantiated panel object. - **/ + */ panels::Panel* create_panel_by_index(const int index); /** @@ -154,7 +154,7 @@ private: * @param index The index of the description to instantiate. * @param dock_item The GdlDockItem to attach this panel to * @return Returns a pointer to the new instantiated panel object. - **/ + */ panels::Panel* create_panel_by_index( const int index, GdlDockItem *dock_item); @@ -162,7 +162,7 @@ private: * Creates a panel by class name. * @param class_name The name of the object class to create. * @return Returns a pointer to the new instantiated panel object. - **/ + */ panels::Panel* create_panel_by_name(const char* class_name); /** @@ -170,18 +170,18 @@ private: * @param panel The Panel to get the type of * @return Returns the index of the panel description found, or -1 * if no description was found for this type. - **/ + */ int get_panel_type(panels::Panel* const panel) const; /** * Removes a panel from the panel list and deletes it. * @param panel The panel to remove and delete. - **/ + */ void remove_panel(panels::Panel* const panel); /** * Removes all panels from the panel list and deletes them. - **/ + */ void clear_panels(); private: @@ -189,55 +189,55 @@ private: /** * An event handler for when the panel is shown or hidden. * @param panel A pointer to the panel that was hidden. - **/ + */ void on_panel_shown(panels::Panel *panel); private: /** * A reference to the owner workspace window object. - **/ + */ WorkspaceWindow &workspaceWindow; /** * The pointer to GDL dock widget. * @remarks This value is NULL until setup_dock has been called. - **/ + */ GdlDock *dock; /** * The pointer to GDL dock bar widget. * @remarks This value is NULL until setup_dock has been called. - **/ + */ GdlDockBar *dockBar; /** * The pointer to GDL dock layout object. * @remarks This value is NULL until setup_dock has been called. - **/ + */ GdlDockLayout *dockLayout; /** * Pointers to the 4 root place holders. * @remarks All 4 entries are NULL until setup_dock has been called. - **/ + */ GdlDockPlaceholder *dockPlaceholders[4]; /** * The list of created panels. - **/ + */ std::list< panels::Panel* > panels; /** * An accumulator for the panel id. - **/ + */ static unsigned short panelID; private: /** * A class to describe and instantiate Panel types. - **/ + */ class PanelDescription { protected: @@ -254,7 +254,7 @@ private: * @param stock_id The Stock ID for this type of panel. * @param create_panel_proc A pointer to a function that will * instantiate the panel object. - **/ + */ PanelDescription(const std::type_info &class_info, const char *title, const gchar *stock_id, CreatePanelProc create_panel_proc) : @@ -269,7 +269,7 @@ private: public: /** * Returns a reference to the typeid of the class. - **/ + */ const std::type_info& get_class_info() const { return classInfo; @@ -277,7 +277,7 @@ private: /** * Returns a pointer to the string name of the class. - **/ + */ const char* get_class_name() const { return classInfo.name(); @@ -285,7 +285,7 @@ private: /** * Returns the localized title that will be shown on the panel. - **/ + */ const char* get_title() const { ENSURE(titleName); @@ -294,7 +294,7 @@ private: /** * Returns the Stock ID for this type of panel. - **/ + */ const gchar* get_stock_id() const { ENSURE(stockID); @@ -306,7 +306,7 @@ private: * @param panel_manager The owner panel manager. * @param dock_item The GdlDockItem that will host this panel. * @return Returns a pointer to the panel object. - **/ + */ panels::Panel* create( PanelManager &panel_manager, GdlDockItem* dock_item) const { @@ -317,22 +317,22 @@ private: private: /** * A reference to the typeid of this class - **/ + */ const std::type_info &classInfo; /** * The localized title that will be shown on the panel. - **/ + */ const char* const titleName; /** * The Stock ID for this type of panel. - **/ + */ const gchar* const stockID; /** * A pointer to a function that will instantiate the panel object. - **/ + */ CreatePanelProc createPanelProc; }; @@ -340,13 +340,13 @@ private: * A helper class that will create PanelDescription objects. * @param P The type of panels::Panel that the PanelDescription will * describe. - **/ + */ template class Panel : public PanelDescription { public: /** * Constructor - **/ + */ Panel() : PanelDescription(typeid(P), P::get_title(), P::get_stock_id(), Panel::create_panel) @@ -358,7 +358,7 @@ private: * @param panel_manager The owner panel manager. * @param dock_item The GdlDockItem that will host this panel. * @return Returns a pointer to the panel object. - **/ + */ static panels::Panel* create_panel( PanelManager &panel_manager, GdlDockItem* dock_item) { @@ -368,7 +368,7 @@ private: /** * The list of panel descriptions. - **/ + */ static const PanelDescription panelDescriptionList[]; }; diff --git a/src/lib/observable-list.hpp b/src/lib/observable-list.hpp index 467fa7020..7b750f761 100644 --- a/src/lib/observable-list.hpp +++ b/src/lib/observable-list.hpp @@ -35,7 +35,7 @@ namespace lumiera /** * An observable_list is an STL list with an inbuilt sigc++ signal that * allows observers to be notified when changes are made to the list. - **/ + */ template > class observable_list { @@ -268,7 +268,7 @@ public: /** * Returns a read only reference to the list. - **/ + */ const std::list& get_list() const { return list; @@ -281,7 +281,7 @@ public: * Access to the signal which will be emit every time the list is * changed in some way. * @return Returns the signal object which will emit notifications. - **/ + */ sigc::signal& signal_changed() { return changed; diff --git a/src/lib/tree.hpp b/src/lib/tree.hpp index 66774b8e1..be7ff8e52 100644 --- a/src/lib/tree.hpp +++ b/src/lib/tree.hpp @@ -93,7 +93,7 @@ class tree_node_ { // size: 5*4=20 bytes (on 32 bit arch), can be reduced by 8. * pre-order, and others). Where possible the access methods are * compatible with the STL or alternative algorithms are * available. - **/ + */ template > > class tree { protected: From 73e9d9ae995effe9a664c5c9e72405483fbcaccf Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Fri, 7 Jan 2011 16:10:27 +0100 Subject: [PATCH 061/140] Fix broken link in documentation --- doc/design/gui/GuiDiscussion/TimelineDiscussion.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/design/gui/GuiDiscussion/TimelineDiscussion.txt b/doc/design/gui/GuiDiscussion/TimelineDiscussion.txt index 7f4cbd0b1..2e5f97279 100644 --- a/doc/design/gui/GuiDiscussion/TimelineDiscussion.txt +++ b/doc/design/gui/GuiDiscussion/TimelineDiscussion.txt @@ -125,7 +125,7 @@ Joel Conclusions: Timeline like this: -image::http://www.kenstone2.net/fcp_homepage/images_fcp_5_new_martin/07_fc_studio_new_martin.jpg[] +image::http://www.kenstone6.net/fcp_homepage/images_fcp_5_new_martin/07_fc_studio_new_martin.jpg[] Tracks '''''''''' From e947d6f05336be954ff506e1fc53797a64e3b855 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Wed, 12 Jan 2011 23:59:12 +0100 Subject: [PATCH 062/140] Add DrawStrategy object for timeline entities --- src/gui/Makefile.am | 3 + .../widgets/timeline/basic-draw-strategy.cpp | 67 +++++++++++++++++++ .../widgets/timeline/basic-draw-strategy.hpp | 53 +++++++++++++++ src/gui/widgets/timeline/draw-strategy.hpp | 64 ++++++++++++++++++ .../widgets/timeline/timeline-clip-track.cpp | 5 +- .../widgets/timeline/timeline-clip-track.hpp | 1 + src/gui/widgets/timeline/timeline-clip.cpp | 58 ++++++++-------- src/gui/widgets/timeline/timeline-clip.hpp | 14 +++- src/gui/widgets/timeline/timeline-entity.cpp | 24 ++++++- src/gui/widgets/timeline/timeline-entity.hpp | 46 +++++++++++-- 10 files changed, 295 insertions(+), 40 deletions(-) create mode 100644 src/gui/widgets/timeline/basic-draw-strategy.cpp create mode 100644 src/gui/widgets/timeline/basic-draw-strategy.hpp create mode 100644 src/gui/widgets/timeline/draw-strategy.hpp diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index 88b8ae341..095baf82c 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -112,6 +112,9 @@ gtk_gui_la_SOURCES = \ $(lumigui_srcdir)/widgets/timecode-widget.hpp \ $(lumigui_srcdir)/widgets/timeline-widget.cpp \ $(lumigui_srcdir)/widgets/timeline-widget.hpp \ + $(lumigui_srcdir)/widgets/timeline/basic-draw-strategy.cpp \ + $(lumigui_srcdir)/widgets/timeline/basic-draw-strategy.hpp \ + $(lumigui_srcdir)/widgets/timeline/draw-strategy.hpp \ $(lumigui_srcdir)/widgets/timeline/timeline-arrow-tool.cpp \ $(lumigui_srcdir)/widgets/timeline/timeline-arrow-tool.hpp \ $(lumigui_srcdir)/widgets/timeline/timeline-body.cpp \ diff --git a/src/gui/widgets/timeline/basic-draw-strategy.cpp b/src/gui/widgets/timeline/basic-draw-strategy.cpp new file mode 100644 index 000000000..52967ced9 --- /dev/null +++ b/src/gui/widgets/timeline/basic-draw-strategy.cpp @@ -0,0 +1,67 @@ +/* + basic-draw-strategy.cpp - Implementation of a basic draw strategy + + Copyright (C) Lumiera.org + 2010, Stefan Kangas cr, + TimelineViewWindow* const window) const + { + REQUIRE (cr); + REQUIRE (window); + + int x = window->time_to_x(entity.getBegin()); + int width = window->time_to_x( + entity.getEnd()) - window->time_to_x(entity.getBegin()); + + // Draw a rectangle for the entity + // TODO: get height from the timeline::Entity + cr->rectangle(x, 1, width, 100-2); + // if (entity.getSelected()) + cr->set_source(Cairo::SolidPattern::create_rgb (0.4, 0.4, 0.8)); + // else + // cr->set_source(Cairo::SolidPattern::create_rgb (0.4, 0.4, 0.4)); + cr->fill_preserve(); + cr->set_source_rgb(0.25, 0.25, 0.25); + cr->stroke(); + + // Show the entities name + cr->rectangle(x, 1, width, 100-2); + cr->clip(); + cr->move_to (x + 3, 12); + cr->set_source_rgb (1.0, 1.0, 1.0); + cr->set_font_size (9); + cr->show_text (entity.getName()); + } + +} // namespace timeline +} // namespace widgets +} // namespace gui + diff --git a/src/gui/widgets/timeline/basic-draw-strategy.hpp b/src/gui/widgets/timeline/basic-draw-strategy.hpp new file mode 100644 index 000000000..2661d4526 --- /dev/null +++ b/src/gui/widgets/timeline/basic-draw-strategy.hpp @@ -0,0 +1,53 @@ +/* + basic-draw-strategy.hpp - Declaration of a basic draw strategy + + Copyright (C) Lumiera.org + 2010, Stefan Kangas cr, + TimelineViewWindow* const window) const; + }; + +} // namespace timeline +} // namespace widgets +} // namespace gui + +#endif // TIMELINE_BASIC_DRAW_STRATEGY_HPP diff --git a/src/gui/widgets/timeline/draw-strategy.hpp b/src/gui/widgets/timeline/draw-strategy.hpp new file mode 100644 index 000000000..759ff84df --- /dev/null +++ b/src/gui/widgets/timeline/draw-strategy.hpp @@ -0,0 +1,64 @@ +/* + draw-strategy.hpp - Definition the timeline draw strategy interface + + Copyright (C) Lumiera.org + 2010, Stefan Kangas cr, + TimelineViewWindow* const window) const = 0; + }; + +} // namespace timeline +} // namespace widgets +} // namespace gui + +#endif // TIMELINE_DRAW_STRATEGY_HPP diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index 6c41ddf83..f18930f60 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -96,6 +96,9 @@ namespace timeline { void ClipTrack::createTimelineClips() { + // Share the draw strategy between all objects + // TODO: use factory/builder here + static boost::shared_ptr drawStrategy(new BasicDrawStrategy()); BOOST_FOREACH (shared_ptr modelClip, getModelTrack()->getClipList()) { // Is a timeline UI clip present in the map already? @@ -104,7 +107,7 @@ namespace timeline { // The timeline UI clip is not present // We will need to create one clipMap[modelClip] = shared_ptr( - new timeline::Clip (modelClip)); + new timeline::Clip (modelClip, drawStrategy)); } } } diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index 9fef82512..461d71876 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -30,6 +30,7 @@ #include +#include "basic-draw-strategy.hpp" #include "timeline-track.hpp" #include "gui/model/clip-track.hpp" diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index e7d95fb3a..9c286b89d 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -26,8 +26,9 @@ namespace gui { namespace widgets { namespace timeline { - Clip::Clip(boost::shared_ptr clip) - : Entity(), + Clip::Clip(boost::shared_ptr clip, + boost::shared_ptr drawStrategy) + : Entity(drawStrategy), modelClip(clip), selected(false) { @@ -40,40 +41,34 @@ namespace timeline { void Clip::draw_clip(Cairo::RefPtr cr, - TimelineViewWindow* const window) const + TimelineViewWindow* const window) const { - REQUIRE(cr); - REQUIRE(window); - REQUIRE(modelClip); + REQUIRE (cr); + REQUIRE (window); + REQUIRE (modelClip); - int x = window->time_to_x(modelClip->getBegin()); - int width = window->time_to_x( - modelClip->getEnd()) - window->time_to_x(modelClip->getBegin()); + getDrawStrategy()->draw(*this, cr, window); + } - // Draw a rectangle for the clip - cr->rectangle(x, 1, width, 100-2); - // TODO: get height from the Timeline::Track + gavl_time_t + Clip::getBegin () const + { + REQUIRE (modelClip); + return modelClip->getBegin(); + } - if (selected) - cr->set_source(Cairo::SolidPattern::create_rgb (0.4, 0.4, 0.8)); - else - cr->set_source(Cairo::SolidPattern::create_rgb (0.4, 0.4, 0.4)); - cr->fill_preserve(); + gavl_time_t + Clip::getEnd () const + { + REQUIRE (modelClip); + return modelClip->getEnd(); + } - cr->set_source_rgb(0.25, 0.25, 0.25); - cr->stroke(); - - // Show the clip name - cr->rectangle(x, 1, width, 100-2); - cr->clip(); - - cr->move_to (x + 3, 12); - cr->set_source_rgb (1.0, 1.0, 1.0); - - cr->set_font_size (9); - cr->show_text (modelClip->getName()); - - // TODO: Show thumbnails for clip + std::string + Clip::getName () const + { + REQUIRE (modelClip); + return modelClip->getName(); } void @@ -82,6 +77,7 @@ namespace timeline { this->selected = selected; } + } // namespace timeline } // namespace widgets } // namespace gui diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index c8e6d7151..bce1fc7ee 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -28,6 +28,8 @@ #include "gui/gtk-lumiera.hpp" #include "gui/model/clip.hpp" #include "include/logging.h" + +#include "draw-strategy.hpp" #include "timeline-entity.hpp" #include "timeline-view-window.hpp" @@ -41,11 +43,21 @@ namespace timeline { class Clip : public Entity { public: - Clip(boost::shared_ptr clip); + Clip(boost::shared_ptr clip, + boost::shared_ptr drawStrategy); void draw_clip(Cairo::RefPtr cairo, TimelineViewWindow* const window) const; + gavl_time_t + getBegin () const; + + gavl_time_t + getEnd () const; + + std::string + getName () const; + /** * Sets the selected status of the clip. */ diff --git a/src/gui/widgets/timeline/timeline-entity.cpp b/src/gui/widgets/timeline/timeline-entity.cpp index 55229b85b..d9cf3ae2e 100644 --- a/src/gui/widgets/timeline/timeline-entity.cpp +++ b/src/gui/widgets/timeline/timeline-entity.cpp @@ -26,10 +26,30 @@ namespace gui { namespace widgets { namespace timeline { - Entity::Entity() - : enabled(true) + Entity::Entity(boost::shared_ptr drawStrategy) + : enabled(true), + drawStrategy(drawStrategy) { } + + boost::shared_ptr + Entity::getDrawStrategy () const + { + return drawStrategy; + } + + bool + Entity::getEnabled () const + { + return enabled; + } + + void + Entity::setEnabled (bool enabled) + { + this->enabled = enabled; + } + } // namespace timeline } // namespace widgets } // namespace gui diff --git a/src/gui/widgets/timeline/timeline-entity.hpp b/src/gui/widgets/timeline/timeline-entity.hpp index 6c0053c67..6b785f583 100644 --- a/src/gui/widgets/timeline/timeline-entity.hpp +++ b/src/gui/widgets/timeline/timeline-entity.hpp @@ -26,10 +26,22 @@ #ifndef TIMELINE_ENTITY_HPP #define TIMELINE_ENTITY_HPP +#include + +// TODO: Remove once we get better measure of duration. +extern "C" { +#include +#include +} + +#include "boost/shared_ptr.hpp" + namespace gui { namespace widgets { namespace timeline { + class DrawStrategy; + /** * Base class for timeline entities. * Everything that can be placed on the timeline is a timeline Entity. @@ -40,18 +52,40 @@ namespace timeline { /** * Constructor */ - Entity(); + Entity(boost::shared_ptr drawStrategy); public: /** - * + * Gets the beginning of this entity's duration. */ - bool - getEnabled(); + virtual gavl_time_t + getBegin () const = 0; + + + boost::shared_ptr + getDrawStrategy () const; /** - * + * Gets the enabled property of this entity. + */ + bool + getEnabled () const; + + /** + * Gets the end of this entity's duration. + */ + virtual gavl_time_t + getEnd () const = 0; + + /** + * Gets the name of this entity. + */ + virtual std::string + getName () const = 0; + + /** + * Sets the enabled property of this entity. */ void setEnabled(bool selected); @@ -62,6 +96,8 @@ namespace timeline { * True when this entity is enabled. */ bool enabled; + + boost::shared_ptr drawStrategy; }; } // namespace timeline From a127ec787332e4feb2cc6feb4389fee4f1fb1199 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Fri, 14 Jan 2011 16:52:38 +0100 Subject: [PATCH 063/140] fix three #include paths for cairomm --- src/gui/widgets/timeline/timeline-body.cpp | 2 +- src/gui/widgets/timeline/timeline-ruler.cpp | 2 +- src/gui/widgets/video-display-widget.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 7d5873f34..431c7d44b 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -20,7 +20,7 @@ * *****************************************************/ -#include +#include #include #include "timeline-body.hpp" diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index 63d6ecc7a..7aab5e5d1 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -20,7 +20,7 @@ * *****************************************************/ -#include +#include #include "timeline-ruler.hpp" #include "gui/widgets/timeline-widget.hpp" diff --git a/src/gui/widgets/video-display-widget.cpp b/src/gui/widgets/video-display-widget.cpp index 0be796233..cddd16841 100644 --- a/src/gui/widgets/video-display-widget.cpp +++ b/src/gui/widgets/video-display-widget.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include "gui/gtk-lumiera.hpp" #include "gui/output/xvdisplayer.hpp" From 1c284fb497a557bf2c9fd68c441e1331e1fa8d9a Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Fri, 14 Jan 2011 18:36:26 +0100 Subject: [PATCH 064/140] remove three unnecessary #includes --- src/gui/widgets/video-display-widget.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/gui/widgets/video-display-widget.cpp b/src/gui/widgets/video-display-widget.cpp index cddd16841..22531d1f5 100644 --- a/src/gui/widgets/video-display-widget.cpp +++ b/src/gui/widgets/video-display-widget.cpp @@ -20,10 +20,6 @@ * *****************************************************/ -#include -#include -#include - #include "gui/gtk-lumiera.hpp" #include "gui/output/xvdisplayer.hpp" #include "gui/output/gdkdisplayer.hpp" From 2fba7aba2caa25a330544c3cb8b3e4dc784c2320 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Fri, 14 Jan 2011 18:46:41 +0100 Subject: [PATCH 065/140] Move draw functionality to timeline::Entity base class --- .../widgets/timeline/basic-draw-strategy.hpp | 3 -- src/gui/widgets/timeline/draw-strategy.hpp | 12 ++----- .../widgets/timeline/timeline-clip-track.cpp | 2 +- src/gui/widgets/timeline/timeline-clip.cpp | 11 ------- src/gui/widgets/timeline/timeline-clip.hpp | 6 ---- src/gui/widgets/timeline/timeline-entity.cpp | 17 ++++++++-- src/gui/widgets/timeline/timeline-entity.hpp | 33 +++++-------------- 7 files changed, 26 insertions(+), 58 deletions(-) diff --git a/src/gui/widgets/timeline/basic-draw-strategy.hpp b/src/gui/widgets/timeline/basic-draw-strategy.hpp index 2661d4526..511ae9808 100644 --- a/src/gui/widgets/timeline/basic-draw-strategy.hpp +++ b/src/gui/widgets/timeline/basic-draw-strategy.hpp @@ -36,9 +36,6 @@ namespace timeline { { public: - /** - * Constructor. - */ BasicDrawStrategy(); void draw(const Entity &entity, diff --git a/src/gui/widgets/timeline/draw-strategy.hpp b/src/gui/widgets/timeline/draw-strategy.hpp index 759ff84df..72eaa8828 100644 --- a/src/gui/widgets/timeline/draw-strategy.hpp +++ b/src/gui/widgets/timeline/draw-strategy.hpp @@ -40,18 +40,12 @@ namespace timeline { { protected: - /** - * Constructor. - */ - DrawStrategy() - { } + DrawStrategy() { } + + virtual ~DrawStrategy() { } public: - /** - * Draw the entity. - * @param entity draw the - */ virtual void draw(const Entity &entity, Cairo::RefPtr cr, TimelineViewWindow* const window) const = 0; diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index f18930f60..b07b3ee1c 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -72,7 +72,7 @@ namespace timeline { pair; BOOST_FOREACH (pair, clipMap) { - pair.second->draw_clip(cairo, window); + pair.second->draw(cairo, window); } } diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index 9c286b89d..001964ae1 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -39,17 +39,6 @@ namespace timeline { // &Clip::onNameChanged); } - void - Clip::draw_clip(Cairo::RefPtr cr, - TimelineViewWindow* const window) const - { - REQUIRE (cr); - REQUIRE (window); - REQUIRE (modelClip); - - getDrawStrategy()->draw(*this, cr, window); - } - gavl_time_t Clip::getBegin () const { diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index bce1fc7ee..f3b8c0151 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -23,15 +23,12 @@ ** This file contains the definition of timeline clip object */ -#include - #include "gui/gtk-lumiera.hpp" #include "gui/model/clip.hpp" #include "include/logging.h" #include "draw-strategy.hpp" #include "timeline-entity.hpp" -#include "timeline-view-window.hpp" #ifndef TIMELINE_CLIP_HPP #define TIMELINE_CLIP_HPP @@ -46,9 +43,6 @@ namespace timeline { Clip(boost::shared_ptr clip, boost::shared_ptr drawStrategy); - void draw_clip(Cairo::RefPtr cairo, - TimelineViewWindow* const window) const; - gavl_time_t getBegin () const; diff --git a/src/gui/widgets/timeline/timeline-entity.cpp b/src/gui/widgets/timeline/timeline-entity.cpp index d9cf3ae2e..0514dafd4 100644 --- a/src/gui/widgets/timeline/timeline-entity.cpp +++ b/src/gui/widgets/timeline/timeline-entity.cpp @@ -22,6 +22,11 @@ #include "timeline-entity.hpp" +#include "draw-strategy.hpp" + +#include "gui/gtk-lumiera.hpp" +#include "include/logging.h" + namespace gui { namespace widgets { namespace timeline { @@ -31,11 +36,17 @@ namespace timeline { drawStrategy(drawStrategy) { } + Entity::~Entity() + { } - boost::shared_ptr - Entity::getDrawStrategy () const + void + Entity::draw(Cairo::RefPtr cr, + TimelineViewWindow* const window) const { - return drawStrategy; + REQUIRE (cr); + REQUIRE (window); + + drawStrategy->draw(*this, cr, window); } bool diff --git a/src/gui/widgets/timeline/timeline-entity.hpp b/src/gui/widgets/timeline/timeline-entity.hpp index 6b785f583..fc1e8e345 100644 --- a/src/gui/widgets/timeline/timeline-entity.hpp +++ b/src/gui/widgets/timeline/timeline-entity.hpp @@ -34,13 +34,15 @@ extern "C" { #include } -#include "boost/shared_ptr.hpp" +#include +#include namespace gui { namespace widgets { namespace timeline { class DrawStrategy; + class TimelineViewWindow; /** * Base class for timeline entities. @@ -49,52 +51,33 @@ namespace timeline { class Entity { protected: - /** - * Constructor - */ Entity(boost::shared_ptr drawStrategy); + virtual ~Entity(); + public: - /** - * Gets the beginning of this entity's duration. - */ virtual gavl_time_t getBegin () const = 0; - - boost::shared_ptr - getDrawStrategy () const; + virtual void + draw(Cairo::RefPtr cairo, + TimelineViewWindow* const window) const; - /** - * Gets the enabled property of this entity. - */ bool getEnabled () const; - /** - * Gets the end of this entity's duration. - */ virtual gavl_time_t getEnd () const = 0; - /** - * Gets the name of this entity. - */ virtual std::string getName () const = 0; - /** - * Sets the enabled property of this entity. - */ void setEnabled(bool selected); private: - /** - * True when this entity is enabled. - */ bool enabled; boost::shared_ptr drawStrategy; From eb8954726589cb5c7ca662b95f574441b230a1e5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 15 Jan 2011 00:52:02 +0100 Subject: [PATCH 066/140] get rid of the QuantiserRef this is going to become soooo complicated better just bite the bullet and use a shared_ptr --- src/lib/time/formats.hpp | 40 +++++++---------------- src/lib/time/quantiser.cpp | 4 +-- src/lib/time/timecode.cpp | 11 ++----- src/lib/time/timecode.hpp | 8 ++--- src/lib/time/timequant.hpp | 10 +++--- tests/lib/time/time-quantisation-test.cpp | 2 +- 6 files changed, 27 insertions(+), 48 deletions(-) diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index ca92b048c..0bd6f7788 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -27,6 +27,7 @@ #include "lib/time/timevalue.hpp" //#include +#include #include @@ -42,27 +43,10 @@ namespace time { class Secs; - class Quantiser; // API for grid aligning + class Quantiser; // API for grid aligning + typedef Quantiser const& QuantR; + typedef std::tr1::shared_ptr PQuant; - /** - * smart reference for accessing an existing quantiser - */ - class QuantiserRef - { - size_t hashID_; - - public: - QuantiserRef (Quantiser const&); - - // using standard copy; - - - const Quantiser * - operator-> () - { - UNIMPLEMENTED ("how to manage and address the existing quantisers"); - } - }; /** @@ -96,8 +80,8 @@ namespace time { struct Frames : Format { - static void rebuild (FrameNr&, Quantiser const&, TimeValue const&); - static TimeValue evaluate (FrameNr const&, QuantiserRef); + static void rebuild (FrameNr&, QuantR, TimeValue const&); + static TimeValue evaluate (FrameNr const&, QuantR); }; @@ -110,8 +94,8 @@ namespace time { struct Smpte : Format { - static void rebuild (SmpteTC&, Quantiser const&); - static TimeValue evaluate (SmpteTC const&, QuantiserRef); + static void rebuild (SmpteTC&, QuantR); + static TimeValue evaluate (SmpteTC const&, QuantR); }; @@ -125,8 +109,8 @@ namespace time { struct Hms : Format { - static void rebuild (HmsTC&, Quantiser const&); - static TimeValue evaluate (HmsTC const&, QuantiserRef); + static void rebuild (HmsTC&, QuantR); + static TimeValue evaluate (HmsTC const&, QuantR); }; @@ -142,8 +126,8 @@ namespace time { struct Seconds : Format { - static void rebuild (Secs&, Quantiser const&); - static TimeValue evaluate (Secs const&, QuantiserRef); + static void rebuild (Secs&, QuantR); + static TimeValue evaluate (Secs const&, QuantR); }; diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 9b81d40a2..ac0df16a8 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -56,9 +56,9 @@ namespace time { /** */ - QuTime::QuTime (TimeValue raw, Quantiser const& quantisation_to_use) + QuTime::QuTime (TimeValue raw, PQuant quantisation_to_use) : Time(raw) - , quantiser_(&quantisation_to_use) + , quantiser_(quantisation_to_use) { } diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 978739635..c4831785d 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -45,26 +45,21 @@ namespace time { * by quantising the given time value */ void - Frames::rebuild (FrameNr& framecnt, Quantiser const& quantiser, TimeValue const& rawTime) + Frames::rebuild (FrameNr& framecnt, QuantR quantiser, TimeValue const& rawTime) { framecnt.setValueRaw(quantiser.gridPoint (rawTime)); } /** calculate the time point denoted by this frame count */ TimeValue - Frames::evaluate (FrameNr const& framecnt, QuantiserRef quantiser) + Frames::evaluate (FrameNr const& framecnt, QuantR quantiser) { - return quantiser->timeOf (framecnt); + return quantiser.timeOf (framecnt); } } - /** */ - QuantiserRef::QuantiserRef (Quantiser const&) - : hashID_(123) /////////////////////////////////////////////////TODO - { } - /** */ FrameNr::FrameNr (QuTime const& quantisedTime) diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index fdfe5f303..978b3c503 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -59,8 +59,8 @@ namespace time { Time getTime() const { return Time(value()); } protected: - TCode (QuantiserRef const& qID) - : qID_(qID) + TCode (PQuant const& quant) + : quantiser_(quant) { } virtual string show() const =0; @@ -68,7 +68,7 @@ namespace time { virtual TimeValue value() const =0; protected: - QuantiserRef qID_; + PQuant quantiser_; }; @@ -88,7 +88,7 @@ namespace time { string show() const { return string(show())+"fr"; } Literal tcID() const { return "Frame-count"; } - TimeValue value() const { return Format::evaluate (*this, qID_); } + TimeValue value() const { return Format::evaluate (*this, *quantiser_); } public: typedef format::Frames Format; diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp index be5e82b71..161d28bfb 100644 --- a/src/lib/time/timequant.hpp +++ b/src/lib/time/timequant.hpp @@ -47,13 +47,13 @@ namespace time { class QuTime : public Time { - const Quantiser *quantiser_; + PQuant quantiser_; public: QuTime (TimeValue raw, Symbol gridID); - QuTime (TimeValue raw, Quantiser const& quantisation_to_use); + QuTime (TimeValue raw, PQuant quantisation_to_use); - operator QuantiserRef() const; + operator PQuant() const; template bool supports() const; @@ -72,10 +72,10 @@ namespace time { /* == implementation == */ inline - QuTime::operator QuantiserRef() const + QuTime::operator PQuant() const { ASSERT (quantiser_); - return QuantiserRef(*quantiser_); + return quantiser_; } template diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index 50d9d9127..ea5a6ed96 100644 --- a/tests/lib/time/time-quantisation-test.cpp +++ b/tests/lib/time/time-quantisation-test.cpp @@ -90,7 +90,7 @@ namespace test{ void check_theFullStory (TimeValue org) { - FixedFrameQuantiser fixQ(25); + PQuant fixQ (new FixedFrameQuantiser(25)); QuTime qVal (org, fixQ); CHECK ( qVal.supports()); From bdc1800470331d05d239628cb1e6f3c240c8c553 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 15 Jan 2011 01:45:55 +0100 Subject: [PATCH 067/140] add support for offset linear combinations --- src/lib/time/timevalue.hpp | 34 ++++++++++++++++++++++-------- tests/lib/time/time-value-test.cpp | 2 ++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 8a0b1f5fe..0778022a7 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -148,7 +148,7 @@ namespace time { // Supporting multiplication with integral factor TimeVar& operator*= (int fact) { t_ *= fact; return *this; } - // Supporting flip sign + // Supporting sign flip TimeVar operator- () const { return TimeVar(*this)*=-1; } // baseclass TimeValue is already totally_ordered @@ -185,16 +185,32 @@ namespace time { { return TimeValue(std::llabs (t_)); } - - Offset - operator+ (Offset const& toChain) const - { - TimeVar distance(*this); - distance += toChain; - return Offset(distance); - } }; + //-- support linear offset chaining --------------- + + inline Offset + operator+ (Offset const& start, Offset const& toChain) + { + TimeVar distance(start); + distance += toChain; + return Offset(distance); + } + + inline Offset + operator* (int factor, Offset const& o) + { + TimeVar distance(o); + distance *= factor; + return Offset(distance); + } + + inline Offset + operator* (Offset const& distance, int factor) + { + return factor*distance; + } + diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index ddc026b9d..a25d22b0c 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -230,6 +230,8 @@ namespace test{ // chaining and copy construction Offset off9 (off5 + Offset(four)); CHECK (9 == off9); + // simple linear combinations + CHECK (7 == -2*off9 + off5*5); } From ee0dcf3ba000d0eb8a7645aa9f018c4389e44b2e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 15 Jan 2011 03:49:35 +0100 Subject: [PATCH 068/140] introduce generic grid API to subsume quantiser and grid asset --- src/lib/time/grid.hpp | 76 +++++++++++++++++++ src/lib/time/lumitime.cpp | 2 +- src/lib/time/quantiser.cpp | 19 ++++- src/lib/time/quantiser.hpp | 11 ++- src/proc/asset/meta/time-grid.cpp | 35 ++++++--- src/proc/asset/meta/time-grid.hpp | 15 +++- .../proc/asset/meta/time-grid-basics-test.cpp | 2 +- tests/lib/time/time-quantisation-test.cpp | 2 +- tests/lib/time/time-value-test.cpp | 2 +- wiki/renderengine.html | 4 +- 10 files changed, 142 insertions(+), 26 deletions(-) create mode 100644 src/lib/time/grid.hpp diff --git a/src/lib/time/grid.hpp b/src/lib/time/grid.hpp new file mode 100644 index 000000000..57a06eec0 --- /dev/null +++ b/src/lib/time/grid.hpp @@ -0,0 +1,76 @@ +/* + GRID.hpp - time scale grid abstraction + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + + +#ifndef LIB_TIME_GRID_H +#define LIB_TIME_GRID_H + +#include "lib/time/timevalue.hpp" + +#include + + +namespace lib { +namespace time { + + + /** + * Abstraction of a value alignment grid. + * Such a grid has an underlying scale (origin and measurement) + * and is comprised of distinct grid intervals, which can be addressed + * by a ordering number, centred at origin with interval number zero. + * The typical example is a 25fps time frame grid, but indeed the + * spacing of the intervals is not necessarily constant. An entity + * defining such a grid provides functions to calculate the + * grid coordinates and to convert back to plain values. + * This includes a way of rounding to the next lower + * grid point, usable for grid aligning values. + * + * \par usage + * For one there is the lib::time::Quantiser, which directly + * implements this interface and plays a central role when it comes + * to converting continuous time into any kind of frame based timecode. + * Besides that, the session stores asset::TimeGrid definitions, which + * can be used to create a Quantiser suitable for some specific output + * bus or rendering target format. + * + * @todo WIP-WIP-WIP + * @todo maybe abstract this from Time altogether? + */ + class Grid + { + public: + virtual ~Grid(); ///< this is an Interface + + + virtual TimeValue gridAlign (TimeValue const& raw) const =0; + virtual long gridPoint (TimeValue const& raw) const =0; + virtual TimeValue timeOf (long gridPoint) const =0; + virtual TimeValue timeOf (FSecs, int =0) const =0; + }; + + typedef std::tr1::shared_ptr PGrid; + + + +}} // lib::time +#endif diff --git a/src/lib/time/lumitime.cpp b/src/lib/time/lumitime.cpp index b42fd4f4c..af3313772 100644 --- a/src/lib/time/lumitime.cpp +++ b/src/lib/time/lumitime.cpp @@ -100,7 +100,7 @@ namespace time { /** predefined constant for PAL framerate */ const FrameRate FrameRate::PAL (25); - const FrameRate FrameRate::NTSC (3000,1001); + const FrameRate FrameRate::NTSC (30000,1001); /** @return time span of one frame of this rate, diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index ac0df16a8..22a469505 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -46,7 +46,7 @@ namespace time { - Quantiser::~Quantiser() { } // hint to emit the VTable here... + Grid::~Grid() { } // hint to emit the VTable here... /** */ @@ -125,6 +125,23 @@ namespace time { } + /** calculate time coordinates of a time spec relative to this quantised time scale + * @param gridTime seconds relative to the origin of this scale + * @param gridOffset additional offset in grid intervals (frames) + * @return time point measured in Lumiera internal time + * @warning returned time values are limited by the + * valid range of lumiera::Time + */ + TimeValue + FixedFrameQuantiser::timeOf (FSecs gridTime, int gridOffset) const + { + Time gt(gridTime); + TimeVar timePoint = gt + origin_; + timePoint += gridOffset * raster_; + return timePoint; + } + + LUMIERA_ERROR_DEFINE (UNKNOWN_GRID, "referring to an undefined grid or scale in value quantisation"); diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index bbc88db70..ebb02e0a7 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -25,8 +25,9 @@ #define LIB_TIME_QUANTISER_H #include "lib/error.hpp" -#include "lib/time/timevalue.hpp" +#include "lib/time/grid.hpp" #include "lib/time/formats.hpp" +#include "lib/time/timevalue.hpp" #include "lib/iter-adapter.hpp" //#include @@ -69,14 +70,13 @@ namespace time { * @todo WIP-WIP-WIP */ class Quantiser + : public Grid { typedef std::vector _FormatTable; typedef _FormatTable::const_iterator _SrcIter; typedef lib::PtrDerefIter<_SrcIter> _Iter; public: - virtual ~Quantiser(); ///< this is an ABC - template bool supports() const; @@ -84,10 +84,12 @@ namespace time { typedef _Iter iterator; iterator getSupportedFormats() const; + + //------Grid-API---------------------------------------------- virtual TimeValue gridAlign (TimeValue const& raw) const =0; virtual long gridPoint (TimeValue const& raw) const =0; virtual TimeValue timeOf (long gridPoint) const =0; - + virtual TimeValue timeOf (FSecs, int =0) const =0; }; @@ -117,6 +119,7 @@ namespace time { TimeValue gridAlign (TimeValue const&) const; long gridPoint (TimeValue const&) const; TimeValue timeOf (long gridPoint) const; + TimeValue timeOf (FSecs, int =0) const; }; diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index c1ab270e8..bc98fb234 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -24,7 +24,7 @@ #include "proc/asset/meta/time-grid.hpp" #include "proc/asset/entry-id.hpp" #include "proc/assetmanager.hpp" -//#include "lib/time/quantiser.hpp" +#include "lib/time/quantiser.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/display.hpp" //#include "lib/util.hpp" @@ -60,6 +60,7 @@ namespace meta { using lib::time::TimeSpan; using lib::time::Duration; using lib::time::Offset; + using lib::time::FSecs; /** * TimeGrid implementation: a trivial time grid, @@ -69,14 +70,24 @@ namespace meta { class SimpleTimeGrid : public TimeGrid { - TimeSpan timeBase_; + lib::time::FixedFrameQuantiser frameGrid_; + + /* == grid API forwarded to embedded quantiser == */ + TimeValue gridAlign (TimeValue const& rawTime) const { return frameGrid_.gridAlign (rawTime); } + long gridPoint (TimeValue const& rawTime) const { return frameGrid_.gridPoint (rawTime); } + TimeValue timeOf (long gridPoint) const { return frameGrid_.timeOf (gridPoint); } + TimeValue timeOf (FSecs gridTime, int gridOffset =0) const { return frameGrid_.timeOf (gridTime,gridOffset); } - // TODO implement the TimeGrid API here public: - SimpleTimeGrid (Time start, Duration gridSpacing, EntryID const& name) + SimpleTimeGrid (Time start, Duration frameDuration, EntryID const& name) : TimeGrid (name) - , timeBase_(start,gridSpacing) + , frameGrid_(frameDuration,start) + { } + + SimpleTimeGrid (Time start, FrameRate frames_per_second, EntryID const& name) + : TimeGrid (name) + , frameGrid_(frames_per_second,start) { } }; @@ -103,12 +114,10 @@ namespace meta { if (!fps_) throw error::Config ("attempt to build a TimeGrid with 0 frames per second"); - Time spacing = 1 / fps_; // seconds per frame - format gridIdFormat("grid_%f_%s"); string name = str(gridIdFormat % fps_ % origin_); EntryID nameID (name); - TimeGrid& newGrid (*new SimpleTimeGrid(origin_, Offset(spacing), nameID)); + TimeGrid& newGrid (*new SimpleTimeGrid(origin_, fps_, nameID)); return AssetManager::instance().wrap (newGrid); } @@ -117,16 +126,20 @@ namespace meta { /* === TimeGrid shortcut builder functions === */ PGrid - TimeGrid::build (Symbol gridID, FSecs frames_per_second) + TimeGrid::build (Symbol gridID, FrameRate frames_per_second) { return build (gridID,frames_per_second, Time(0)); } PGrid - TimeGrid::build (Symbol gridID, FSecs frames_per_second, Time origin) + TimeGrid::build (Symbol gridID, FrameRate frames_per_second, Time origin) { - UNIMPLEMENTED ("build a trivial time grid"); + Builder spec; + spec.fps_ = frames_per_second; + spec.origin_ = origin; + + return spec.commit(); } diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 1020b016e..77f589ca9 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -48,6 +48,7 @@ #define ASSET_META_TIME_GRID_H #include "proc/asset/meta.hpp" +#include "lib/time/grid.hpp" #include "lib/time/timevalue.hpp" #include "lib/symbol.hpp" @@ -59,6 +60,7 @@ namespace meta { using lib::Symbol; using lib::time::Time; using lib::time::TimeValue; + using lib::time::FrameRate; using lib::time::FSecs; @@ -73,15 +75,20 @@ namespace meta { */ class TimeGrid : public Meta + , public lib::time::Grid { public: + //--------Grid-API------------------------------------ + TimeValue gridAlign (TimeValue const& raw) const =0; + long gridPoint (TimeValue const& raw) const =0; + TimeValue timeOf (long gridPoint) const =0; + TimeValue timeOf (FSecs, int =0) const =0; - // TODO define the TimeGrid API here /* === shortcut builder functions === */ - static PGrid build (Symbol gridID, FSecs frames_per_second); - static PGrid build (Symbol gridID, FSecs frames_per_second, Time origin); + static PGrid build (Symbol gridID, FrameRate frames_per_second); + static PGrid build (Symbol gridID, FrameRate frames_per_second, Time origin); protected: TimeGrid (EntryID const&); @@ -94,7 +101,7 @@ namespace meta { template<> struct Builder { - FSecs fps_; + FrameRate fps_; Time origin_; /** when building a compound or variable grid, diff --git a/tests/components/proc/asset/meta/time-grid-basics-test.cpp b/tests/components/proc/asset/meta/time-grid-basics-test.cpp index c85fe2b95..2314927c2 100644 --- a/tests/components/proc/asset/meta/time-grid-basics-test.cpp +++ b/tests/components/proc/asset/meta/time-grid-basics-test.cpp @@ -66,7 +66,7 @@ namespace test { namespace { // Test definitions... Time testOrigin (12,23); - FSecs testFps (5,4); + FrameRate testFps (5,4); } diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index ea5a6ed96..4f12fc64e 100644 --- a/tests/lib/time/time-quantisation-test.cpp +++ b/tests/lib/time/time-quantisation-test.cpp @@ -123,7 +123,7 @@ namespace test{ void checkMultipleGrids (TimeValue org) { - TimeGrid::build("my_alternate_grid", FSecs(30000,1001)); + TimeGrid::build("my_alternate_grid", FrameRate::NTSC); QuTime palVal (org, "my_simple_grid"); QuTime ntscVal (org, "my_alternate_grid"); diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index a25d22b0c..a1533ca67 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -261,7 +261,7 @@ namespace test{ CHECK (Time(2) == unit); // duration of 50 frames at 25fps is... (guess what) CHECK (FrameRate::PAL.duration() == Time(FSecs(1,25))); - CHECK (FrameRate::NTSC.duration() == Time(FSecs(1001,3000))); + CHECK (FrameRate::NTSC.duration() == Time(FSecs(1001,30000))); cout << "NTSC-Framerate = " << FrameRate::NTSC.asDouble() << endl; CHECK (zero == Duration::NIL); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 83e9cff48..013b42c4d 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6746,7 +6746,7 @@ Question is: how fine grained and configurable needs this to be?
-
+
The handling of [[Timecode]] is closely related to [[time representation and quantisation|TimeQuant]]. In fact, these two topics blend into one another. Time will be quantised into a //grid,// but this grid only makes sense when linked to a externally relevant meaning and representation, which is the Timecode. But a timecode value also denotes a specific point in time -- performing operations on a timecode is equivalent to manipulating a quantised time value.
 
 !Problem of dependencies
@@ -6770,7 +6770,7 @@ And last but not least, it is possible to get a new ~TimeValue, reflecting the c
 
 !!{{red{WIP 1/11}}}design tasks
 * @@color:green;✔@@ find out about the connection to the MetaAsset &rarr; [[Advice]]
-* determine the primitives which need to be on the //real quantiser API.//
+* @@color:green;✔@@ determine the primitives which need to be on the //effective quantiser API.//
 * find out how a format can address the individual components it's comprised of
 * decide how a concrete TC value can refer to his quantiser
 * maybe coin a //value handle// -- to tie the three required parts together

From 8bc5bf88c9072ea8bbc9d854ba5665b774fbe72f Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 15 Jan 2011 13:10:02 +0100
Subject: [PATCH 069/140] fill in the missing parts to create a grid asset

---
 src/proc/asset/meta.cpp           | 17 +++++++++--------
 src/proc/asset/meta.hpp           |  4 ++--
 src/proc/asset/meta/time-grid.cpp | 15 ++++++++++-----
 src/proc/asset/meta/time-grid.hpp |  7 +++++--
 src/proc/asset/struct-scheme.hpp  | 12 +++++++++++-
 5 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/src/proc/asset/meta.cpp b/src/proc/asset/meta.cpp
index 09a771068..3e5e15e99 100644
--- a/src/proc/asset/meta.cpp
+++ b/src/proc/asset/meta.cpp
@@ -24,7 +24,6 @@
 #include "proc/assetmanager.hpp"
 #include "proc/asset/meta.hpp"
 #include "lib/util.hpp"
-#include "include/logging.h"
 
 
 namespace asset {
@@ -50,14 +49,15 @@ namespace asset {
   
   /** Generic factory method for Metadata Asset instances.
    *  @param  EntryID specifying the type and a human readable name-ID
-   *  @return an Meta smart ptr linked to the internally registered smart ptr
-   *          created as a side effect of calling the concrete Meta subclass ctor.
+   *  @return an meta::Builder struct with the metadata parameters. After configuring
+   *          and tweaking those parameters, the builder's \c commit() function
+   *          will create a new (immutable) meta asset.
    */
   template
   meta::Builder
-  MetaFactory::operator () (EntryID elementIdentity)
+  MetaFactory::operator() (EntryID elementIdentity)
   {
-    UNIMPLEMENTED ("Meta-Factory");
+    return meta::Builder (elementIdentity.getSym());
   }
   
   
@@ -66,14 +66,15 @@ namespace asset {
    *          to be augmented and further specialised. Can indeed
    *          be an existing asset::Meta instance
    *  @param  EntryID specifying the type and a human readable name-ID
-   *  @return an Meta smart ptr linked to the internally registered smart ptr
-   *          created as a side effect of calling the concrete Meta subclass ctor.
+   *  @return an meta::Builder struct with the metadata parameters. After configuring
+   *          and tweaking those parameters, the builder's \c commit() function
+   *          will create a new (immutable) meta asset.
    */
   template
   meta::Builder
   MetaFactory::operator() (Descriptor const& prototype, EntryID elementIdentity)
   {
-    UNIMPLEMENTED ("Meta-Factory");
+    UNIMPLEMENTED ("Meta-Factory: extend or supersede existing meta asset");       ////////////////////TICKET #746
   }
   
   
diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp
index 21f029c14..77e08b71a 100644
--- a/src/proc/asset/meta.hpp
+++ b/src/proc/asset/meta.hpp
@@ -89,7 +89,7 @@ namespace asset {
     class Descriptor
       {
       public:
-        virtual ~Descriptor();  ///< this is an ABC
+        virtual ~Descriptor();  ///< this is an Interface
       };
       
       /**
@@ -122,7 +122,7 @@ namespace asset {
       
       virtual const ID& getID()  const    ///< @return ID of kind Meta 
         { 
-          return static_cast& > (Asset::getID()); 
+          return static_cast& > (Asset::getID());
         }
       
     protected:
diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp
index bc98fb234..dea09cb91 100644
--- a/src/proc/asset/meta/time-grid.cpp
+++ b/src/proc/asset/meta/time-grid.cpp
@@ -27,12 +27,13 @@
 #include "lib/time/quantiser.hpp"
 #include "lib/time/timevalue.hpp"
 #include "lib/time/display.hpp"
-//#include "lib/util.hpp"
+#include "lib/util.hpp"
 //#include "include/logging.h"
 
 #include 
 #include 
 
+using util::isnil;
 using boost::format;
 using boost::str;
 using std::string;
@@ -114,9 +115,12 @@ namespace meta {
     if (!fps_)
       throw error::Config ("attempt to build a TimeGrid with 0 frames per second");
     
-    format gridIdFormat("grid_%f_%s");
-    string name = str(gridIdFormat % fps_ % origin_);
-    EntryID nameID (name);
+    if (isnil (id_))
+      {
+        format gridIdFormat("grid_%f_%s");
+        id_ = str(gridIdFormat % fps_ % origin_);
+      }
+    EntryID nameID (id_);
     TimeGrid& newGrid (*new SimpleTimeGrid(origin_, fps_, nameID));
     
     return AssetManager::instance().wrap (newGrid);
@@ -135,7 +139,8 @@ namespace meta {
   PGrid
   TimeGrid::build (Symbol gridID, FrameRate frames_per_second, Time origin)
   {
-    Builder spec;
+    string name(gridID);
+    Builder spec(name);
     spec.fps_ = frames_per_second;
     spec.origin_ = origin;
     
diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp
index 77f589ca9..a56380807 100644
--- a/src/proc/asset/meta/time-grid.hpp
+++ b/src/proc/asset/meta/time-grid.hpp
@@ -101,6 +101,8 @@ namespace meta {
   template<>
   struct Builder
     {
+      string id_;
+      
       FrameRate fps_;
       Time origin_;
       
@@ -116,8 +118,9 @@ namespace meta {
        * You need at least to set the framerate,
        * in order to create a usable TimeGrid
        */
-      Builder()
-        : fps_(0)
+      Builder(string const& nameID  ="")
+        : id_(nameID)
+        , fps_(0)
         , origin_(TimeValue(0))
         , predecessor_()
         { }
diff --git a/src/proc/asset/struct-scheme.hpp b/src/proc/asset/struct-scheme.hpp
index f39069101..e0f7af463 100644
--- a/src/proc/asset/struct-scheme.hpp
+++ b/src/proc/asset/struct-scheme.hpp
@@ -64,6 +64,10 @@ namespace asset{
   class Timeline;
   class Sequence;
   
+  namespace meta {
+    class TimeGrid;
+  }
+  
   
   namespace idi  {
     
@@ -78,7 +82,7 @@ namespace asset{
       {
         static Symbol namePrefix();
         static Symbol catFolder();
-        static Symbol idSymbol();
+        static Symbol idSymbol();   ///< used as type predicate symbol
       };
     
     
@@ -131,6 +135,12 @@ namespace asset{
         static Symbol catFolder()  { return "sequences";}
         static Symbol idSymbol()   { return "sequence"; }
       };
+    template<> struct StructTraits
+      {
+        static Symbol namePrefix() { return "grid";     }
+        static Symbol catFolder()  { return "time-scales";}
+        static Symbol idSymbol()   { return "timeGrid"; }
+      };
     
     
     /* catch-all defaults */

From a70376dc4b8033899d8b25ce06d322dca78dc4aa Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 15 Jan 2011 14:07:25 +0100
Subject: [PATCH 070/140] grid asset passes first basic unit test!

---
 src/proc/asset/meta/time-grid.hpp             |  2 +-
 .../proc/asset/meta/time-grid-basics-test.cpp | 30 +++++++++++++++----
 tests/lib/time/quantiser-basics-test.cpp      |  4 +--
 tests/lib/time/time-quantisation-test.cpp     |  2 +-
 4 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp
index a56380807..2de580655 100644
--- a/src/proc/asset/meta/time-grid.hpp
+++ b/src/proc/asset/meta/time-grid.hpp
@@ -120,7 +120,7 @@ namespace meta {
        */
       Builder(string const& nameID  ="")
         : id_(nameID)
-        , fps_(0)
+        , fps_(1)
         , origin_(TimeValue(0))
         , predecessor_()
         { }
diff --git a/tests/components/proc/asset/meta/time-grid-basics-test.cpp b/tests/components/proc/asset/meta/time-grid-basics-test.cpp
index 2314927c2..ab7f03c91 100644
--- a/tests/components/proc/asset/meta/time-grid-basics-test.cpp
+++ b/tests/components/proc/asset/meta/time-grid-basics-test.cpp
@@ -26,6 +26,7 @@
 
 #include "proc/asset/meta.hpp"
 #include "proc/asset/meta/time-grid.hpp"
+#include "lib/time/timevalue.hpp"
 //#include "proc/asset/entry-id.hpp"
 //#include "lib/p.hpp"
 //#include "proc/assetmanager.hpp"
@@ -37,6 +38,7 @@
 //#include "lib/symbol.hpp"
 
 //#include 
+#include 
 //#include 
 //#include 
 
@@ -46,6 +48,7 @@
 //using util::and_all;
 //using util::for_each;
 //using util::isnil;
+using boost::rational_cast;
 using lib::test::randStr;
 //using lib::Literal;
 //using lib::Symbol;
@@ -60,14 +63,18 @@ namespace asset {
 namespace meta {
 namespace test {
   
+  using namespace lib::time;
+  
   typedef Builder GridBuilder;
   typedef EntryID GridID;
   
   namespace { // Test definitions...
     
-    Time testOrigin (12,23);
-    FrameRate testFps (5,4);
+    const Time testOrigin (12,34);
+    const FrameRate testFps (5,6);
     
+    const uint MAX_FRAMES = 1000;
+    const uint DIRT_GRAIN = 50;
   }
   
   
@@ -98,17 +105,28 @@ namespace test {
           GridID myGrID (randStr(8));
           GridBuilder spec = asset::Meta::create (myGrID);
           
-          CHECK (!spec.fps_);
-          CHECK (spec.origin_ == Time(0));
+          CHECK ( spec.fps_    == 1);
+          CHECK ( spec.origin_ == Time(0));
           CHECK (!spec.predecessor_);
           
-          spec.fps_ = testFps;
+          spec.fps_    = testFps;
           spec.origin_ = testOrigin;
           
           PGrid myGrid = spec.commit();
           CHECK (myGrid);
           
-          UNIMPLEMENTED ("the basic Grid API, so we can actually check anything");
+           // now verify the grid
+          //  by performing some conversions...
+          int randomFrame = (rand() % MAX_FRAMES);
+          
+          Time point (myGrid->timeOf (randomFrame));
+          CHECK (point == testOrigin + randomFrame * testFps.duration());
+          
+          uint fract = rand() % DIRT_GRAIN;
+          FSecs dirt = rational_cast (1 / testFps / fract);
+          
+          Time dirty(point + Time(dirt));
+          CHECK (point == testOrigin + myGrid->gridAlign(dirty));
         }
       
       
diff --git a/tests/lib/time/quantiser-basics-test.cpp b/tests/lib/time/quantiser-basics-test.cpp
index bd8c11c72..e790d5f8b 100644
--- a/tests/lib/time/quantiser-basics-test.cpp
+++ b/tests/lib/time/quantiser-basics-test.cpp
@@ -40,7 +40,7 @@ namespace test{
   namespace {
     
     const uint MAX_FRAMES = 25*500;
-    const uint MAX_DIRT   = 50;
+    const uint DIRT_GRAIN = 50;
     
     const FSecs F25(1,25); // duration of one PAL frame 
   }
@@ -82,7 +82,7 @@ namespace test{
           FixedFrameQuantiser fixQ(25);
           
           uint frames = (rand() % MAX_FRAMES);
-          FSecs dirt  = (F25 / (rand() % MAX_DIRT));
+          FSecs dirt  = (F25 / (rand() % DIRT_GRAIN));
           
           Time rawTime = dirt + frames*F25;
           
diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp
index 4f12fc64e..3fae0c0f9 100644
--- a/tests/lib/time/time-quantisation-test.cpp
+++ b/tests/lib/time/time-quantisation-test.cpp
@@ -82,7 +82,7 @@ namespace test{
           FrameNr count(qVal);                      // materialise this quantised time into..
           int n = count;                            // frame count, accessible as plain number
           
-          CHECK (Time(FSecs(n-1, 25)) < org);       // verify quantisation: the original time
+          CHECK (Time(FSecs(n-1, 25)) <= org);      // verify quantisation: the original time
           CHECK (org < Time(FSecs(n+1, 25)));       // is properly bracketed by (n-1, n+2)
         }
       

From 240c5ac232fa030ad665843d669ae1356ce678dd Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 15 Jan 2011 14:43:50 +0100
Subject: [PATCH 071/140] extract asset iostream display into separate header

---
 src/proc/asset.cpp                            |  1 +
 src/proc/asset/asset-format.hpp               | 53 ++++++++++++++++++
 src/proc/asset/category.hpp                   |  7 ---
 .../proc/asset/asset-category-test.cpp        |  1 +
 .../proc/asset/asset-diagnostics.hpp          | 56 +++++++++----------
 5 files changed, 83 insertions(+), 35 deletions(-)
 create mode 100644 src/proc/asset/asset-format.hpp

diff --git a/src/proc/asset.cpp b/src/proc/asset.cpp
index 69bcd8a38..0f8560ad1 100644
--- a/src/proc/asset.cpp
+++ b/src/proc/asset.cpp
@@ -23,6 +23,7 @@
 
 #include "proc/asset.hpp"
 #include "proc/assetmanager.hpp"
+#include "proc/asset/asset-format.hpp"
 #include "lib/util-foreach.hpp"
 #include "lib/util.hpp"
 
diff --git a/src/proc/asset/asset-format.hpp b/src/proc/asset/asset-format.hpp
new file mode 100644
index 000000000..8ee6c71ce
--- /dev/null
+++ b/src/proc/asset/asset-format.hpp
@@ -0,0 +1,53 @@
+/*
+  ASSET-FORMAT.hpp  -  helpers for display of asset entities
+
+  Copyright (C)         Lumiera.org
+    2011,               Hermann Vosseler 
+
+  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.
+
+*/
+
+
+#ifndef ASSET_ASSET_FORMAT_H
+#define ASSET_ASSET_FORMAT_H
+
+#include "proc/asset.hpp"
+#include "proc/asset/category.hpp"
+
+#include 
+
+
+
+namespace asset {
+  
+  
+  inline std::ostream&
+  operator<< (std::ostream& os, Category const& cat)
+  {
+    return os << string(cat); 
+  }
+  
+  inline std::ostream&
+  operator<< (std::ostream& os, Asset::Ident const& idi)
+  {
+    return os << string(idi); 
+  }
+  
+  
+  
+  
+} // namespace asset
+#endif
diff --git a/src/proc/asset/category.hpp b/src/proc/asset/category.hpp
index aa263ee7d..b7c8df2f2 100644
--- a/src/proc/asset/category.hpp
+++ b/src/proc/asset/category.hpp
@@ -27,7 +27,6 @@
 #include "lib/symbol.hpp"
 
 #include 
-#include 
 #include 
 
 
@@ -101,12 +100,6 @@ namespace asset {
     };
   
   
-  inline ostream&
-  operator<< (ostream& os, Category const& cat)
-    {
-      return os << string(cat); 
-    }
-  
   inline size_t
   hash_value (Category const& cat)
   {
diff --git a/tests/components/proc/asset/asset-category-test.cpp b/tests/components/proc/asset/asset-category-test.cpp
index bc0676011..7bfb68f4e 100644
--- a/tests/components/proc/asset/asset-category-test.cpp
+++ b/tests/components/proc/asset/asset-category-test.cpp
@@ -25,6 +25,7 @@
 #include "lib/util.hpp"
 
 #include "proc/asset/category.hpp"
+#include "proc/asset/asset-format.hpp"
 
 #include 
 #include 
diff --git a/tests/components/proc/asset/asset-diagnostics.hpp b/tests/components/proc/asset/asset-diagnostics.hpp
index 08619bbba..76788d3f5 100644
--- a/tests/components/proc/asset/asset-diagnostics.hpp
+++ b/tests/components/proc/asset/asset-diagnostics.hpp
@@ -50,42 +50,42 @@ using std::string;
 using std::cout;
 
 
-namespace asset
-  {
- 
+namespace asset {
+  
   inline void
   dump (PcAsset& aa)
-    {
-      if (!aa)
-        cout << "Asset(NULL)\n";
-      else
-        {
-          format fmt("%s %|50T.| id=%s  adr=%p smart-ptr=%p use-count=%u");
-          cout << fmt % str(aa) % aa->getID() % aa.get() % &aa % (aa.use_count() - 1) << "\n";
-    }   }
- 
+  {
+    if (!aa)
+      cout << "Asset(NULL)\n";
+    else
+      {
+        format fmt("%s %|50T.| id=%s  adr=%p smart-ptr=%p use-count=%u");
+        cout << fmt % str(aa) % aa->getID() % aa.get() % &aa % (aa.use_count() - 1) << "\n";
+  }   }
+  
+  
   inline void
   dumpAssetManager ()
-    {
-      list assets (AssetManager::instance().listContent());
-      cout << "----all-registered-Assets----\n";
-      for_each (assets, bind (&dump, _1));
-    }
+  {
+    list assets (AssetManager::instance().listContent());
+    cout << "----all-registered-Assets----\n";
+    for_each (assets, bind (&dump, _1));
+  }
   
   
   template
   inline bool
   dependencyCheck (P child, P parent)
-    {
-      return (child == parent)
-          || (0 < child->getParents().size()
-             && (parent == child->getParents()[0])
-             && (contains (parent->getDependant(), child)))
-          ;
-    }
-
-    
-    
-
+  {
+    return (child == parent)
+        || (0 < child->getParents().size()
+           && (parent == child->getParents()[0])
+           && (contains (parent->getDependant(), child)))
+        ;
+  }
+  
+  
+  
+  
 } // namespace asset
 #endif

From aa5c78a30f918ff9444270d62080df7110f04f0d Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sat, 15 Jan 2011 15:04:23 +0100
Subject: [PATCH 072/140] TimeGridBasics_test pass

---
 src/proc/asset/meta/time-grid.cpp             |  3 +-
 tests/41asset.tests                           |  3 +-
 .../proc/asset/meta/time-grid-basics-test.cpp | 43 ++++++-------------
 wiki/renderengine.html                        | 14 +++---
 4 files changed, 24 insertions(+), 39 deletions(-)

diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp
index dea09cb91..b0b41f2c1 100644
--- a/src/proc/asset/meta/time-grid.cpp
+++ b/src/proc/asset/meta/time-grid.cpp
@@ -112,8 +112,7 @@ namespace meta {
     if (predecessor_)
       throw error::Invalid("compound and variable time grids are a planned feature"
                           , error::LUMIERA_ERROR_UNIMPLEMENTED);
-    if (!fps_)
-      throw error::Config ("attempt to build a TimeGrid with 0 frames per second");
+    ENSURE (fps_, "infinite grid was not properly detected by FrameRate ctor");
     
     if (isnil (id_))
       {
diff --git a/tests/41asset.tests b/tests/41asset.tests
index c218522fa..a117cd120 100644
--- a/tests/41asset.tests
+++ b/tests/41asset.tests
@@ -70,6 +70,7 @@ return: 0
 END
 
 
-PLANNED "Simple TimeGrid" TimeGridBasics_test <
 #include 
-//#include 
-//#include 
+#include 
 
-//using lib::test::showSizeof;
-//using lib::test::randStr;
-//using util::isSameObject;
-//using util::and_all;
-//using util::for_each;
-//using util::isnil;
 using boost::rational_cast;
 using lib::test::randStr;
-//using lib::Literal;
-//using lib::Symbol;
-//using lumiera::P;
-//using std::string;
-//using std::cout;
-//using std::endl;
+using std::cout;
+using std::endl;
 
 
 
-namespace asset {
+namespace asset{
 namespace meta {
 namespace test {
   
@@ -83,8 +64,6 @@ namespace test {
    * @test build some simple time grids and verify their behaviour
    *       for quantising (grid aligning) time values.
    * 
-   * @todo WIP-WIP-WIP                                       ////////////////////TICKET #736
-   * 
    * @see asset::meta::TimeGrid
    * @see time-quantisation-test.cpp usage context
    */
@@ -114,6 +93,7 @@ namespace test {
           
           PGrid myGrid = spec.commit();
           CHECK (myGrid);
+          CHECK (myGrid->ident.name == myGrID.getSym());
           
            // now verify the grid
           //  by performing some conversions...
@@ -133,9 +113,14 @@ namespace test {
       void
       createGrid_simplified()
         {
+          PGrid simplePALGrid = TimeGrid::build ("", FrameRate::PAL);
+          CHECK (simplePALGrid);
+          CHECK (!util::isnil (simplePALGrid->ident.name));   // note: name-ID filled in automatically
+          cout << "simple PAL Grid: " << simplePALGrid->ident << endl;
+          
+          CHECK (Time(2) == simplePALGrid->timeOf(50));
+          CHECK (Time(2) == simplePALGrid->timeOf(FSecs(2)));
         }
-      
-      
     };
   
   
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 013b42c4d..85651f235 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -2745,10 +2745,10 @@ For the case here in question this seems to be the ''resource allocation is cons
 And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the session or the sequences, which are created once and don't bear the danger of causing memory pressure. Besides, the ProcLayer code shouldn't issue "new" and "delete" when it comes in hand, rather it should use some centralized [[Factories]] for all allocation and freeing, so we can redirect these calls down to the backend, which may use pooling or special placement allocators or the like. The rationale is, for modern hardware/architectures, care has to be taken with heap allocations, esp. with many small objects and irregular usage patterns.
 
-
-
This category is comprised of the various aspects of the way the application controls and manages its own behaviour. They are more related to the way the application behaves, as opposed to the way the edited data is structured and organised.
-* StreamType
-* ScaleGrid
+
+
This category is comprised of the various aspects of the way the application controls and manages its own behaviour. They are more related to the way the application behaves, as opposed to the way the edited data is structured and organised (which is the realm of [[structural assets|StructAsset]]
+* StreamType &rarr; a type system for describing and relating media data streams
+* ScaleGrid &rarr; to manage time scales and frame alignment
 
 !accessing meta assets
 It turns out that all meta assets follow a distinct usage pattern: //they aren't built as individual entities.// Either, they are introduced into the system as part of a larger scale configuration activity, or they are //derived from category.// The latter fits in with a prototype-like approach; initially, the individual entry just serves to keep track of a categorisation, while at some point, such a link into a describing category may evolve into a local differentiation of some settings.
@@ -2762,7 +2762,7 @@ These observation leads to a design relying on a self referential structure: eac
 
 !!!mutating meta assets
 Meta assets are ''immutable'' -- but they can be //superseded.//
-For each meta asset instance, initially a //builder// is created for setting up the properties; when done, the builder will "drop off" the new meta asset instance. The same procedure is used augmenting or superseding an existing element.
+For each meta asset instance, initially a //builder// is created for setting up the properties; when done, the builder will "drop off" the new meta asset instance. The same procedure is used for augmenting or superseding an existing element.
 
@@ -6746,7 +6746,7 @@ Question is: how fine grained and configurable needs this to be?
-
+
The handling of [[Timecode]] is closely related to [[time representation and quantisation|TimeQuant]]. In fact, these two topics blend into one another. Time will be quantised into a //grid,// but this grid only makes sense when linked to a externally relevant meaning and representation, which is the Timecode. But a timecode value also denotes a specific point in time -- performing operations on a timecode is equivalent to manipulating a quantised time value.
 
 !Problem of dependencies
@@ -6772,7 +6772,7 @@ And last but not least, it is possible to get a new ~TimeValue, reflecting the c
 * @@color:green;✔@@ find out about the connection to the MetaAsset &rarr; [[Advice]]
 * @@color:green;✔@@ determine the primitives which need to be on the //effective quantiser API.//
 * find out how a format can address the individual components it's comprised of
-* decide how a concrete TC value can refer to his quantiser
+* @@color:green;✔@@ decide how a concrete TC value can refer to his quantiser &rarr; always using smart-ptr
 * maybe coin a //value handle// -- to tie the three required parts together
 
From 2c90335b1d50b7d6052d68f822d6bc0f262e0bc1 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 16 Jan 2011 15:41:34 +0100 Subject: [PATCH 073/140] WIP idea how to represent support for some timecode formats concrete quantiser instances need a way to state support for just some timecode formats -- yet I dont want to push vectors aroud all over the place --- src/lib/meta/no-instance.hpp | 47 +++++++++++++ src/lib/time/formats.hpp | 67 +++++++++++------- src/lib/time/grid.hpp | 2 +- src/lib/time/quantiser.hpp | 15 ++-- src/lib/time/timecode.cpp | 3 +- src/lib/typed-counter.hpp | 26 +++++-- src/proc/asset/meta/time-grid.cpp | 2 +- src/proc/asset/meta/time-grid.hpp | 2 +- tests/lib/time/format-support-test.cpp | 94 ++++++++++++++++++++++++++ 9 files changed, 217 insertions(+), 41 deletions(-) create mode 100644 src/lib/meta/no-instance.hpp create mode 100644 tests/lib/time/format-support-test.cpp diff --git a/src/lib/meta/no-instance.hpp b/src/lib/meta/no-instance.hpp new file mode 100644 index 000000000..73f7b71b0 --- /dev/null +++ b/src/lib/meta/no-instance.hpp @@ -0,0 +1,47 @@ +/* + NO-INSTANCE.hpp - marker for pure metaprogramming types + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + + +#ifndef LIB_META_NO_INSTANCE_H +#define LIB_META_NO_INSTANCE_H + +#include + + +namespace lib { +namespace meta{ + + /** + * An Entity never to be instantiated. + * Marker baseclass for elements used for metaprogramming only. + * Every attempt to instantiate such an element will cause an + * compilation failure + */ + template + struct NoInstance + { + NoInstance() { BOOST_STATIC_ASSERT(!sizeof(X)); } + NoInstance (NoInstance const&) { BOOST_STATIC_ASSERT(!sizeof(X)); } + }; + +}} // namespace lib::meta +#endif diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index 0bd6f7788..7ded43c77 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -25,6 +25,9 @@ #define LIB_TIME_FORMATS_H #include "lib/time/timevalue.hpp" +#include "lib/meta/no-instance.hpp" +#include "lib/meta/typelist.hpp" +#include "lib/meta/generator.hpp" //#include #include @@ -48,27 +51,11 @@ namespace time { typedef std::tr1::shared_ptr PQuant; - - /** - * descriptor for a time representation format (ABC). - * A time format describes how to specify time; it allows - * to format a time value and to parse a textual representation. - * @note while Format is an generic descriptor, the actual - * TCode (timecode) values are time values, which \em - * use a specific format, given as template parameter - * - * @todo WIP-WIP-WIP - */ - class Format - { - - public: - virtual ~Format(); - }; - - namespace format { + using lib::meta::NoInstance; // the following types are for metaprogramming only... + + /** * Frame count as timecode format. * An integral number used to count frames @@ -78,7 +65,7 @@ namespace time { * underlying framerate/quantisation remains implicit. */ struct Frames - : Format + : NoInstance { static void rebuild (FrameNr&, QuantR, TimeValue const&); static TimeValue evaluate (FrameNr const&, QuantR); @@ -92,7 +79,7 @@ namespace time { * frame number within the actual second. */ struct Smpte - : Format + : NoInstance { static void rebuild (SmpteTC&, QuantR); static TimeValue evaluate (SmpteTC const&, QuantR); @@ -107,7 +94,7 @@ namespace time { * floating point milliseconds value instead of the frame count */ struct Hms - : Format + : NoInstance { static void rebuild (HmsTC&, QuantR); static TimeValue evaluate (HmsTC const&, QuantR); @@ -124,7 +111,7 @@ namespace time { * decimal format, not the usual sexagesimal time format */ struct Seconds - : Format + : NoInstance { static void rebuild (Secs&, QuantR); static TimeValue evaluate (Secs const&, QuantR); @@ -163,5 +150,39 @@ namespace time { + using lumiera::typelist::Types; + + /** + * Denote support for a specific (timecode) format. + * This helper can be used to configure a selection of specific + * timecode formats to be or not to be supported by some facility. + * Formats are described by the format descriptor types defined in + * this header (or elsewhere for additional formats). This helper + * establishes an numeric ID at runtime and uses a bitset to keep + * track of the support for a given format. + * + * @todo WIP-WIP-WIP + */ + class Supported + { + public: + template + static Supported + formats(); + + template + bool + check(); + }; + + struct SupportStandardTimecode + : Supported + { + SupportStandardTimecode() + : Supported(formats >()) + { } + }; + + }}} // lib::time::format #endif diff --git a/src/lib/time/grid.hpp b/src/lib/time/grid.hpp index 57a06eec0..4053fa5b0 100644 --- a/src/lib/time/grid.hpp +++ b/src/lib/time/grid.hpp @@ -62,8 +62,8 @@ namespace time { virtual ~Grid(); ///< this is an Interface - virtual TimeValue gridAlign (TimeValue const& raw) const =0; virtual long gridPoint (TimeValue const& raw) const =0; + virtual TimeValue gridAlign (TimeValue const& raw) const =0; virtual TimeValue timeOf (long gridPoint) const =0; virtual TimeValue timeOf (FSecs, int =0) const =0; }; diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index ebb02e0a7..20764bf33 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -66,28 +66,29 @@ namespace time { /** * Facility to create grid-aligned time values. + * Effectively, a quantiser exposes the value Grid API, but + * additionally also manages a set of supported (display) formats or + * "time code" formats. Plus there is an static API to fetch a suitable + * quantiser instance by-name; actually this utilises a hidden link to + * the Lumiera session. Time quantisation and timecode handling explicitly + * relies on this Quantiser interface. * * @todo WIP-WIP-WIP */ class Quantiser : public Grid { - typedef std::vector _FormatTable; - typedef _FormatTable::const_iterator _SrcIter; - typedef lib::PtrDerefIter<_SrcIter> _Iter; public: template bool supports() const; - typedef _Iter iterator; - iterator getSupportedFormats() const; //------Grid-API---------------------------------------------- - virtual TimeValue gridAlign (TimeValue const& raw) const =0; virtual long gridPoint (TimeValue const& raw) const =0; + virtual TimeValue gridAlign (TimeValue const& raw) const =0; virtual TimeValue timeOf (long gridPoint) const =0; virtual TimeValue timeOf (FSecs, int =0) const =0; }; @@ -116,8 +117,8 @@ namespace time { FixedFrameQuantiser (Duration const& frame_duration, TimeValue referencePoint =TimeValue(0)); - TimeValue gridAlign (TimeValue const&) const; long gridPoint (TimeValue const&) const; + TimeValue gridAlign (TimeValue const&) const; TimeValue timeOf (long gridPoint) const; TimeValue timeOf (FSecs, int =0) const; }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index c4831785d..4af2fec19 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -36,8 +36,7 @@ namespace lib { namespace time { - Format::~Format() { } // emit VTable here.... - TCode::~TCode() { } + TCode::~TCode() { } // emit VTable here.... namespace format { diff --git a/src/lib/typed-counter.hpp b/src/lib/typed-counter.hpp index 252faaab5..f208f8cdc 100644 --- a/src/lib/typed-counter.hpp +++ b/src/lib/typed-counter.hpp @@ -22,13 +22,27 @@ /** @file typed-counter.hpp - ** Creating sets of type-based contexts. + ** Creating series of type-based contexts. ** The idea is to get a "slot" for any given type, so we can build - ** tables or families of implementations based on these types. Currently, - ** the slot allocation is based on static variables and thus is global. - ** This leads to a waste of slots, as the various clients of this service - ** typically will utilise different sets of types. - ** @todo WIP WIP... this is the first, preliminary version of a facility, + ** tables or families of implementations based on these types. Each of + ** those "slots" can be addressed by a distinct (compile time) type, but + ** at the same time holds a numeric ID (runtime assigned on demand). This + ** setup allows to bridge between metaprogramming and (runtime) dispatcher tables. + ** + ** Each such series of type-id-slots is associated to a distinct usage context. + ** Those usage contexts are discerned by the template parameter \c XY. Each of + ** these usage contexts uses a separate numbering scheme on his own, i.e. every + ** new type encountered at runtime gets the next higher ID number (slot). + ** @warning the actual ID numbers depend on the sequence of first encountering + ** a given type. If this sequence isn't reproducible between runs, then + ** also the generated type-IDs aren't reproducible. Thus its advisable + ** \em not to rely on any specific numeric value here, but always just + ** access the service through the type slots. + ** @note Locking is based on a class lock per usage context, but a lock needs + ** only be acquired to allocate a new ID number (double checked locking). + ** Thus lock contention is considered not to be a problem, yet we need + ** actually to verify this by real measurements (as of 2011) + ** @todo 2010 ... this is the first, preliminary version of a facility, ** which is expected to get quite important for custom allocation management. ** ** @see typed-counter-test.cpp diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index b0b41f2c1..65f1978c8 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -74,8 +74,8 @@ namespace meta { lib::time::FixedFrameQuantiser frameGrid_; /* == grid API forwarded to embedded quantiser == */ - TimeValue gridAlign (TimeValue const& rawTime) const { return frameGrid_.gridAlign (rawTime); } long gridPoint (TimeValue const& rawTime) const { return frameGrid_.gridPoint (rawTime); } + TimeValue gridAlign (TimeValue const& rawTime) const { return frameGrid_.gridAlign (rawTime); } TimeValue timeOf (long gridPoint) const { return frameGrid_.timeOf (gridPoint); } TimeValue timeOf (FSecs gridTime, int gridOffset =0) const { return frameGrid_.timeOf (gridTime,gridOffset); } diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 2de580655..01b96f480 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -80,8 +80,8 @@ namespace meta { public: //--------Grid-API------------------------------------ - TimeValue gridAlign (TimeValue const& raw) const =0; long gridPoint (TimeValue const& raw) const =0; + TimeValue gridAlign (TimeValue const& raw) const =0; TimeValue timeOf (long gridPoint) const =0; TimeValue timeOf (FSecs, int =0) const =0; diff --git a/tests/lib/time/format-support-test.cpp b/tests/lib/time/format-support-test.cpp new file mode 100644 index 000000000..066c44375 --- /dev/null +++ b/tests/lib/time/format-support-test.cpp @@ -0,0 +1,94 @@ +/* + FormatSupport(Test) - verify the configuration to support a specific format + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "lib/test/run.hpp" +#include "lib/time/formats.hpp" + +//#include +//#include + +//using std::rand; +//using std::cout; +//using std::endl; + + +namespace lib { +namespace time { +namespace format{ +namespace test { + + namespace { + + } + + + /******************************************************** + * @test check how support for a specific timecode format + * can be enabled and detected. Actually this test + * verifies a simple metaprogramming facility, + * which allows to check type support at runtime. + */ + class FormatSupport_test : public Test + { + virtual void + run (Arg) + { + SupportStandardTimecode just_fine; + Supported just_smpte = Supported::formats >(); + Supported just_simple = Supported::formats >(); + + Supported& support1 (just_fine); + Supported& support2 (just_smpte); + Supported& support3 (just_simple); + + CHECK ( support1.check()); + CHECK ( support1.check()); + CHECK ( support1.check()); + CHECK ( support1.check()); + + CHECK (!support2.check()); + CHECK ( support2.check()); + CHECK (!support2.check()); + CHECK (!support2.check()); + + CHECK (!support3.check()); + CHECK (!support3.check()); + CHECK ( support3.check()); + CHECK ( support3.check()); + + // format support descriptors are assignable + just_smpte = just_simple; + CHECK (support2.check() == support3.check()); + CHECK (support2.check() == support3.check()); + CHECK (support2.check() == support3.check()); + CHECK (support2.check() == support3.check()); + } + }; + + + /** Register this test class... */ + LAUNCHER (FormatSupport_test, "unit common"); + + + +}}}} // namespace lib::time::format::test From 92c4516caea7feb561b6db733a26c734719c4327 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 16 Jan 2011 19:50:42 +0100 Subject: [PATCH 074/140] implement this format-support descriptor --- src/lib/time/formats.hpp | 67 +++++++++++++++++++++----- tests/40components.tests | 5 ++ tests/lib/time/format-support-test.cpp | 14 +----- 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index 7ded43c77..e374c407d 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -28,10 +28,10 @@ #include "lib/meta/no-instance.hpp" #include "lib/meta/typelist.hpp" #include "lib/meta/generator.hpp" +#include "lib/typed-counter.hpp" -//#include #include -#include +#include namespace lib { @@ -150,10 +150,14 @@ namespace time { + /* == Descriptor to define Support for specific formats == */ + using lumiera::typelist::Types; - + using lumiera::typelist::Node; + using lumiera::typelist::NullType; + /** - * Denote support for a specific (timecode) format. + * Descriptor to denote support for a specific (timecode) format. * This helper can be used to configure a selection of specific * timecode formats to be or not to be supported by some facility. * Formats are described by the format descriptor types defined in @@ -161,25 +165,66 @@ namespace time { * establishes an numeric ID at runtime and uses a bitset to keep * track of the support for a given format. * - * @todo WIP-WIP-WIP + * @note preconfigured limit #MAXID on the absolute number of + * different Format types; maybe needs to be increased accordingly. + * @warning the actual numeric IDs might vary on each run + * @see TypedContext */ class Supported { - public: - template - static Supported - formats(); + enum { MAXID = 8 }; + + std::bitset flags_; + template + size_t + typeID() + { + return TypedContext::ID::get(); + } + + template + Supported + define(Node) ///< @internal set the flag for one Format in the typelist + { + flags_.set (typeID()); + return define(FS()); + } + Supported define(NullType) { return *this;} ///< recursion end + + Supported() { } ///< @note use #formats to set up a new descriptor + + + public: + /** build a new Descriptor to denote support for all the Formats, + * @param TY typelist holding all the Format types to support + */ + template + static Supported + formats() + { + typedef typename TY::List SupportedFormats; + return Supported().define(SupportedFormats()); + } + + /** check if a specific Format is supported */ template bool - check(); + check() + { + return flags_[typeID()]; + } }; + /** + * predefined standard configuration: + * Descriptor for supporting all the classical timecode formats + */ struct SupportStandardTimecode : Supported { SupportStandardTimecode() - : Supported(formats >()) + : Supported(formats< Types >()) { } }; diff --git a/tests/40components.tests b/tests/40components.tests index 97bacd935..490730d84 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -668,6 +668,11 @@ return: 0 END +TEST "Defining Suppport for specific timecode formats" FormatSupport_test < -//#include - -//using std::rand; -//using std::cout; -//using std::endl; - namespace lib { namespace time { namespace format{ namespace test { - namespace { - - } /******************************************************** @@ -54,8 +44,8 @@ namespace test { run (Arg) { SupportStandardTimecode just_fine; - Supported just_smpte = Supported::formats >(); - Supported just_simple = Supported::formats >(); + Supported just_smpte = Supported::formats< Types >(); + Supported just_simple = Supported::formats< Types >(); Supported& support1 (just_fine); Supported& support2 (just_smpte); From 71a80d3df6990266ad849c94549d7a8b7963919b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 16 Jan 2011 22:19:48 +0100 Subject: [PATCH 075/140] integrate check for supported formats into Quantiser --- src/lib/time/formats.hpp | 4 ++-- src/lib/time/quantiser.hpp | 16 ++++++++++++---- src/lib/time/timequant.hpp | 3 ++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index e374c407d..a5cccc321 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -178,7 +178,7 @@ namespace time { template size_t - typeID() + typeID() const { return TypedContext::ID::get(); } @@ -210,7 +210,7 @@ namespace time { /** check if a specific Format is supported */ template bool - check() + check() const { return flags_[typeID()]; } diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 20764bf33..c45489032 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -78,12 +78,20 @@ namespace time { class Quantiser : public Grid { + protected: + format::Supported supportedFormats_; + + Quantiser() + : supportedFormats_(format::SupportStandardTimecode()) + { } public: - template - bool supports() const; - + bool + supports() const + { + return supportedFormats_.check(); + } //------Grid-API---------------------------------------------- @@ -116,11 +124,11 @@ namespace time { FixedFrameQuantiser (FrameRate const& frames_per_second, TimeValue referencePoint =TimeValue(0)); FixedFrameQuantiser (Duration const& frame_duration, TimeValue referencePoint =TimeValue(0)); - long gridPoint (TimeValue const&) const; TimeValue gridAlign (TimeValue const&) const; TimeValue timeOf (long gridPoint) const; TimeValue timeOf (FSecs, int =0) const; + }; diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp index 161d28bfb..3b25592c3 100644 --- a/src/lib/time/timequant.hpp +++ b/src/lib/time/timequant.hpp @@ -82,7 +82,7 @@ namespace time { inline bool QuTime::supports() const { - return false; ////////////////TODO; + return quantiser_->supports(); } @@ -100,6 +100,7 @@ namespace time { QuTime::castInto (TC& timecode) const { typedef typename TC::Format Format; + REQUIRE (supports()); Format::rebuild (timecode, *quantiser_, TimeValue(*this)); } From 484c771d2a060921664bfc1f0dc61807937c4dff Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 16 Jan 2011 23:58:42 +0100 Subject: [PATCH 076/140] establish hidden advice link to the session --- src/lib/time/quantiser.cpp | 30 ++++++++++++++++++++--- src/lib/time/quantiser.hpp | 14 +++++------ src/lib/time/timequant.hpp | 14 +++++++++-- tests/lib/time/time-quantisation-test.cpp | 7 +++--- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/lib/time/quantiser.cpp b/src/lib/time/quantiser.cpp index 22a469505..9c4eca790 100644 --- a/src/lib/time/quantiser.cpp +++ b/src/lib/time/quantiser.cpp @@ -25,6 +25,7 @@ #include "lib/time/timevalue.hpp" #include "lib/time/timequant.hpp" #include "lib/time.h" +#include "lib/advice.hpp" #include @@ -40,7 +41,16 @@ namespace time { namespace { // implementation helpers... - ///////////TODO superfluous?? + PQuant + retrieveQuantiser (Symbol gridID) + { + advice::Request query(gridID); + PQuant grid_found = query.getAdvice(); + if (!grid_found) + throw error::Logic ("unable to fetch the quantisation grid -- is it already defined?" + , LUMIERA_ERROR_UNKNOWN_GRID); + return grid_found; + } }//(End) implementation helpers @@ -49,13 +59,25 @@ namespace time { Grid::~Grid() { } // hint to emit the VTable here... - /** */ + /** + * build a quantised time value, referring the time grid by-name. + * This is the preferred standard way of establishing a quantisation, + * but it requires an existing time scale defined in the Lumiera Session, + * as TimeGrid (meta asset). Usually, such a time scale gets built based + * on the format and parameters of an output bus. + */ QuTime::QuTime (TimeValue raw, Symbol gridID) - : Time(raw) /////////////////////////////////////////////////TODO fetch quantiser + : Time(raw) + , quantiser_(retrieveQuantiser (gridID)) { } - /** */ + /** + * build a quantised time value by explicitly specifying a + * grid alignment facility and without any hidden reference + * to the Lumiera session. This is mainly intended for + * debugging and unit testing. + */ QuTime::QuTime (TimeValue raw, PQuant quantisation_to_use) : Time(raw) , quantiser_(quantisation_to_use) diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index c45489032..6aaff8e69 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -106,13 +106,13 @@ namespace time { /** - * Simple stand-alone Quantiser implementation for debugging and test. - * This is a self-contained quantiser implementation without any implicit - * referral to the Lumiera session. It is mainly intended for simplified unit testing. - * @warning real GUI and Proc-Layer code should always prefer to build a real quantiser, - * which referres some TimeGrid definition within the session. Basically, the overall - * purpose of the time-quantisation framework is to enforce such a link to a specific - * time and quantisation scale and to prevent "wild and uncoordinated" rounding attempts. + * Simple stand-alone Quantiser implementation based on a constant sized gird. + * This is a self-contained quantiser implementation without any implicit referral + * to the Lumiera session. As such it is suited for simplified unit testing. + * @warning real GUI and Proc-Layer code should always fetch a quantiser from the + * Session, referring to a pre defined TimeGrid. Basically, the overall purpose of + * the time-quantisation framework is to enforce such a link to a distinct time scale + * and quantisation, so to prevent "wild and uncoordinated" rounding attempts. */ class FixedFrameQuantiser : public Quantiser diff --git a/src/lib/time/timequant.hpp b/src/lib/time/timequant.hpp index 3b25592c3..0580d2e6f 100644 --- a/src/lib/time/timequant.hpp +++ b/src/lib/time/timequant.hpp @@ -40,9 +40,19 @@ namespace time { /** - * fixed format time specification. + * grid aligned time specification, referring to a specific scale. + * A quantised time value allows to access the time specification + * as numeric value in one of the supported timecode formats, and + * relative to the defined time scale. Usually this time scale + * exists already in the Lumiera session and is referred simply + * by symbolic ID, it will be fetched on demand through the + * \link advice.hpp advice system.\endlink * - * @todo WIP-WIP-WIP + * By creating a QuTime value, the relation to such a predefined + * time scale is made explicit. This doesn't change the internal + * time value, but the actual creation of a timecode formatted + * value (#formatAs) usually implies to quantise or grid align + * the time to the frame grid specific to this time scale. */ class QuTime : public Time diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index 3fae0c0f9..c2f0bfbf5 100644 --- a/tests/lib/time/time-quantisation-test.cpp +++ b/tests/lib/time/time-quantisation-test.cpp @@ -140,13 +140,12 @@ namespace test{ void checkGridLateBinding (TimeValue org) { - QuTime funny (org, "special_funny_grid"); // refer a not yet existing grid - CHECK (org == funny); // no problem, unless we request quantisation - - VERIFY_ERROR (UNKNOWN_GRID, funny.formatAs() ); + // refer to a grid not yet defined + VERIFY_ERROR (UNKNOWN_GRID, QuTime wired(org, "special_funny_grid")); TimeGrid::build("special_funny_grid", 1); // provide the grid's definition (1 frame per second) + QuTime funny (org, "special_funny_grid"); // now OK, grid is known int cnt = funny.formatAs(); // and now performing quantisation is OK SmpteTC smpte (funny); // also converting into SMPTE (which implies frame quantisation) From 02653621f6e19b4db41ca38be4ad9dd03f7450eb Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 17 Jan 2011 06:38:10 +0100 Subject: [PATCH 077/140] adapt the TimeGrid meta asset, so it can be published as Quantiser --- src/lib/time/quantiser.hpp | 3 +- src/proc/asset/meta/time-grid.cpp | 63 +++++++++++++++++++++---------- src/proc/asset/meta/time-grid.hpp | 20 +++++----- tests/41asset.tests | 2 +- 4 files changed, 54 insertions(+), 34 deletions(-) diff --git a/src/lib/time/quantiser.hpp b/src/lib/time/quantiser.hpp index 6aaff8e69..369c48055 100644 --- a/src/lib/time/quantiser.hpp +++ b/src/lib/time/quantiser.hpp @@ -73,10 +73,9 @@ namespace time { * the Lumiera session. Time quantisation and timecode handling explicitly * relies on this Quantiser interface. * - * @todo WIP-WIP-WIP */ class Quantiser - : public Grid + : public virtual Grid { protected: format::Supported supportedFormats_; diff --git a/src/proc/asset/meta/time-grid.cpp b/src/proc/asset/meta/time-grid.cpp index 65f1978c8..ad3599a80 100644 --- a/src/proc/asset/meta/time-grid.cpp +++ b/src/proc/asset/meta/time-grid.cpp @@ -27,12 +27,14 @@ #include "lib/time/quantiser.hpp" #include "lib/time/timevalue.hpp" #include "lib/time/display.hpp" +#include "lib/advice.hpp" #include "lib/util.hpp" //#include "include/logging.h" #include #include +using util::cStr; using util::isnil; using boost::format; using boost::str; @@ -44,10 +46,6 @@ namespace meta { namespace error = lumiera::error; - namespace { - - // Implementation details (still required???) - } /** */ @@ -63,32 +61,58 @@ namespace meta { using lib::time::Offset; using lib::time::FSecs; + using lib::time::PQuant; + using lib::time::Quantiser; + using lib::time::FixedFrameQuantiser; + using std::tr1::dynamic_pointer_cast; + + namespace advice = lib::advice; + + namespace { + + /** @internal helper to retrieve the smart-ptr + * from the AssetManager, then attach a further + * smart-ptr-to-Quantiser to that, which then can be + * published via the \link advice.hpp "advice system"\endlink + */ + inline PGrid + publishWrapped (TimeGrid& newGrid) + { + PGrid gridImplementation = AssetManager::instance().wrap (newGrid); + PQuant quantiser (dynamic_pointer_cast(gridImplementation)); + Literal bindingID (cStr(newGrid.ident.name)); + + advice::Provision(bindingID).setAdvice(quantiser); + return gridImplementation; + } + + + } + /** * TimeGrid implementation: a trivial time grid, * starting at a given point in time and using a - * constant grid spacing + * constant grid spacing. + * + * @note The actual implementation is mixed in, + * together with the Quantiser API; the intended use + * of this implementation is to publish it via the advice + * framework, when building and registering the meta asset. */ class SimpleTimeGrid : public TimeGrid + , public FixedFrameQuantiser { - lib::time::FixedFrameQuantiser frameGrid_; - - /* == grid API forwarded to embedded quantiser == */ - long gridPoint (TimeValue const& rawTime) const { return frameGrid_.gridPoint (rawTime); } - TimeValue gridAlign (TimeValue const& rawTime) const { return frameGrid_.gridAlign (rawTime); } - TimeValue timeOf (long gridPoint) const { return frameGrid_.timeOf (gridPoint); } - TimeValue timeOf (FSecs gridTime, int gridOffset =0) const { return frameGrid_.timeOf (gridTime,gridOffset); } - public: SimpleTimeGrid (Time start, Duration frameDuration, EntryID const& name) : TimeGrid (name) - , frameGrid_(frameDuration,start) + , FixedFrameQuantiser(frameDuration,start) { } SimpleTimeGrid (Time start, FrameRate frames_per_second, EntryID const& name) : TimeGrid (name) - , frameGrid_(frames_per_second,start) + , FixedFrameQuantiser(frames_per_second,start) { } }; @@ -104,7 +128,7 @@ namespace meta { * might raise further exception when asset registration fails. * @todo currently (12/2010) the AssetManager is unable to detect duplicate assets. * Later on the intention is that in such cases, instead of creating a new grid - * we'll silently return the already registered exisiting and equivalent grid. + * we'll silently return the already registered existing and equivalent grid. */ P Builder::commit() @@ -116,13 +140,12 @@ namespace meta { if (isnil (id_)) { - format gridIdFormat("grid_%f_%s"); - id_ = str(gridIdFormat % fps_ % origin_); + format gridIdFormat("grid(%f_%d)"); + id_ = str(gridIdFormat % fps_ % _raw(origin_)); } EntryID nameID (id_); - TimeGrid& newGrid (*new SimpleTimeGrid(origin_, fps_, nameID)); - return AssetManager::instance().wrap (newGrid); + return publishWrapped (*new SimpleTimeGrid(origin_, fps_, nameID)); } diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index 01b96f480..b8e768326 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -69,23 +69,21 @@ namespace meta { /** - * Interface: a grid and scale for time quantisation. - * This meta-Asset describes a coordinate system or - * reference scale for quantised time values. + * Interface: a grid and scale definition for time quantisation. + * This meta-Asset describes a coordinate system or reference scale + * for quantised time values. Especially it allows to define an actual + * implementation, which can then implicitly be used by lib::time::QuTime + * and for conversions into timecode. + * @note for this to work, the actual implementation classes returned + * by the builder or the static #build function additionally expose + * an implementation of the lib::time::Quantiser API */ class TimeGrid : public Meta - , public lib::time::Grid + , public virtual lib::time::Grid { public: - //--------Grid-API------------------------------------ - long gridPoint (TimeValue const& raw) const =0; - TimeValue gridAlign (TimeValue const& raw) const =0; - TimeValue timeOf (long gridPoint) const =0; - TimeValue timeOf (FSecs, int =0) const =0; - - /* === shortcut builder functions === */ static PGrid build (Symbol gridID, FrameRate frames_per_second); static PGrid build (Symbol gridID, FrameRate frames_per_second, Time origin); diff --git a/tests/41asset.tests b/tests/41asset.tests index a117cd120..bd2cb93a2 100644 --- a/tests/41asset.tests +++ b/tests/41asset.tests @@ -71,6 +71,6 @@ END TEST "Simple TimeGrid" TimeGridBasics_test < Date: Mon, 17 Jan 2011 07:25:22 +0100 Subject: [PATCH 078/140] get simple quantisation/timecode integration to run passing the basic tests now; still missing: implementation of specific timecodes, e.g. SMPTE, HMS,.... --- src/lib/time/digxel.hpp | 2 +- src/lib/time/timecode.hpp | 2 +- tests/40components.tests | 1 + tests/lib/time/digxel-configurations-test.cpp | 1 + tests/lib/time/time-formats-test.cpp | 46 +++++++++++++++---- tests/lib/time/time-quantisation-test.cpp | 33 +++++++++---- 6 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 6ade5c742..7256c0061 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -172,7 +172,7 @@ namespace time { struct CountFormatter : PrintfFormatter { - CountFormatter() : PrintfFormatter("%04l") { } + CountFormatter() : PrintfFormatter("%04ld") { } }; } //(End) digxel configuration namespace diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 978b3c503..704db4293 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -86,7 +86,7 @@ namespace time { , public CountVal { - string show() const { return string(show())+"fr"; } + string show() const { return string(CountVal::show())+"fr"; } Literal tcID() const { return "Frame-count"; } TimeValue value() const { return Format::evaluate (*this, *quantiser_); } diff --git a/tests/40components.tests b/tests/40components.tests index 490730d84..3b451e8a2 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -247,6 +247,7 @@ out: .+Digxel.+SexaFormatter.+--empty--00--\(val=42\)--42 out: .+Digxel.+SexaFormatter.+--empty--00--\(val=-5\)---5 out: .+Digxel.+HexaFormatter.+--empty--00--\(val=12\)--0C out: .+Digxel.+HexaFormatter.+--empty--00--\(val=111\)--6F +out: .+Digxel.+CountFormatter.+--empty--0000--\(val=-1234567890\)---1234567890 return: 0 END diff --git a/tests/lib/time/digxel-configurations-test.cpp b/tests/lib/time/digxel-configurations-test.cpp index 185fd06a7..6a226eb23 100644 --- a/tests/lib/time/digxel-configurations-test.cpp +++ b/tests/lib/time/digxel-configurations-test.cpp @@ -68,6 +68,7 @@ namespace test{ verifyConfiguration (-5); verifyConfiguration (0xc); verifyConfiguration (0x6f); + verifyConfiguration (-1234567890); } diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index d11e5c3c2..e22c8ce1b 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -22,6 +22,7 @@ #include "lib/test/run.hpp" +//#include "lib/test/test-helper.hpp" #include "lib/time/timecode.hpp" #include "lib/util.hpp" @@ -43,9 +44,10 @@ namespace test{ /******************************************************** * @test verify handling of grid aligned timecode values. - * - creating timecode values - * - some conversions - * - formatting + * - full cycle from parsing to formatting + * - mutating the components of timecode + * - some formatting corner cases + * - formatting in various formats */ class TimeFormats_test : public Test { @@ -56,29 +58,55 @@ namespace test{ TimeValue ref (refval); - checkBasics (ref); - checkComparisons (ref); - checkComponentAccess(); + checkTimecodeUsageCycle (ref); + checkFrames (); + checkSeconds (); + checkHms (); + checkSmpte(); + checkDropFrame(); } void - checkBasics (TimeValue ref) + checkTimecodeUsageCycle (TimeValue ref) { + UNIMPLEMENTED ("full usage cycle for a timecode value"); } void - checkComparisons (TimeValue ref) + checkFrames () { + UNIMPLEMENTED ("verify frame count time format"); } void - checkComponentAccess() + checkSeconds () { + UNIMPLEMENTED ("verify seconds as timecode format"); } + + void + checkHms () + { + UNIMPLEMENTED ("verify hour-minutes-seconds-millis timecode"); + } + + + void + checkSmpte () + { + UNIMPLEMENTED ("verify SMPTE timecode format"); + } + + + void + checkDropFrame () + { + UNIMPLEMENTED ("verify especially SMPTE-drop-frame timecode"); + } }; diff --git a/tests/lib/time/time-quantisation-test.cpp b/tests/lib/time/time-quantisation-test.cpp index c2f0bfbf5..c1a1a2812 100644 --- a/tests/lib/time/time-quantisation-test.cpp +++ b/tests/lib/time/time-quantisation-test.cpp @@ -31,12 +31,12 @@ #include #include #include -//#include +#include using boost::lexical_cast; using util::isnil; using util::contains; -//using std::rand; +using std::rand; using std::cout; using std::endl; @@ -52,23 +52,36 @@ namespace test{ /******************************************************** * @test verify handling of quantised time values. - * - creating times and time intervals - * - comparisons - * - time arithmetics + * - the simple usage, just referring to an + * predefined grid by name + * - explicitly defining an quantiser + * - converting these quantised values into + * various timecode formats + * - error detection */ class TimeQuantisation_test : public Test { + int + random_or_get (Arg arg) + { + if (isnil(arg)) + return 1 + (rand() % 10000); + else + return lexical_cast (arg[1]); + } + + + virtual void run (Arg arg) { - long refval= isnil(arg)? 1 : lexical_cast (arg[1]); - - TimeValue ref (refval); + Time ref (random_or_get(arg)); CHECK (Time(0) < ref); checkSimpleUsage (ref); check_theFullStory (ref); checkMultipleGrids (ref); + checkGridBinding (ref); } @@ -133,12 +146,12 @@ namespace test{ FrameNr palNr (palVal); FrameNr ntscNr(ntscVal); - CHECK (palNr < ntscNr); + CHECK (palNr <= ntscNr); } void - checkGridLateBinding (TimeValue org) + checkGridBinding (TimeValue org) { // refer to a grid not yet defined VERIFY_ERROR (UNKNOWN_GRID, QuTime wired(org, "special_funny_grid")); From ce420a1570c61e75131967787e2c350295c05d8a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 18 Jan 2011 04:59:40 +0100 Subject: [PATCH 079/140] special digxel to represent the sign --- src/lib/time/digxel.hpp | 28 ++++++++++++++++++- tests/lib/time/digxel-configurations-test.cpp | 22 ++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 7256c0061..fddae6168 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -175,6 +175,14 @@ namespace time { CountFormatter() : PrintfFormatter("%04ld") { } }; + + struct SignFormatter + { + void clear() { } + size_t maxlen() const { return 1; } + CBuf show (int val) { return val<0? "-":" "; } + }; + } //(End) digxel configuration namespace @@ -273,7 +281,25 @@ namespace time { /* == predefined Digxel configurations == */ typedef Digxel< int, digxel::SexaFormatter> SexaDigit; ///< for displaying time components (sexagesimal) typedef Digxel HexaDigit; ///< for displaying a hex byte - typedef Digxel CountVal; ///< for displaying a hex byte + typedef Digxel CountVal; ///< for displaying a counter + + + /** special Digxel to show a sign. + * @note values limited to +1 and -1 */ + struct Signum + : Digxel + { + Signum() { setValueRaw(1); } + + void + operator= (int n) + { + int newSign = 0 > mutator(n)? -1:+1; + this->setValueRaw (newSign); + } + + friend int operator*= (Signum s, int c) { s = c*s; return s; } + }; }} // lib::time diff --git a/tests/lib/time/digxel-configurations-test.cpp b/tests/lib/time/digxel-configurations-test.cpp index 6a226eb23..607be80b1 100644 --- a/tests/lib/time/digxel-configurations-test.cpp +++ b/tests/lib/time/digxel-configurations-test.cpp @@ -69,7 +69,27 @@ namespace test{ verifyConfiguration (0xc); verifyConfiguration (0x6f); verifyConfiguration (-1234567890); - } + + verifySignum(); + } + + + void + verifySignum() + { + Signum sig; + CHECK (1 == sig); + + sig = 123; + CHECK (1 == sig); + sig = -sig; + CHECK (-1 == sig); + sig = -98; + CHECK (-1 == sig); + CHECK (sig.show() == string("-")); + sig *= -1; + CHECK (sig.show() == string("-")); + } template From 95a1687a5b34b66f4367ad90fd4b1a887cb2e019 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 18 Jan 2011 05:01:25 +0100 Subject: [PATCH 080/140] draft how a SMPTE-Timecode element could be implemented passes Compiler, but thats about all... --- src/lib/time/digxel.hpp | 12 +++- src/lib/time/formats.hpp | 6 +- src/lib/time/timecode.cpp | 63 +++++++++++++------ src/lib/time/timecode.hpp | 24 +++++--- tests/lib/time/time-formats-test.cpp | 74 ++++++++++++++++++----- tests/lib/time/time-quantisation-test.cpp | 4 +- 6 files changed, 136 insertions(+), 47 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index fddae6168..1e4aa8f95 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -144,9 +144,9 @@ namespace time { template<> struct Formatter - : PrintfFormatter + : PrintfFormatter { - Formatter() : PrintfFormatter("%3d") { } + Formatter() : PrintfFormatter("%3d") { } }; template<> @@ -271,6 +271,14 @@ namespace time { + //---Supporting-increments-------------- + Digxel& operator+= (NUM inc) { value_ += inc; return *this; } + Digxel& operator-= (NUM dec) { value_ -= dec; return *this; } + Digxel& operator++ () { value_ += 1; return *this; } + Digxel& operator-- () { value_ -= 1; return *this; } + NUM operator++ (int) { NUM p(value_++); return p; } + NUM operator-- (int) { NUM p(value_--); return p; } + //---Supporting-totally_ordered--------- bool operator< (Digxel const& o) const { return value_ < NUM(o); } bool operator== (Digxel const& o) const { return value_ == NUM(o); } diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index a5cccc321..df7b83db2 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -81,7 +81,7 @@ namespace time { struct Smpte : NoInstance { - static void rebuild (SmpteTC&, QuantR); + static void rebuild (SmpteTC&, QuantR, TimeValue const&); static TimeValue evaluate (SmpteTC const&, QuantR); }; @@ -96,7 +96,7 @@ namespace time { struct Hms : NoInstance { - static void rebuild (HmsTC&, QuantR); + static void rebuild (HmsTC&, QuantR, TimeValue const&); static TimeValue evaluate (HmsTC const&, QuantR); }; @@ -113,7 +113,7 @@ namespace time { struct Seconds : NoInstance { - static void rebuild (Secs&, QuantR); + static void rebuild (Secs&, QuantR, TimeValue const&); static TimeValue evaluate (Secs const&, QuantR); }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 4af2fec19..1f3201cf0 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -27,8 +27,10 @@ #include "lib/time/timequant.hpp" #include "lib/time/formats.hpp" #include "lib/time.h" +#include "lib/util.hpp" +using util::unConst; using std::string; @@ -56,6 +58,25 @@ namespace time { return quantiser.timeOf (framecnt); } + + /** build up a frame count + * by quantising the given time value + */ + void + Smpte::rebuild (SmpteTC& tc, QuantR quantiser, TimeValue const& rawTime) + { +// framecnt.setValueRaw(quantiser.gridPoint (rawTime)); + UNIMPLEMENTED("build smpte components from raw time"); + } + + /** calculate the time point denoted by this frame count */ + TimeValue + Smpte::evaluate (SmpteTC const& tc, QuantR quantiser) + { +// return quantiser.timeOf (framecnt); + UNIMPLEMENTED("calculate time from smpte"); + } + } @@ -72,7 +93,6 @@ namespace time { /** */ SmpteTC::SmpteTC (QuTime const& quantisedTime) : TCode(quantisedTime) -// : tpoint_(quantisedTime) /////////////////////////////TODO eternal bullshit { } @@ -91,32 +111,41 @@ namespace time { - /** */ - int - SmpteTC::getSecs() const + void + SmpteTC::rebuild() const { - return lumiera_time_seconds (tpoint_); + TimeValue point = Format::evaluate(*this, *quantiser_); + Format::rebuild(unConst(*this), *quantiser_, point); } - /** */ - int - SmpteTC::getMins() const + + string + SmpteTC::show() const { - return lumiera_time_minutes (tpoint_); + rebuild(); + string tc; + tc.reserve(15); + tc += sgn.show(); + tc += hours.show(); + tc += ':'; + tc += mins.show(); + tc += ':'; + tc += secs.show(); + tc += ':'; + tc += frames.show(); + return tc; } - /** */ - int - SmpteTC::getHours() const + SmpteTC& + SmpteTC::operator++ () { - return lumiera_time_hours (tpoint_); + UNIMPLEMENTED ("SMPTE unit increment"); } - /** */ - int - SmpteTC::getFrames() const + SmpteTC& + SmpteTC::operator-- () { - UNIMPLEMENTED ("Frame-Quantisation"); + UNIMPLEMENTED ("SMPTE unit decrement"); } /** */ diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 704db4293..e633ccf58 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -30,7 +30,7 @@ #include "lib/symbol.hpp" //#include -//#include +#include #include ///////////////TODO #include @@ -105,22 +105,28 @@ namespace time { */ class SmpteTC : public TCode + , boost::unit_steppable { - TimeVar tpoint_; - - virtual string show() const { return string(tpoint_); } + + virtual string show() const ; virtual Literal tcID() const { return "SMPTE"; } - virtual TimeValue value() const { return tpoint_; } + virtual TimeValue value() const { return Format::evaluate (*this, *quantiser_); } public: typedef format::Smpte Format; SmpteTC (QuTime const& quantisedTime); - int getSecs () const; - int getMins () const; - int getHours () const; - int getFrames () const; + void rebuild() const; + + Digxel hours; + SexaDigit mins; + SexaDigit secs; + SexaDigit frames; + Signum sgn; + + SmpteTC& operator++(); + SmpteTC& operator--(); }; diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index e22c8ce1b..2fa68ec18 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -23,24 +23,28 @@ #include "lib/test/run.hpp" //#include "lib/test/test-helper.hpp" +#include "proc/asset/meta/time-grid.hpp" +#include "lib/time/timequant.hpp" #include "lib/time/timecode.hpp" +#include "lib/time/display.hpp" #include "lib/util.hpp" #include -//#include +#include //#include using boost::lexical_cast; using util::isnil; //using std::rand; -//using std::cout; -//using std::endl; +using std::cout; +using std::endl; namespace lib { namespace time{ namespace test{ + using asset::meta::TimeGrid; /******************************************************** * @test verify handling of grid aligned timecode values. @@ -52,28 +56,33 @@ namespace test{ class TimeFormats_test : public Test { virtual void - run (Arg arg) + run (Arg) { - long refval= isnil(arg)? 1 : lexical_cast (arg[1]); + TimeGrid::build("pal0", FrameRate::PAL); - TimeValue ref (refval); - - checkTimecodeUsageCycle (ref); - checkFrames (); - checkSeconds (); - checkHms (); +// checkTimecodeUsageCycle (); +// checkFrames (); +// checkSeconds (); +// checkHms (); checkSmpte(); - checkDropFrame(); +// checkDropFrame(); } void - checkTimecodeUsageCycle (TimeValue ref) + checkTimecodeUsageCycle () { UNIMPLEMENTED ("full usage cycle for a timecode value"); } + template + void + showTimeCode (TC timecode) + { + cout << timecode.describe()<<"=\""<(); // and now performing quantisation is OK SmpteTC smpte (funny); // also converting into SMPTE (which implies frame quantisation) - CHECK (0 == smpte.getFrames()); // we have 1fps, thus the frame part is always zero! - CHECK (cnt % 60 == smpte.getSecs()); // and the seconds part will be in sync with the frame count + CHECK (0 == smpte.frames); // we have 1fps, thus the frame part is always zero! + CHECK (cnt % 60 == smpte.secs); // and the seconds part will be in sync with the frame count } }; From d2702e82540e747aa72118d355b1c4ee24339a64 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Fri, 10 Dec 2010 12:53:39 +0100 Subject: [PATCH 081/140] Add frame counting capabilities to time conversion lib. --- src/lib/time.c | 13 +++++++++++++ src/lib/time.h | 14 ++++++++++++++ tests/library/test-time.c | 9 +++++++++ 3 files changed, 36 insertions(+) diff --git a/src/lib/time.c b/src/lib/time.c index 5af40d640..3748d7e8d 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -91,3 +91,16 @@ lumiera_time_millis(gavl_time_t time) { return (time / GAVL_TIME_SCALE_MS) % 1000; } + +int +lumiera_time_frames(gavl_time_t time, float fps) +{ + return (fps * (lumiera_time_millis(time))) / 1000; +} + +int +lumiera_time_frame_count(gavl_time_t time, float fps) +{ + int ms = (time / GAVL_TIME_SCALE_MS); + return fps * ms / 1000; +} diff --git a/src/lib/time.h b/src/lib/time.h index f812c1b9d..4a3812238 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -60,5 +60,19 @@ int lumiera_time_seconds(gavl_time_t time); */ int lumiera_time_millis(gavl_time_t time); +/** + * Get the frame part of given time, using the given number of frames. + * @param gavl_time_t the time we are interested in converting + * @param fps Frame rate (float for now, but should be a rational) + */ +int lumiera_time_frames(gavl_time_t time, float fps); + +/** + * Get the frame count for the given time. + * @param gavl_time_t the time we are interested in converting + * @param fps Frame rate (float for now, but should be a rational) + */ +int lumiera_time_frame_count(gavl_time_t time, float fps); + #endif diff --git a/tests/library/test-time.c b/tests/library/test-time.c index 4ce8b63bb..fc0f4f343 100644 --- a/tests/library/test-time.c +++ b/tests/library/test-time.c @@ -32,6 +32,7 @@ const int MILLIS = 700; const int SECONDS = 20; const int MINUTES = 55; const int HOURS = 3; +const float FPS = 24.0; /* * 1. Basic functionality @@ -46,6 +47,10 @@ TEST (basic) { CHECK (lumiera_time_seconds(t) == 0); CHECK (lumiera_time_minutes(t) == 0); CHECK (lumiera_time_hours(t) == 0); + CHECK (lumiera_time_frames(t, FPS) == 0); + CHECK (lumiera_time_frames(t, FPS+5) == 0); + CHECK (lumiera_time_frame_count(t, FPS) == 0); + CHECK (lumiera_time_frame_count(t, FPS+5) == 0); ECHO ("%s", lumiera_tmpbuf_print_time(t)); @@ -56,6 +61,10 @@ TEST (basic) { CHECK (lumiera_time_seconds(t) == SECONDS); CHECK (lumiera_time_minutes(t) == MINUTES); CHECK (lumiera_time_hours(t) == HOURS); + CHECK (lumiera_time_frames(t, FPS) == (int)((FPS * MILLIS) / 1000)); + CHECK (lumiera_time_frames(t, FPS+5) == (int)(((FPS+5) * MILLIS) / 1000)); + CHECK (lumiera_time_frame_count(t, FPS) == 338896); + CHECK (lumiera_time_frame_count(t, FPS+5) == 409500); ECHO ("%s", lumiera_tmpbuf_print_time(t)); } From 94f8379aa2c4984969a3fe5343c40fd1a1e9b042 Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Mon, 13 Dec 2010 18:16:15 +0100 Subject: [PATCH 082/140] Improved frame counting capabilities for time lib. Unit tests. --- src/lib/time.c | 39 ++++++++++++++++++------ src/lib/time.h | 52 ++++++++++++++++++++++--------- tests/15time.tests | 3 ++ tests/library/test-time.c | 64 +++++++++++++++++++++++++-------------- 4 files changed, 113 insertions(+), 45 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index 3748d7e8d..337456db3 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -22,6 +22,7 @@ #include #include "lib/time.h" #include "lib/tmpbuf.h" +#include /* GAVL_TIME_SCALE is the correct factor or dividend when using gavl_time_t for * units of whole seconds from gavl_time_t. Since we want to use milliseconds, @@ -58,8 +59,12 @@ lumiera_tmpbuf_print_time (gavl_time_t time) } gavl_time_t -lumiera_build_time(long millis, uint secs, uint mins, uint hours) +lumiera_build_time (long millis, uint secs, uint mins, uint hours) { + REQUIRE (millis >= 0 && millis <= 999); + REQUIRE (mins < 60); + REQUIRE (secs < 60); + gavl_time_t time = millis + 1000 * secs + 1000 * 60 * mins @@ -68,39 +73,55 @@ lumiera_build_time(long millis, uint secs, uint mins, uint hours) return time; } +gavl_time_t +lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours) +{ + REQUIRE (mins < 60); + REQUIRE (secs < 60); + REQUIRE (frames < fps); + + gavl_time_t time = frames * (1000.0 / fps) + + 1000 * secs + + 1000 * 60 * mins + + 1000 * 60 * 60 * hours; + time *= GAVL_TIME_SCALE_MS; + return time; +} + int -lumiera_time_hours(gavl_time_t time) +lumiera_time_hours (gavl_time_t time) { return time / GAVL_TIME_SCALE_MS / 1000 / 60 / 60; } int -lumiera_time_minutes(gavl_time_t time) +lumiera_time_minutes (gavl_time_t time) { return (time / GAVL_TIME_SCALE_MS / 1000 / 60) % 60; } int -lumiera_time_seconds(gavl_time_t time) +lumiera_time_seconds (gavl_time_t time) { return (time / GAVL_TIME_SCALE_MS / 1000) % 60; } int -lumiera_time_millis(gavl_time_t time) +lumiera_time_millis (gavl_time_t time) { return (time / GAVL_TIME_SCALE_MS) % 1000; } int -lumiera_time_frames(gavl_time_t time, float fps) +lumiera_time_frames (gavl_time_t time, float fps) { return (fps * (lumiera_time_millis(time))) / 1000; } int -lumiera_time_frame_count(gavl_time_t time, float fps) +lumiera_time_frame_count (gavl_time_t time, float fps) { - int ms = (time / GAVL_TIME_SCALE_MS); - return fps * ms / 1000; + REQUIRE (fps > 0); + + return roundf((time / GAVL_TIME_SCALE_MS / 1000.0f) * fps); } diff --git a/src/lib/time.h b/src/lib/time.h index 4a3812238..df7b1dfb0 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -36,43 +36,67 @@ lumiera_tmpbuf_print_time (gavl_time_t time); /** * Builds a time value by summing up the given components. + * @param millis number of milliseconds + * @param secs number of seconds + * @param mins number of minutes + * @param hours number of hours */ gavl_time_t lumiera_build_time (long millis, uint secs, uint mins, uint hours); /** - * Get the hour part of given time. + * Builds a time value by summing up the given components. + * @param fps the frame rate to be used + * @param frames number of frames + * @param secs number of seconds + * @param mins number of minutes + * @param hours number of hours */ -int lumiera_time_hours(gavl_time_t time); +gavl_time_t +lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours); + +/** + * Get the hour part of given time. + * @param time the time value to use + */ +int +lumiera_time_hours (gavl_time_t time); /** * Get the minute part of given time. + * @param time the time value to use */ -int lumiera_time_minutes(gavl_time_t time); +int +lumiera_time_minutes (gavl_time_t time); /** * Get the seconds part of given time. + * @param time the time value to use */ -int lumiera_time_seconds(gavl_time_t time); +int +lumiera_time_seconds (gavl_time_t time); /** * Get the milliseconds part of given time. + * @param time the time value to use */ -int lumiera_time_millis(gavl_time_t time); +int +lumiera_time_millis (gavl_time_t time); /** - * Get the frame part of given time, using the given number of frames. - * @param gavl_time_t the time we are interested in converting - * @param fps Frame rate (float for now, but should be a rational) + * Get the frame part of given time, using the given fps. + * @param time the time value to use + * @param fps frame rate (float for now, but should be a rational) */ -int lumiera_time_frames(gavl_time_t time, float fps); +int +lumiera_time_frames (gavl_time_t time, float fps); /** - * Get the frame count for the given time. - * @param gavl_time_t the time we are interested in converting - * @param fps Frame rate (float for now, but should be a rational) + * Get the frame count for the given time, using the given fps. + * @param time the time value to use + * @param fps frame rate (float for now, but should be a rational) */ -int lumiera_time_frame_count(gavl_time_t time, float fps); +int +lumiera_time_frame_count (gavl_time_t time, float fps); #endif - diff --git a/tests/15time.tests b/tests/15time.tests index 0cb57ddcc..30ae5515c 100644 --- a/tests/15time.tests +++ b/tests/15time.tests @@ -4,3 +4,6 @@ TEST "basic functionality" basic < Date: Mon, 13 Dec 2010 18:22:12 +0100 Subject: [PATCH 083/140] Add support for NTSC drop-frame timecode. --- src/lib/time.c | 67 ++++++++++++++++++++++++++++++-- src/lib/time.h | 42 ++++++++++++++++++++ tests/15time.tests | 3 ++ tests/library/test-time.c | 81 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 4 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index 337456db3..43ed80d34 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -34,14 +34,14 @@ lumiera_tmpbuf_print_time (gavl_time_t time) { int milliseconds, seconds, minutes, hours; int negative; - + if(time < 0) { negative = 1; time = -time; } else negative = 0; - + time /= GAVL_TIME_SCALE_MS; milliseconds = time % 1000; time /= 1000; @@ -50,10 +50,10 @@ lumiera_tmpbuf_print_time (gavl_time_t time) minutes = time % 60; time /= 60; hours = time; - + char *buffer = lumiera_tmpbuf_snprintf(64, "%s%02d:%02d:%02d.%03d", negative ? "-" : "", hours, minutes, seconds, milliseconds); - + ENSURE(buffer != NULL); return buffer; } @@ -88,6 +88,24 @@ lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours return time; } +gavl_time_t +lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours) +{ + REQUIRE (mins < 60); + REQUIRE (secs < 60); + REQUIRE (frames < 30); + REQUIRE_IF (secs == 0 && mins % 10, frames >= 2, "non-existent frame in NTSC drop-frame"); + + int total_mins = 60 * hours + mins; + int total_frames = 108000 * hours + + 1800 * mins + + 30 * secs + + frames + - 2 * (total_mins - total_mins / 10); + + return (total_frames / 29.97f) * 1000 * GAVL_TIME_SCALE_MS; +} + int lumiera_time_hours (gavl_time_t time) { @@ -125,3 +143,44 @@ lumiera_time_frame_count (gavl_time_t time, float fps) return roundf((time / GAVL_TIME_SCALE_MS / 1000.0f) * fps); } + +/** + * This function is used in the NTSC drop-frame functions to avoid code + * repetition. + * @return the frame number for given time + */ +static int +ntsc_drop_get_frame_number (gavl_time_t time) +{ + int frame = lumiera_time_frame_count (time, NTSC_DROP_FRAME_FPS); + + int d = frame / 17982; // 17982 = 600 * 29.97 + int m = frame % 17982; // 17982 = 600 * 29.97 + + return frame + 18*d + 2*((m - 2) / 1798); // 1798 = 60 * 29.97 +} + +int +lumiera_time_ntsc_drop_frames (gavl_time_t time) +{ + return ntsc_drop_get_frame_number(time) % 30; +} + +int +lumiera_time_ntsc_drop_seconds (gavl_time_t time) +{ + return ntsc_drop_get_frame_number(time) / 30 % 60; +} + +int +lumiera_time_ntsc_drop_minutes (gavl_time_t time) +{ + return ntsc_drop_get_frame_number(time) / 30 / 60 % 60; +} + +int +lumiera_time_ntsc_drop_hours (gavl_time_t time) +{ + return ntsc_drop_get_frame_number(time) / 30 / 60 / 60 % 24; +} + diff --git a/src/lib/time.h b/src/lib/time.h index df7b1dfb0..2b3e9523d 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -25,6 +25,8 @@ #include #include +#define NTSC_DROP_FRAME_FPS 29.97 + /** * Formats a time in a safeclib tmpbuf in HH:MM:SS:mmm format. * @param size maximal length for the string @@ -55,6 +57,18 @@ lumiera_build_time (long millis, uint secs, uint mins, uint hours); gavl_time_t lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours); +/** + * Builds a time value by summing up the given components. + * The components are interpreted as a NTSC drop-frame timecode. + * @param frames number of frames + * @param secs number of seconds + * @param mins number of minutes + * @param hours number of hours + * You must take care not to specify time codes that are illegal NTSC drop-frame times. + */ +gavl_time_t +lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours); + /** * Get the hour part of given time. * @param time the time value to use @@ -99,4 +113,32 @@ lumiera_time_frames (gavl_time_t time, float fps); int lumiera_time_frame_count (gavl_time_t time, float fps); +/** + * Get the frame part of given time, using NTSC drop-frame timecode. + * @param time the time value to use + */ +int +lumiera_time_ntsc_drop_frames (gavl_time_t time); + +/** + * Get the second part of given time, using NTSC drop-frame timecode. + * @param time the time value to use + */ +int +lumiera_time_ntsc_drop_seconds (gavl_time_t time); + +/** + * Get the minute part of given time, using NTSC drop-frame timecode. + * @param time the time value to use + */ +int +lumiera_time_ntsc_drop_minutes (gavl_time_t time); + +/** + * Get the hour part of given time, using NTSC drop-frame timecode. + * @param time the time value to use + */ +int +lumiera_time_ntsc_drop_hours (gavl_time_t time); + #endif diff --git a/tests/15time.tests b/tests/15time.tests index 30ae5515c..2dcedbea9 100644 --- a/tests/15time.tests +++ b/tests/15time.tests @@ -7,3 +7,6 @@ END TEST "frame rate dependent calculations" fps < Date: Tue, 14 Dec 2010 14:22:33 +0100 Subject: [PATCH 084/140] Remove preconditions requiring correct formatting of input times in time lib. For example, one could not specify minutes > 59 nor milliseconds > 999. Other parts of the code however relied on the fact that one could give arbitrary times in all fields (hours, milliseconds and minutes). --- src/lib/time.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/lib/time.c b/src/lib/time.c index 43ed80d34..6e486262e 100644 --- a/src/lib/time.c +++ b/src/lib/time.c @@ -61,10 +61,6 @@ lumiera_tmpbuf_print_time (gavl_time_t time) gavl_time_t lumiera_build_time (long millis, uint secs, uint mins, uint hours) { - REQUIRE (millis >= 0 && millis <= 999); - REQUIRE (mins < 60); - REQUIRE (secs < 60); - gavl_time_t time = millis + 1000 * secs + 1000 * 60 * mins @@ -76,10 +72,6 @@ lumiera_build_time (long millis, uint secs, uint mins, uint hours) gavl_time_t lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours) { - REQUIRE (mins < 60); - REQUIRE (secs < 60); - REQUIRE (frames < fps); - gavl_time_t time = frames * (1000.0 / fps) + 1000 * secs + 1000 * 60 * mins @@ -91,11 +83,6 @@ lumiera_build_time_fps (float fps, uint frames, uint secs, uint mins, uint hours gavl_time_t lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours) { - REQUIRE (mins < 60); - REQUIRE (secs < 60); - REQUIRE (frames < 30); - REQUIRE_IF (secs == 0 && mins % 10, frames >= 2, "non-existent frame in NTSC drop-frame"); - int total_mins = 60 * hours + mins; int total_frames = 108000 * hours + 1800 * mins From 54189024420605df6cf07ad23df987b868ca9f51 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 19 Jan 2011 10:20:04 +0100 Subject: [PATCH 085/140] remove superfluous default parameters from Tuple constructor function --- src/lib/meta/tuple.hpp | 96 +++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/lib/meta/tuple.hpp b/src/lib/meta/tuple.hpp index 39e9e53dd..38d5def29 100644 --- a/src/lib/meta/tuple.hpp +++ b/src/lib/meta/tuple.hpp @@ -302,7 +302,7 @@ namespace typelist{ template inline Tuple< Types > - make ( T1 a1 =T1() + make ( T1 a1 ) { return Tuple > (a1); @@ -314,8 +314,8 @@ namespace typelist{ > inline Tuple< Types > - make ( T1 a1 =T1() - , T2 a2 =T2() + make ( T1 a1 + , T2 a2 ) { return Tuple > (a1,a2); @@ -328,9 +328,9 @@ namespace typelist{ > inline Tuple< Types > - make ( T1 a1 =T1() - , T2 a2 =T2() - , T3 a3 =T3() + make ( T1 a1 + , T2 a2 + , T3 a3 ) { return Tuple > (a1,a2,a3); @@ -344,10 +344,10 @@ namespace typelist{ > inline Tuple< Types > - make ( T1 a1 =T1() - , T2 a2 =T2() - , T3 a3 =T3() - , T4 a4 =T4() + make ( T1 a1 + , T2 a2 + , T3 a3 + , T4 a4 ) { return Tuple > (a1,a2,a3,a4); @@ -362,11 +362,11 @@ namespace typelist{ > inline Tuple< Types > - make ( T1 a1 =T1() - , T2 a2 =T2() - , T3 a3 =T3() - , T4 a4 =T4() - , T5 a5 =T5() + make ( T1 a1 + , T2 a2 + , T3 a3 + , T4 a4 + , T5 a5 ) { return Tuple > (a1,a2,a3,a4,a5); @@ -382,12 +382,12 @@ namespace typelist{ > inline Tuple< Types > - make ( T1 a1 =T1() - , T2 a2 =T2() - , T3 a3 =T3() - , T4 a4 =T4() - , T5 a5 =T5() - , T6 a6 =T6() + make ( T1 a1 + , T2 a2 + , T3 a3 + , T4 a4 + , T5 a5 + , T6 a6 ) { return Tuple > (a1,a2,a3,a4,a5,a6); @@ -404,13 +404,13 @@ namespace typelist{ > inline Tuple< Types > - make ( T1 a1 =T1() - , T2 a2 =T2() - , T3 a3 =T3() - , T4 a4 =T4() - , T5 a5 =T5() - , T6 a6 =T6() - , T7 a7 =T7() + make ( T1 a1 + , T2 a2 + , T3 a3 + , T4 a4 + , T5 a5 + , T6 a6 + , T7 a7 ) { return Tuple > (a1,a2,a3,a4,a5,a6,a7); @@ -428,14 +428,14 @@ namespace typelist{ > inline Tuple< Types > - make ( T1 a1 =T1() - , T2 a2 =T2() - , T3 a3 =T3() - , T4 a4 =T4() - , T5 a5 =T5() - , T6 a6 =T6() - , T7 a7 =T7() - , T8 a8 =T8() + make ( T1 a1 + , T2 a2 + , T3 a3 + , T4 a4 + , T5 a5 + , T6 a6 + , T7 a7 + , T8 a8 ) { return Tuple > (a1,a2,a3,a4,a5,a6,a7,a8); @@ -454,15 +454,15 @@ namespace typelist{ > inline Tuple< Types > - make ( T1 a1 =T1() - , T2 a2 =T2() - , T3 a3 =T3() - , T4 a4 =T4() - , T5 a5 =T5() - , T6 a6 =T6() - , T7 a7 =T7() - , T8 a8 =T8() - , T9 a9 =T9() + make ( T1 a1 + , T2 a2 + , T3 a3 + , T4 a4 + , T5 a5 + , T6 a6 + , T7 a7 + , T8 a8 + , T9 a9 ) { return Tuple > (a1,a2,a3,a4,a5,a6,a7,a8,a9); @@ -664,12 +664,12 @@ namespace typelist{ /** * Decorating a tuple type with auxiliary data access operations. - * This helper template builds up a subclass of the given BASE type + * This helper template builds up a subclass of the given TUP (base) type * (which is assumed to be a Tuple or at least need to be copy constructible * from \c Tuple ). The purpose is to use the Tuple as storage, but * to add a layer of access functions, which in turn might rely on the exact * type of the individual elements within the Tuple. To achieve this, for each - * type within the Tuple, the BASE type is decorated with an instance of the + * type within the Tuple, the TUP type is decorated with an instance of the * template passed in as template template parameter _X_. Each of these * decorating instances is provided with a index allowing to access "his" * specific element within the underlying tuple. @@ -678,7 +678,7 @@ namespace typelist{ * parameter. Typically, operations on _X_ will be defined in a recursive fashion, * calling down into this templated base class. To support this, an instantiation * of _X_ with the empty type sequence is generated for detecting recursion end - * (built as innermost decorator, i.e. immediate subclass of BASE) + * (built as innermost decorator, i.e. immediate subclass of TUP) */ template < typename TYPES ///< Type sequence to use within the Accessor (usually the Tuple Types) From c3d0fda4959b27d455d61b08338f8ba0f3de56c3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 19 Jan 2011 10:22:46 +0100 Subject: [PATCH 086/140] remove superfluous type qualifier --- src/proc/play/dummy-image-generator.cpp | 4 ++-- src/proc/play/dummy-image-generator.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/proc/play/dummy-image-generator.cpp b/src/proc/play/dummy-image-generator.cpp index 250549b42..44cf97cd1 100644 --- a/src/proc/play/dummy-image-generator.cpp +++ b/src/proc/play/dummy-image-generator.cpp @@ -89,7 +89,7 @@ namespace proc { { } - const LumieraDisplayFrame + LumieraDisplayFrame DummyImageGenerator::next() { @@ -156,7 +156,7 @@ namespace proc { } - const LumieraDisplayFrame + LumieraDisplayFrame DummyImageGenerator::current() { if (!current_) return outFrame_A_; diff --git a/src/proc/play/dummy-image-generator.hpp b/src/proc/play/dummy-image-generator.hpp index e2d70d322..c893962b8 100644 --- a/src/proc/play/dummy-image-generator.hpp +++ b/src/proc/play/dummy-image-generator.hpp @@ -69,11 +69,11 @@ namespace proc { * occupy the alternate buffer. * @return the buffer containing the new frame */ - const LumieraDisplayFrame next(); + LumieraDisplayFrame next(); /** just re-return a pointer to the current frame * without generating any new image data */ - const LumieraDisplayFrame current(); + LumieraDisplayFrame current(); private: From e1c025778b38449471e56becc7d09de822885a8d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 19 Jan 2011 10:52:18 +0100 Subject: [PATCH 087/140] Digxel: ensure the mutator functor is also called on increment --- src/lib/time/digxel.hpp | 12 ++-- tests/lib/time/digxel-configurations-test.cpp | 1 + tests/lib/time/digxel-test.cpp | 57 +++++++++++++++++-- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 1e4aa8f95..ceb901461 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -272,12 +272,12 @@ namespace time { //---Supporting-increments-------------- - Digxel& operator+= (NUM inc) { value_ += inc; return *this; } - Digxel& operator-= (NUM dec) { value_ -= dec; return *this; } - Digxel& operator++ () { value_ += 1; return *this; } - Digxel& operator-- () { value_ -= 1; return *this; } - NUM operator++ (int) { NUM p(value_++); return p; } - NUM operator-- (int) { NUM p(value_--); return p; } + Digxel& operator+= (NUM inc) { *this = value_ + inc; return *this; } + Digxel& operator-= (NUM dec) { *this = value_ - dec; return *this; } + Digxel& operator++ () { *this = value_ + 1; return *this; } + Digxel& operator-- () { *this = value_ - 1; return *this; } + NUM operator++ (int) { NUM p(value_); *this =p+1; return p;} + NUM operator-- (int) { NUM p(value_); *this =p-1; return p;} //---Supporting-totally_ordered--------- bool operator< (Digxel const& o) const { return value_ < NUM(o); } diff --git a/tests/lib/time/digxel-configurations-test.cpp b/tests/lib/time/digxel-configurations-test.cpp index 607be80b1..ba6424054 100644 --- a/tests/lib/time/digxel-configurations-test.cpp +++ b/tests/lib/time/digxel-configurations-test.cpp @@ -56,6 +56,7 @@ namespace test{ * - hex byte Digxel * - ...more to come * @todo cover any newly added Digxel configurations. + * @see Digxel_test */ class DigxelConfigurations_test : public Test { diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp index ebf62c853..fff6acb87 100644 --- a/tests/lib/time/digxel-test.cpp +++ b/tests/lib/time/digxel-test.cpp @@ -63,6 +63,9 @@ namespace test{ } + + /* === special Digxel configuration for this test === */ + double sum(0), checksum(0); @@ -73,8 +76,15 @@ namespace test{ return val; } + double preval(0), newval(0); - /* === special Digxel configuration for this test === */ + double + protocollingMutator (double val) + { + preval = newval; + newval = val; + return val; + } double limitingMutator (double value2set) @@ -107,6 +117,7 @@ namespace test{ * - build a Digxel * - set a value * - retrieve formatted display + * - verify comparisons and increments * - performing side-effects from the setter-functor * - formatted value caching */ @@ -118,6 +129,7 @@ namespace test{ checkSimpleUsage (); checkMutation (); verifyMutatorInfluence (); + verifyAssignMutatingOperators (); verifyComparisons (); checkCopy (); checkDisplayOverrun (); @@ -192,6 +204,43 @@ namespace test{ } + /** @test verify the self-assigning increment/decrement operators. + * @note especially these need to invoke the mutator function, + * much like a direct assignment. We use a special mutator + * to protocol the previous / new value. + */ + void + verifyAssignMutatingOperators () + { + TestDigxel digi; + digi.mutator = protocollingMutator; + + digi = 12.3; + CHECK ( 0.0 == preval && 12.3 == newval); + digi += 10; + CHECK (12.3 == preval && 22.3 == newval); + digi -= 5; + CHECK (22.3 == preval && 17.3 == newval); + ++digi; + CHECK (17.3 == preval && 18.3 == newval); + digi++; + CHECK (18.3 == preval && 19.3 == newval); + --digi; + CHECK (19.3 == preval && 18.3 == newval); + digi--; + CHECK (18.3 == preval && 17.3 == newval); + + double val = ++digi; + CHECK (18.3 == digi && 18.3 == val); + val = digi++; + CHECK (19.3 == digi && 18.3 == val); + val = --digi; + CHECK (18.3 == digi && 18.3 == val); + val = digi--; + CHECK (17.3 == digi && 18.3 == val); + } + + void verifyComparisons () { @@ -257,11 +306,11 @@ namespace test{ /** @test verify caching of formatted values. * Digxel avoids reformatting unchanged values; - * to verify the effectivity of this measure, we - * take some timings. + * to verify the effectivity of this measure, + * we'll take some timings. * @warning the results of such tests could be unreliable, * but in this case here I saw a significant difference, - * with values of 0.5sec / 0.8sec */ + * with values of about 0.5sec / 0.7sec */ void verifyDisplayCaching () { From 05383ea44a187d81a40928c2ff7dfa3076306f76 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 20 Jan 2011 13:21:14 +0100 Subject: [PATCH 088/140] crafting a integer scale wrapping util, for timecode conversions --- src/lib/time.h | 2 +- src/lib/time/digxel.hpp | 1 + src/lib/time/timecode.cpp | 40 ++++++++- src/tool/try.cpp | 47 ++++------- tests/40components.tests | 30 +++++++ tests/lib/util-floorwrap-test.cpp | 131 ++++++++++++++++++++++++++++++ wiki/renderengine.html | 6 +- 7 files changed, 218 insertions(+), 39 deletions(-) create mode 100644 tests/lib/util-floorwrap-test.cpp diff --git a/src/lib/time.h b/src/lib/time.h index 286e2ccb8..521b5658d 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -85,7 +85,7 @@ extern "C" { /* ===================== C interface ======================== */ #endif #define NTSC_DROP_FRAME_FPS 29.97 -/* TODO: replace this by lib/time/FrameRate::NTSC */ +/* TODO: replace this by lib::time::FrameRate::NTSC */ /** * Formats a time value in H:MM:SS.mmm format into a temporary buffer. diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index ceb901461..9c49983f4 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -255,6 +255,7 @@ namespace time { void operator= (NUM n) { + if (n == value_) return; NUM changedValue = mutator(n); this->setValueRaw (changedValue); } diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 1f3201cf0..7053800cf 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -59,8 +59,9 @@ namespace time { } - /** build up a frame count - * by quantising the given time value + /** build up a SMPTE timecode + * by quantising the given time value and then splitting it + * into hours, minutes, seconds and frame offset */ void Smpte::rebuild (SmpteTC& tc, QuantR quantiser, TimeValue const& rawTime) @@ -69,7 +70,8 @@ namespace time { UNIMPLEMENTED("build smpte components from raw time"); } - /** calculate the time point denoted by this frame count */ + /** calculate the time point denoted by this SMPTE timecode, + * by summing up the timecode's components */ TimeValue Smpte::evaluate (SmpteTC const& tc, QuantR quantiser) { @@ -79,6 +81,38 @@ namespace time { } + namespace { // Timecode implementation details + + + int + wrapFrames (SmpteTC& thisTC, int rawFrames) + { + int fps = 25; ////////////////////////////////TODO outch + thisTC.secs += rawFrames / fps; + return rawFrames % fps; + //////////////////////////////////TODO need floorwrap-util + } + + int + wrapSeconds (SmpteTC& thisTC, int rawSecs) + { + + } + + int + wrapMinutes (SmpteTC& thisTC, int rawMins) + { + + } + + int + wrapHours (SmpteTC& thisTC, int rawHours) + { + + } + + + }//(End)implementation details /** */ diff --git a/src/tool/try.cpp b/src/tool/try.cpp index f73e9dd6f..46080a56e 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -17,6 +17,7 @@ // 1/10 - can we determine at compile time the presence of a certain function (for duck-typing)? // 4/10 - pretty printing STL containers with python enabled GDB? // 1/11 - exploring numeric limits +// 1/11 - integer floor and wrap operation(s) //#include @@ -38,31 +39,24 @@ using std::string; using std::cout; using std::endl; -long -floordiv (long num, long den) - { - if (0 < (num^den)) - return num/den; - else - { - ldiv_t res = ldiv(num,den); - return (res.rem)? res.quot-1 - : res.quot; - } - } - -long -floordiv2 (long num, long den) - { - ldiv_t res = ldiv(num,den); - return (0 >= res.quot && res.rem)? res.quot-1 - : res.quot; - } +div_t +floorwrap (int num, int den) +{ + div_t res = div (num,den); + if (0 > (num^den) && res.rem) + { // wrap similar to floor() + --res.quot; + res.rem = den - (-res.rem); + } + return res; +} void checkDiv(int lhs, int rhs) { - cout << format ("%f / %f = %f \tfloor=%f floordiv=%f \n") % lhs % rhs % (lhs / rhs) % floor(double(lhs)/rhs) % floordiv2(lhs,rhs); + div_t wrap = floorwrap(lhs,rhs); + cout << format ("%2d / %2d = %2d %% = % d \tfloor=%6.2f wrap = (%2d, %2d) \n") + % lhs % rhs % (lhs/rhs) % (lhs%rhs) % floor(double(lhs)/rhs) % wrap.quot % wrap.rem; } int @@ -87,17 +81,6 @@ main (int, char**) checkDiv (-1,4); checkDiv (-1,-4); - - - int64_t muks = std::numeric_limits::max(); - muks /= 30; - double murks(muks); - - cout << format("%f // %f || %g \n") % muks % murks % std::numeric_limits::epsilon(); - - int64_t glucks = murks; - cout << glucks < + + 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 "lib/test/run.hpp" +#include "lib/util.hpp" + +#include +//#include +#include +#include + +using ::Test; +using std::cout; +//using std::rand; +using boost::format; + + +namespace util { +namespace test { + + + + namespace{ // Test data and operations + + div_t + floorwrap (int num, int den) + { + div_t res = div (num,den); + if (0 > (num^den) && res.rem) + { // wrap similar to floor() + --res.quot; + res.rem = den - (-res.rem); + } + return res; + } + + + void + showWrap (int val, int scale) + { + div_t wrap = floorwrap(val,scale); + cout << format ("% 3d /% 1d =% 1d %% =% d floor=% 4.1f wrap = (%2d,%2d) \n") + % val % scale % (val/scale) + % (val%scale) % floor(double(val)/scale) + % wrap.quot % wrap.rem; + } + + + + } // (End) test data and operations + + + + /********************************************************************** + * @test Evaluate a custom built integer floor function. + * This function is crucial for Lumiera's rule of quantisation + * of time values into frame intervals. This rule requires time + * points to be rounded towards the next lower frame border always, + * irrespective of the relation to the actual time origin. + * Contrast this to the built-in integer division operator, which + * truncates towards zero. + * + * @note if invoked with an non empty parameter, this test performs + * some interesting timing comparisons, which initially were + * used to tweak the implementation a bit. + * @see util.hpp + * @see QuantiserBasics_test + */ + class UtilFloorwrap_test : public Test + { + + virtual void + run (Arg arg) + { + showWrap ( 12,4); + showWrap ( 11,4); + showWrap ( 10,4); + showWrap ( 9,4); + showWrap ( 8,4); + showWrap ( 7,4); + showWrap ( 6,4); + showWrap ( 5,4); + showWrap ( 4,4); + showWrap ( 3,4); + showWrap ( 2,4); + showWrap ( 1,4); + showWrap ( 0,4); + showWrap (- 1,4); + showWrap (- 2,4); + showWrap (- 3,4); + showWrap (- 4,4); + showWrap (- 5,4); + showWrap (- 6,4); + showWrap (- 7,4); + showWrap (- 8,4); + showWrap (- 9,4); + showWrap (-10,4); + showWrap (-11,4); + showWrap (-12,4); + } + + + }; + + + + + LAUNCHER (UtilFloorwrap_test, "unit common"); + + +}} // namespace util::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 85651f235..792a6487d 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6746,7 +6746,7 @@ Question is: how fine grained and configurable needs this to be?
-
+
The handling of [[Timecode]] is closely related to [[time representation and quantisation|TimeQuant]]. In fact, these two topics blend into one another. Time will be quantised into a //grid,// but this grid only makes sense when linked to a externally relevant meaning and representation, which is the Timecode. But a timecode value also denotes a specific point in time -- performing operations on a timecode is equivalent to manipulating a quantised time value.
 
 !Problem of dependencies
@@ -6771,9 +6771,9 @@ And last but not least, it is possible to get a new ~TimeValue, reflecting the c
 !!{{red{WIP 1/11}}}design tasks
 * @@color:green;✔@@ find out about the connection to the MetaAsset &rarr; [[Advice]]
 * @@color:green;✔@@ determine the primitives which need to be on the //effective quantiser API.//
-* find out how a format can address the individual components it's comprised of
+* @@color:green;✔@@ find out how a format can address the individual components it's comprised of &rarr; direct access by concrete type
 * @@color:green;✔@@ decide how a concrete TC value can refer to his quantiser &rarr; always using smart-ptr
-* maybe coin a //value handle// -- to tie the three required parts together
+* @@color:red;??@@ maybe coin a //value handle// -- to tie the three required parts together
 
From c55260d4e49ca25a64cee3008eaae25c5404d20b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 20 Jan 2011 21:30:48 +0100 Subject: [PATCH 089/140] generalise to long and int; improve test coverage --- src/lib/util.hpp | 56 ++++++++++++- tests/40components.tests | 131 ++++++++++++++++++++++++------ tests/lib/util-floordiv-test.cpp | 16 ++-- tests/lib/util-floorwrap-test.cpp | 110 ++++++++++--------------- 4 files changed, 205 insertions(+), 108 deletions(-) diff --git a/src/lib/util.hpp b/src/lib/util.hpp index 916374a90..ce3755c52 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -56,6 +56,30 @@ namespace util { return n1 < n2? N1(n2) : n1; } + + /** helper to treat int or long division uniformly */ + template + struct IDiv; + + template<> + struct IDiv + : div_t + { + IDiv (int num, int den) + : div_t(div (num,den)) + { } + }; + + template<> + struct IDiv + : ldiv_t + { + IDiv (long num, long den) + : ldiv_t(ldiv (num,den)) + { } + }; + + /** floor function for integer arithmetics. * Unlike the built-in integer division, this function * always rounds towards the next \em smaller integer, @@ -63,20 +87,44 @@ namespace util { * @warning floor on doubles performs way better * @see UtilFloordiv_test */ - template - inline LI - floordiv (LI num, LI den) + template + inline I + floordiv (I num, I den) { if (0 < (num^den)) return num/den; else { // truncate similar to floor() - ldiv_t res = ldiv(num,den); + IDiv res(num,den); return (res.rem)? res.quot-1 // negative results truncated towards next smaller int : res.quot; //..unless the division result not truncated at all } } + /** scale wrapping operation. + * Quantises the numerator value into the scale given by the denominator. + * Unlike the built-in integer division, this function always rounds towards + * the next \em smaller integer and also relates the remainder (=modulo) to + * this next lower scale grid point. + * @return quotient and remainder packed into a struct + * @see UtilFloorwarp_test + */ + template + inline IDiv + floorwrap (I num, I den) + { + IDiv res(num,den); + if (0 > (num^den) && res.rem) + { // negative results + // wrapped similar to floor() + --res.quot; + res.rem = den - (-res.rem); + } + return res; + } + + + /* ======== generic empty check ========= */ /** a family of util functions providing a "no value whatsoever" test. Works on strings and all STL containers, includes NULL test for pointers */ diff --git a/tests/40components.tests b/tests/40components.tests index 395981ba3..3020d34c7 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -1094,32 +1094,111 @@ return: 0 END -PLANNED "integer scale wrapping utility" UtilFloorwrap_test < instantiation is largely moot, because - * this internally calls \c fdiv on values promoted to long. Another - * oddity in the same category is the slightly better performance - * of long over int64_t. Also, the alternative formulation of - * the function, which uses the \c fdiv() function also to divide - * the positive results, performs only slightly worse. So this - * actual implementation was chosen mainly because it seems - * to state its intent more clearly in code. + * compared to the built-in integer div function. An oddity to note + * is the slightly better performance of long over int64_t. Also, + * the alternative formulation of the function, which uses the + * \c fdiv() function also to divide the positive results, + * performs only slightly worse. So this implementation + * was chosen mainly because it seems to state its + * intent more clearly in code. */ void runPerformanceTest () diff --git a/tests/lib/util-floorwrap-test.cpp b/tests/lib/util-floorwrap-test.cpp index 0e1885e5b..c42f37593 100644 --- a/tests/lib/util-floorwrap-test.cpp +++ b/tests/lib/util-floorwrap-test.cpp @@ -22,17 +22,20 @@ #include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" #include "lib/util.hpp" #include -//#include #include #include +#include using ::Test; using std::cout; -//using std::rand; +using std::endl; using boost::format; +using boost::lexical_cast; +using lib::test::showType; namespace util { @@ -40,51 +43,18 @@ namespace test { - namespace{ // Test data and operations - - div_t - floorwrap (int num, int den) - { - div_t res = div (num,den); - if (0 > (num^den) && res.rem) - { // wrap similar to floor() - --res.quot; - res.rem = den - (-res.rem); - } - return res; - } - - - void - showWrap (int val, int scale) - { - div_t wrap = floorwrap(val,scale); - cout << format ("% 3d /% 1d =% 1d %% =% d floor=% 4.1f wrap = (%2d,%2d) \n") - % val % scale % (val/scale) - % (val%scale) % floor(double(val)/scale) - % wrap.quot % wrap.rem; - } - - - - } // (End) test data and operations - - /********************************************************************** - * @test Evaluate a custom built integer floor function. - * This function is crucial for Lumiera's rule of quantisation - * of time values into frame intervals. This rule requires time - * points to be rounded towards the next lower frame border always, - * irrespective of the relation to the actual time origin. - * Contrast this to the built-in integer division operator, which - * truncates towards zero. - * - * @note if invoked with an non empty parameter, this test performs - * some interesting timing comparisons, which initially were - * used to tweak the implementation a bit. + /*************************************************************************** + * @test Verify a custom built integer scale division and wrapping function. + * This function is relevant for decimating values into a given scale, + * like splitting time measurements in hours, minutes, seconds etc. + * Basically, in Lumiera the quantisation into a scale is always + * done with the same orientation, irrespective of the zero point + * on the given scale. Contrast this to the built-in integer + * division and modulo operators working symmetrical to zero. * @see util.hpp - * @see QuantiserBasics_test + * @see TimeFormats_test */ class UtilFloorwrap_test : public Test { @@ -92,34 +62,36 @@ namespace test { virtual void run (Arg arg) { - showWrap ( 12,4); - showWrap ( 11,4); - showWrap ( 10,4); - showWrap ( 9,4); - showWrap ( 8,4); - showWrap ( 7,4); - showWrap ( 6,4); - showWrap ( 5,4); - showWrap ( 4,4); - showWrap ( 3,4); - showWrap ( 2,4); - showWrap ( 1,4); - showWrap ( 0,4); - showWrap (- 1,4); - showWrap (- 2,4); - showWrap (- 3,4); - showWrap (- 4,4); - showWrap (- 5,4); - showWrap (- 6,4); - showWrap (- 7,4); - showWrap (- 8,4); - showWrap (- 9,4); - showWrap (-10,4); - showWrap (-11,4); - showWrap (-12,4); + int range = 0 < arg.size()? lexical_cast (arg[0]) : 12; + int scale = 1 < arg.size()? lexical_cast (arg[1]) : 4; + + checkWrap (range, scale); + checkWrap (range, -scale); + checkWrap (range, scale); + checkWrap (range, -scale); } + template + void + checkWrap (I range, I scale) + { + cout << "--------"<< showType() + << "--------"<< range<<"/"<=-range; --i) + showWrap (i, scale); + } + + template + void + showWrap (I val, I scale) + { + IDiv wrap = floorwrap(val,scale); + cout << format ("% 3d /% 1d =% 1d %% =% d floor=% 4.1f wrap = (%2d,%2d)\n") + % val % scale % (val/scale) + % (val%scale) % floor(double(val)/scale) + % wrap.quot % wrap.rem; + } }; From 1b79b4a937a0d5fd8e9920ef0e365ed64d23b94d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 20 Jan 2011 21:39:17 +0100 Subject: [PATCH 090/140] fix regression --- tests/lib/time/digxel-test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp index fff6acb87..fd663fa37 100644 --- a/tests/lib/time/digxel-test.cpp +++ b/tests/lib/time/digxel-test.cpp @@ -190,6 +190,8 @@ namespace test{ digi.mutator = limitingMutator; CHECK (12.3 == digi); digi = 12.3; + CHECK (12.3 == digi); // triggered on real change only + digi = 12.2; CHECK (1 == digi); digi = 0.5; From f930703e86fe913cfa3c9f7f67719f701260b13a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 20 Jan 2011 22:17:25 +0100 Subject: [PATCH 091/140] WIP draft cascading breakdown of time into timecode components TODO not entirely correct... and how to access the framerate? --- src/lib/time/timecode.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 7053800cf..26a0ffa79 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -30,8 +30,9 @@ #include "lib/util.hpp" -using util::unConst; using std::string; +using util::unConst; +using util::IDiv; namespace lib { @@ -88,27 +89,32 @@ namespace time { wrapFrames (SmpteTC& thisTC, int rawFrames) { int fps = 25; ////////////////////////////////TODO outch - thisTC.secs += rawFrames / fps; - return rawFrames % fps; - //////////////////////////////////TODO need floorwrap-util + IDiv scaleRelation(rawFrames, fps); + thisTC.secs += scaleRelation.quot; + return scaleRelation.rem; } int wrapSeconds (SmpteTC& thisTC, int rawSecs) { - + IDiv scaleRelation(rawSecs, 60); + thisTC.mins += scaleRelation.quot; + return scaleRelation.rem; } int wrapMinutes (SmpteTC& thisTC, int rawMins) { - + IDiv scaleRelation(rawMins, 60); + thisTC.hours += scaleRelation.quot; + return scaleRelation.rem; } int wrapHours (SmpteTC& thisTC, int rawHours) { - + thisTC.sgn = rawHours; + return rawHours; } From 0493caac1d56393bab8a30ddacf75c347a7ee737 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 21 Jan 2011 11:42:29 +0100 Subject: [PATCH 092/140] first complete implementation of SMPTE timecode first integration of the various components developed thus far; design still needs some improvements, esp. regarding the effectiveFramerate Note NTSC-drop-frame not yet supported... --- src/lib/time/formats.hpp | 1 + src/lib/time/timecode.cpp | 78 +++++++++++++++++++++++++++++++-------- src/lib/time/timecode.hpp | 2 + 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index df7b83db2..80d7f9722 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -83,6 +83,7 @@ namespace time { { static void rebuild (SmpteTC&, QuantR, TimeValue const&); static TimeValue evaluate (SmpteTC const&, QuantR); + static uint getFramerate (QuantR, TimeValue const&); }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 26a0ffa79..f637b2ae8 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -29,10 +29,12 @@ #include "lib/time.h" #include "lib/util.hpp" +#include using std::string; using util::unConst; -using util::IDiv; +using std::tr1::bind; +using std::tr1::placeholders::_1; namespace lib { @@ -62,13 +64,13 @@ namespace time { /** build up a SMPTE timecode * by quantising the given time value and then splitting it - * into hours, minutes, seconds and frame offset + * into hours, minutes, seconds and frame offset. */ void Smpte::rebuild (SmpteTC& tc, QuantR quantiser, TimeValue const& rawTime) { -// framecnt.setValueRaw(quantiser.gridPoint (rawTime)); - UNIMPLEMENTED("build smpte components from raw time"); + tc.frames = quantiser.gridPoint (rawTime); + // will automatically wrap over to the secs, minutes and hour fields } /** calculate the time point denoted by this SMPTE timecode, @@ -76,20 +78,48 @@ namespace time { TimeValue Smpte::evaluate (SmpteTC const& tc, QuantR quantiser) { -// return quantiser.timeOf (framecnt); - UNIMPLEMENTED("calculate time from smpte"); + uint frameRate = tc.getFps(); + long gridPoint = tc.frames + + tc.secs * frameRate + + tc.mins * frameRate * 60 + + tc.hours * frameRate * 60 * 60; + return quantiser.timeOf (gridPoint); + } + + /** yield the Framerate in effect at that point. + * Especially Timecode in SMPTE format exposes a "frames" field + * to contain the remainder of frames in addition to the h:m:s value. + * Obviously this value has to be kept below the number of frames for + * a full second and wrap around accordingly. + * @note SMPTE format assumes this framerate to be constant. Actually, + * in this implementation the value returned here neither needs + * to be constant (independent of the given rawTime), nor does + * it need to be the actual framerate used by the quantiser. + * Especially in case of NTSC drop-frame, the timecode + * uses 30fps here, while the quantisation uses 29.97 + * @todo this design just doesn't feel quite right... + */ + uint + Smpte::getFramerate (QuantR quantiser_, TimeValue const& rawTime) + { + long refCnt = quantiser_.gridPoint(rawTime); + long newCnt = quantiser_.gridPoint(Time(0.5,1) + rawTime); + long effectiveFrames = newCnt - refCnt; + ENSURE (1000 > effectiveFrames); + ENSURE (0 < effectiveFrames); + return uint(effectiveFrames); } } namespace { // Timecode implementation details + typedef util::IDiv Div; int wrapFrames (SmpteTC& thisTC, int rawFrames) { - int fps = 25; ////////////////////////////////TODO outch - IDiv scaleRelation(rawFrames, fps); + Div scaleRelation(rawFrames, thisTC.getFps()); thisTC.secs += scaleRelation.quot; return scaleRelation.rem; } @@ -97,7 +127,7 @@ namespace time { int wrapSeconds (SmpteTC& thisTC, int rawSecs) { - IDiv scaleRelation(rawSecs, 60); + Div scaleRelation(rawSecs, 60); thisTC.mins += scaleRelation.quot; return scaleRelation.rem; } @@ -105,7 +135,7 @@ namespace time { int wrapMinutes (SmpteTC& thisTC, int rawMins) { - IDiv scaleRelation(rawMins, 60); + Div scaleRelation(rawMins, 60); thisTC.hours += scaleRelation.quot; return scaleRelation.rem; } @@ -133,7 +163,16 @@ namespace time { /** */ SmpteTC::SmpteTC (QuTime const& quantisedTime) : TCode(quantisedTime) - { } + , effectiveFramerate_(Format::getFramerate (*quantiser_, quantisedTime)) + { + // Functors to normalise raw component values + hours.mutator = bind (wrapHours, *this, _1 ); + mins.mutator = bind (wrapMinutes, *this, _1 ); + secs.mutator = bind (wrapSeconds, *this, _1 ); + frames.mutator = bind (wrapFrames, *this, _1 ); + + quantisedTime.castInto (*this); + } /** */ @@ -154,8 +193,15 @@ namespace time { void SmpteTC::rebuild() const { - TimeValue point = Format::evaluate(*this, *quantiser_); - Format::rebuild(unConst(*this), *quantiser_, point); + TimeValue point = Format::evaluate (*this, *quantiser_); + Format::rebuild (unConst(*this), *quantiser_, point); + } + + + uint + SmpteTC::getFps() const + { + return effectiveFramerate_; //////////////////////////////////TODO better design. Shouldn't Format::getFramerate(QuantR, TimeValue) be moved here? } @@ -179,13 +225,15 @@ namespace time { SmpteTC& SmpteTC::operator++ () { - UNIMPLEMENTED ("SMPTE unit increment"); + ++frames; + return *this; } SmpteTC& SmpteTC::operator-- () { - UNIMPLEMENTED ("SMPTE unit decrement"); + --frames; + return *this; } /** */ diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index e633ccf58..2befbfa60 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -107,6 +107,7 @@ namespace time { : public TCode , boost::unit_steppable { + uint effectiveFramerate_; virtual string show() const ; virtual Literal tcID() const { return "SMPTE"; } @@ -118,6 +119,7 @@ namespace time { SmpteTC (QuTime const& quantisedTime); void rebuild() const; + uint getFps() const; Digxel hours; SexaDigit mins; From 1a07cc9bb241cb6dceb4d78e42fa7e29340094ae Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 21 Jan 2011 16:22:01 +0100 Subject: [PATCH 093/140] SMPTE Timecode: first round of debugging and testing --- src/lib/time/digxel.hpp | 7 +++ src/lib/time/timecode.cpp | 73 +++++++++++++++++++++------- src/lib/time/timecode.hpp | 12 +++-- tests/lib/time/time-formats-test.cpp | 32 +++++++----- 4 files changed, 90 insertions(+), 34 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 9c49983f4..00a569311 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -175,6 +175,12 @@ namespace time { CountFormatter() : PrintfFormatter("%04ld") { } }; + struct HourFormatter + : PrintfFormatter + { + HourFormatter() : PrintfFormatter("%2d") { } + }; + struct SignFormatter { @@ -290,6 +296,7 @@ namespace time { /* == predefined Digxel configurations == */ typedef Digxel< int, digxel::SexaFormatter> SexaDigit; ///< for displaying time components (sexagesimal) typedef Digxel HexaDigit; ///< for displaying a hex byte + typedef Digxel< int, digxel::HourFormatter> HourDigit; ///< for displaying hours in H:M.S typedef Digxel CountVal; ///< for displaying a counter diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index f637b2ae8..a57106ecb 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -33,8 +33,7 @@ using std::string; using util::unConst; -using std::tr1::bind; -using std::tr1::placeholders::_1; +using util::isSameObject; namespace lib { @@ -117,37 +116,51 @@ namespace time { typedef util::IDiv Div; int - wrapFrames (SmpteTC& thisTC, int rawFrames) + wrapFrames (SmpteTC* thisTC, int rawFrames) { - Div scaleRelation(rawFrames, thisTC.getFps()); - thisTC.secs += scaleRelation.quot; + Div scaleRelation(rawFrames, thisTC->getFps()); + thisTC->secs += scaleRelation.quot; return scaleRelation.rem; } int - wrapSeconds (SmpteTC& thisTC, int rawSecs) + wrapSeconds (SmpteTC* thisTC, int rawSecs) { Div scaleRelation(rawSecs, 60); - thisTC.mins += scaleRelation.quot; + thisTC->mins += scaleRelation.quot; return scaleRelation.rem; } int - wrapMinutes (SmpteTC& thisTC, int rawMins) + wrapMinutes (SmpteTC* thisTC, int rawMins) { Div scaleRelation(rawMins, 60); - thisTC.hours += scaleRelation.quot; + thisTC->hours += scaleRelation.quot; return scaleRelation.rem; } int - wrapHours (SmpteTC& thisTC, int rawHours) + wrapHours (SmpteTC* thisTC, int rawHours) { - thisTC.sgn = rawHours; + thisTC->sgn = rawHours; return rawHours; } + using std::tr1::bind; + using std::tr1::placeholders::_1; + + /** bind the individual Digxel mutation functors + * to normalise raw component values */ + inline void + setupComponentNormalisation (SmpteTC * thisTC) + { + thisTC->hours.mutator = bind (wrapHours, thisTC, _1 ); + thisTC->mins.mutator = bind (wrapMinutes, thisTC, _1 ); + thisTC->secs.mutator = bind (wrapSeconds, thisTC, _1 ); + thisTC->frames.mutator = bind (wrapFrames, thisTC, _1 ); + } + }//(End)implementation details @@ -165,16 +178,41 @@ namespace time { : TCode(quantisedTime) , effectiveFramerate_(Format::getFramerate (*quantiser_, quantisedTime)) { - // Functors to normalise raw component values - hours.mutator = bind (wrapHours, *this, _1 ); - mins.mutator = bind (wrapMinutes, *this, _1 ); - secs.mutator = bind (wrapSeconds, *this, _1 ); - frames.mutator = bind (wrapFrames, *this, _1 ); - + setupComponentNormalisation (this); quantisedTime.castInto (*this); } + SmpteTC::SmpteTC (SmpteTC const& o) + : TCode(o) + , effectiveFramerate_(o.effectiveFramerate_) + { + setupComponentNormalisation (this); + sgn = o.sgn; + hours = o.hours; + mins = o.mins; + secs = o.secs; + frames = o.frames; + } + + + SmpteTC& + SmpteTC::operator= (SmpteTC const& o) + { + if (!isSameObject (*this, o)) + { + TCode::operator= (o); + effectiveFramerate_ = o.effectiveFramerate_; + sgn = o.sgn; + hours = o.hours; + mins = o.mins; + secs = o.secs; + frames = o.frames; + } + return *this; + } + + /** */ HmsTC::HmsTC (QuTime const& quantisedTime) : TCode(quantisedTime) @@ -208,7 +246,6 @@ namespace time { string SmpteTC::show() const { - rebuild(); string tc; tc.reserve(15); tc += sgn.show(); diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 2befbfa60..faaf00adb 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -117,15 +117,17 @@ namespace time { typedef format::Smpte Format; SmpteTC (QuTime const& quantisedTime); + SmpteTC (SmpteTC const&); + SmpteTC& operator= (SmpteTC const&); void rebuild() const; uint getFps() const; - Digxel hours; - SexaDigit mins; - SexaDigit secs; - SexaDigit frames; - Signum sgn; + HourDigit hours; + SexaDigit mins; + SexaDigit secs; + SexaDigit frames; + Signum sgn; SmpteTC& operator++(); SmpteTC& operator--(); diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index 2fa68ec18..343f8f6bb 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -66,6 +66,7 @@ namespace test{ // checkHms (); checkSmpte(); // checkDropFrame(); +// checkCopyAssignments(); } @@ -107,13 +108,13 @@ namespace test{ void checkSmpte () { - Time raw(0.5,23,42,5); + Time raw(555,23,42,5); QuTime t1 (raw, "pal0"); SmpteTC smpte(t1); showTimeCode(smpte); - CHECK ("5:42:23:13" == string(smpte)); - CHECK (raw + Time(0.02,0) == smpte.getTime()); + CHECK (" 5:42:23:13" == string(smpte)); + CHECK (raw - Time(35,0) == smpte.getTime()); CHECK (13 == smpte.frames); CHECK (23 == smpte.secs); CHECK (42 == smpte.mins); @@ -122,29 +123,31 @@ namespace test{ CHECK ("SMPTE" == smpte.describe()); ++smpte; - CHECK ("5:42:23:14" == string(smpte)); + CHECK (" 5:42:23:14" == string(smpte)); smpte.frames += 12; - CHECK ("5:42:24:01" == string(smpte)); + CHECK (" 5:42:24:01" == string(smpte)); smpte.secs = -120; - CHECK ("5:40:00:01" == string(smpte)); + CHECK (" 5:40:00:01" == string(smpte)); CHECK (smpte.mins-- == 40); CHECK (--smpte.mins == 38); - CHECK ("5:38:00:01" == string(smpte)); + CHECK (" 5:38:00:01" == string(smpte)); Time tx = smpte.getTime(); smpte.hours -= 6; - CHECK ("-0:21:59:24"== string(smpte)); + CHECK ("- 0:21:59:24"== string(smpte)); CHECK (tx - Time(6*60*60) == smpte.getTime()); CHECK (-1 == smpte.sgn); smpte.sgn += 123; - CHECK ("0:21:59:24"== string(smpte)); + CHECK (" 0:21:59:24"== string(smpte)); smpte.secs.setValueRaw(61); CHECK (smpte.secs == 61); - CHECK (smpte.getTime() == Time(24.0/25,01,22)); + CHECK (smpte.getTime() == Time(1000*24/25, 01, 22)); CHECK (smpte.secs == 61); - smpte.hours += 0; + CHECK (" 0:21:61:24"== string(smpte)); + smpte.rebuild(); CHECK (smpte.secs == 1); CHECK (smpte.mins == 22); + CHECK (" 0:22:01:24"== string(smpte)); } @@ -153,6 +156,13 @@ namespace test{ { UNIMPLEMENTED ("verify especially SMPTE-drop-frame timecode"); } + + + void + checkCopyAssignments () + { + UNIMPLEMENTED ("verify Timecode values can be copied and assigned properly"); + } }; From 9d75739089beaa768b1b7932a14d0bbecb67a8f5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 21 Jan 2011 20:24:41 +0100 Subject: [PATCH 094/140] SMPTE Timecode (without drop frame) unit test pass --- src/lib/time/digxel.hpp | 22 +++-- src/lib/time/formats.hpp | 1 + src/lib/time/timecode.cpp | 93 +++++++++++++++++-- src/lib/time/timecode.hpp | 4 +- tests/40components.tests | 3 +- tests/lib/time/digxel-configurations-test.cpp | 6 +- tests/lib/time/digxel-test.cpp | 2 +- tests/lib/time/time-formats-test.cpp | 16 ++-- wiki/renderengine.html | 21 ++++- 9 files changed, 139 insertions(+), 29 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 00a569311..95814bd37 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -302,19 +302,29 @@ namespace time { /** special Digxel to show a sign. * @note values limited to +1 and -1 */ - struct Signum - : Digxel + class Signum + : public Digxel { - Signum() { setValueRaw(1); } + static int + just_the_sign (int val) + { + return val<0? -1:+1; + } + + public: + Signum() + { + setValueRaw(1); + mutator = just_the_sign; + } void operator= (int n) { - int newSign = 0 > mutator(n)? -1:+1; - this->setValueRaw (newSign); + this->setValueRaw (mutator(n)); } - friend int operator*= (Signum s, int c) { s = c*s; return s; } + friend int operator*= (Signum& s, int c) { s = c*s; return s; } }; diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index 80d7f9722..05adaaecb 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -84,6 +84,7 @@ namespace time { static void rebuild (SmpteTC&, QuantR, TimeValue const&); static TimeValue evaluate (SmpteTC const&, QuantR); static uint getFramerate (QuantR, TimeValue const&); + static void rangeLimitStrategy (SmpteTC&, int& rawHours); }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index a57106ecb..9a0ff6f27 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -34,6 +34,7 @@ using std::string; using util::unConst; using util::isSameObject; +using util::floorwrap; namespace lib { @@ -68,6 +69,7 @@ namespace time { void Smpte::rebuild (SmpteTC& tc, QuantR quantiser, TimeValue const& rawTime) { + tc.clear(); tc.frames = quantiser.gridPoint (rawTime); // will automatically wrap over to the secs, minutes and hour fields } @@ -82,7 +84,7 @@ namespace time { + tc.secs * frameRate + tc.mins * frameRate * 60 + tc.hours * frameRate * 60 * 60; - return quantiser.timeOf (gridPoint); + return quantiser.timeOf (tc.sgn * gridPoint); } /** yield the Framerate in effect at that point. @@ -108,9 +110,73 @@ namespace time { ENSURE (0 < effectiveFrames); return uint(effectiveFrames); } - + + + /** handle the limits of SMPTE timecode range. + * This is an extension and configuration point to control how + * to handle values beyond the official SMPTE timecode range of + * 0:0:0:0 to 23:59:59:##. When this strategy function is invoked, + * the frames, seconds and minutes fields have already been processed + * under the assumption the overall value stays in range. After returning + * from this strategy function, the rawHours value will be returned to be + * stored into the hours field without any further adjustments. + * @note currently the range is extended "naturally" (i.e. mathematically). + * The representation is flipped around the zero point and the value + * of the hours is just allowed to increase beyond 23 + * @todo If necessary, this extension point should be converted into a + * configurable strategy. Possible variations + * - clip values beyond the boundaries + * - wrap around from 23:59:59:## to 0:0:0:0 + * - just make the hour negative, but continue with the same + * orientation (0:0:0:0 - 1sec = -1:59:59:0) + */ + void + Smpte::rangeLimitStrategy (SmpteTC& tc, int& rawHours) + { + if ((rawHours^tc.sgn) >= 0) return; + + tc.sgn = rawHours; // transfer sign into the sign field + rawHours = abs(rawHours); + + REQUIRE (0 <= tc.frames && uint(tc.frames) < tc.getFps()); + REQUIRE (0 <= tc.secs && tc.secs < 60 ); + REQUIRE (0 <= tc.mins && tc.mins < 60 ); + + // sign flip was detected: + // switch orientation of all timecode fields + // i.e. -h + (m+s+f) becomes - (h+m+s+f) + uint fr (tc.getFps() - tc.frames); + uint secs (60 - tc.secs); + uint mins (60 - tc.mins); + + ASSERT (fr <= tc.getFps()); + ASSERT (0 < secs); + if (fr < tc.getFps()) + --secs; + else + fr = 0; + + ASSERT (secs <= 60); + ASSERT (0 < mins); + if (secs < 60) + --mins; + else + secs = 0; + + ASSERT (mins <= 60); + ASSERT (0 < rawHours); + if (mins < 60) + --rawHours; + else + mins = 0; + + tc.frames.setValueRaw (fr); + tc.secs.setValueRaw (secs); + tc.mins.setValueRaw (mins); + } } + namespace { // Timecode implementation details typedef util::IDiv Div; @@ -118,7 +184,7 @@ namespace time { int wrapFrames (SmpteTC* thisTC, int rawFrames) { - Div scaleRelation(rawFrames, thisTC->getFps()); + Div scaleRelation = floorwrap (rawFrames, thisTC->getFps()); thisTC->secs += scaleRelation.quot; return scaleRelation.rem; } @@ -126,7 +192,7 @@ namespace time { int wrapSeconds (SmpteTC* thisTC, int rawSecs) { - Div scaleRelation(rawSecs, 60); + Div scaleRelation = floorwrap (rawSecs, 60); thisTC->mins += scaleRelation.quot; return scaleRelation.rem; } @@ -134,7 +200,7 @@ namespace time { int wrapMinutes (SmpteTC* thisTC, int rawMins) { - Div scaleRelation(rawMins, 60); + Div scaleRelation = floorwrap (rawMins, 60); thisTC->hours += scaleRelation.quot; return scaleRelation.rem; } @@ -142,7 +208,7 @@ namespace time { int wrapHours (SmpteTC* thisTC, int rawHours) { - thisTC->sgn = rawHours; + format::Smpte::rangeLimitStrategy (*thisTC, rawHours); return rawHours; } @@ -229,10 +295,21 @@ namespace time { void - SmpteTC::rebuild() const + SmpteTC::clear() + { + frames.setValueRaw(0); + secs.setValueRaw (0); + mins.setValueRaw (0); + hours.setValueRaw (0); + sgn.setValueRaw (+1); + } + + + void + SmpteTC::rebuild() { TimeValue point = Format::evaluate (*this, *quantiser_); - Format::rebuild (unConst(*this), *quantiser_, point); + Format::rebuild (*this, *quantiser_, point); } diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index faaf00adb..8392aed9f 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -120,9 +120,11 @@ namespace time { SmpteTC (SmpteTC const&); SmpteTC& operator= (SmpteTC const&); - void rebuild() const; uint getFps() const; + void clear(); + void rebuild(); + HourDigit hours; SexaDigit mins; SexaDigit secs; diff --git a/tests/40components.tests b/tests/40components.tests index 3020d34c7..2b426a360 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -674,7 +674,8 @@ return: 0 END -PLANNED "Time formats and timecodes" TimeFormats_test <
-
+
The handling of [[Timecode]] is closely related to [[time representation and quantisation|TimeQuant]]. In fact, these two topics blend into one another. Time will be quantised into a //grid,// but this grid only makes sense when linked to a externally relevant meaning and representation, which is the Timecode. But a timecode value also denotes a specific point in time -- performing operations on a timecode is equivalent to manipulating a quantised time value.
 
 !Problem of dependencies
@@ -6773,7 +6773,24 @@ And last but not least, it is possible to get a new ~TimeValue, reflecting the c
 * @@color:green;✔@@ determine the primitives which need to be on the //effective quantiser API.//
 * @@color:green;✔@@ find out how a format can address the individual components it's comprised of &rarr; direct access by concrete type
 * @@color:green;✔@@ decide how a concrete TC value can refer to his quantiser &rarr; always using smart-ptr
-* @@color:red;??@@ maybe coin a //value handle// -- to tie the three required parts together
+* @@color:green;✔@@ decide on how to handle wrap-around and negative values &rarr; by strategy
+
+!negative values and wrap-around
+Many timecode formats were defined in the era of analogue media -- typically the key point of a timecode format was the way it can be encoded into some nits and bits within the available channel bandwidth. Often this causes the use of fixed field length representations, imposing hard limits on the number range. Adhering strictly to such ancient specifications in the context of a general purpose computer program usually results in lots of additional complexity without a fundamental reason.
+
+When in doubt, for the design of Lumiera we tend to err for the basic mathematical definition. For time values this means to use an practically unlimited time axis with an arbitrary time origin. Under this assumptions, negative time values are just natural and will be handled without case distinctions. This way, at least the core is kept simple and straight forward. Which leaves us with some of the aforementioned additional complexities showing up when it comes to interfacing with an existing timecode format. Especially, the following points need to be clarified:
+* wrap-around: when a timecode component (e.g. the seconds) exceeds the defined range, this creates a propagation (e.g. to the minutes) and a remainder (wrapped seconds value).
+* range limitation: what happens when an internal time value exceeds the range of possible timecode values? (e.g. what is 23:59:59 + 70 frames?)
+* how to extend the definition to negative values, if applicable.
+
+Especially ''SMPTE Timecode'' is limited to the range from 0:0:0:0 to 23:59:59:##.
+But because for Lumiera the SMPTE format is just a presentation rule applied to a more orthogonal internal repesentation, we could think of extending the allowed values...
+# by just letting the hours increase arbitrarily
+# by letting the timecode wrap from 23:59:59:## to 0:0:0:0 and treat this junction as continuous (effectively a "modulo 1 day").
+# by making the hours-field signed, but otherwise contain the same wrapping scheme (0:0:0:0 - 1sec = -1:59:59:0)
+# by a representation symmetrical to the zero point (0:0:0:0 - 1sec = -0:0:1:0) -- but beware: //the underlying frame quantisation won't flip//
+Because this decision is considered arbitrary and any reasoning will be context dependant, the decision is to provide a hook for a //strategy// --
+Currently (1/11), the strategy is implemented according to (1) and (4) above, leaving the actual configuration of a strategy for later.
 
From 14f233f83bd513d9707097316b407c5a8a42a659 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 22 Jan 2011 02:35:58 +0100 Subject: [PATCH 095/140] WIP: some more test coverage ... unveiling yet more bugs --- src/lib/time/timecode.cpp | 4 ++-- src/lib/time/timevalue.hpp | 1 + tests/lib/time/time-formats-test.cpp | 26 +++++++++++++++++++++++++- tests/lib/time/time-value-test.cpp | 1 + wiki/renderengine.html | 4 +++- 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 9a0ff6f27..7b33f6521 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -339,14 +339,14 @@ namespace time { SmpteTC& SmpteTC::operator++ () { - ++frames; + frames += sgn; return *this; } SmpteTC& SmpteTC::operator-- () { - --frames; + frames -= sgn; return *this; } diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 0778022a7..f8407717f 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -273,6 +273,7 @@ namespace time { /** convenience start for time calculations */ TimeVar operator+ (TimeValue const& tval) const { return TimeVar(*this) + tval; } TimeVar operator- (TimeValue const& tval) const { return TimeVar(*this) - tval; } + TimeVar operator- () const { return -TimeVar(*this); } }; diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index b56fac95e..79f5293d2 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -137,9 +137,33 @@ namespace test{ smpte.hours -= 6; CHECK ("- 0:21:59:24"== string(smpte)); // representation is symmetrical to origin CHECK (tx - Time(6*60*60) == smpte.getTime()); // Continuous time axis - CHECK (-1 == smpte.sgn); + + CHECK (-1 == smpte.sgn); // Note: for these negative (extended) SMPTE... + CHECK (smpte.mins > 0); // ...the representation is really flipped around zero + CHECK (smpte.secs > 0); + CHECK (smpte.frames > 0); + tx = smpte.getTime(); + ++smpte.frames; // now *increasing* the frame value + CHECK ("- 0:22:00:00"== string(smpte)); // means decreasing the resulting time + CHECK (tx - Time(1000/25,0,0,0) == smpte.getTime()); + ++smpte; // but the orientation of the increment on the *whole* TC values is unaltered + CHECK ("- 0:21:59:24"== string(smpte)); // so this actually *advanced* time by one frame + CHECK (tx == smpte.getTime()); + CHECK (tx < Time(0)); + + smpte.mins -= 2*60; // now lets flip it again... + CHECK (" 1:38:00:01"== string(smpte)); + CHECK (+1 == smpte.sgn); + CHECK (smpte.getTime() > 0); + CHECK (tx + Time(2*60*60) == smpte.getTime()); + smpte.secs -= 2*60*60; // and again... + CHECK (tx == smpte.getTime()); + CHECK ("- 0:21:59:24"== string(smpte)); + smpte.sgn += 123; // just flip the sign CHECK (" 0:21:59:24"== string(smpte)); + CHECK (tx == -smpte.getTime()); + CHECK (+1 == smpte.sgn); // sign value is limited to +1 / -1 smpte.secs.setValueRaw(61); // set "wrong" value, bypassing normalisation CHECK (smpte.secs == 61); diff --git a/tests/lib/time/time-value-test.cpp b/tests/lib/time/time-value-test.cpp index a1533ca67..9a1383e8b 100644 --- a/tests/lib/time/time-value-test.cpp +++ b/tests/lib/time/time-value-test.cpp @@ -190,6 +190,7 @@ namespace test{ CHECK (th+th == t1); CHECK (t1-th == th); CHECK (((t1-th)*=2) == t1); + CHECK (th-th == Time(0)); // that was indeed a temporary and didn't affect the originals CHECK (t1 == TimeValue(GAVL_TIME_SCALE)); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 9bfb2a429..10923eb49 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6746,7 +6746,7 @@ Question is: how fine grained and configurable needs this to be?
-
+
The handling of [[Timecode]] is closely related to [[time representation and quantisation|TimeQuant]]. In fact, these two topics blend into one another. Time will be quantised into a //grid,// but this grid only makes sense when linked to a externally relevant meaning and representation, which is the Timecode. But a timecode value also denotes a specific point in time -- performing operations on a timecode is equivalent to manipulating a quantised time value.
 
 !Problem of dependencies
@@ -6791,6 +6791,8 @@ But because for Lumiera the SMPTE format is just a presentation rule applied to
 # by a representation symmetrical to the zero point (0:0:0:0 - 1sec = -0:0:1:0) -- but beware: //the underlying frame quantisation won't flip//
 Because this decision is considered arbitrary and any reasoning will be context dependant, the decision is to provide a hook for a //strategy// --
 Currently (1/11), the strategy is implemented according to (1) and (4) above, leaving the actual configuration of a strategy for later.
+!!{{red{WIP 1/11}}}BUG
+Implementation of this strategy is still broken: it doesn't work properly when actually the change passing over the zero point happens by propagation from lower digits. Because then -- given the way the mutators are implemented -- the //new value of the wrapping digit hasn't been stored.// It seems the only sensible solution is to change the definition of the functors, so that any value will be changed by side-effect
 
From ac9e9a99dfbea75d08325b4573623fd3f0345011 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 22 Jan 2011 14:04:43 +0100 Subject: [PATCH 096/140] change the way the Digxel mutator is invoked this refactoring prepares a change to address the problems with negative extended SMPTE Timecode --- src/lib/time/digxel.hpp | 53 ++++++++----- src/lib/time/timecode.cpp | 8 +- tests/40components.tests | 3 +- tests/lib/time/digxel-test.cpp | 140 +++++++++++++++++++++++---------- 4 files changed, 138 insertions(+), 66 deletions(-) diff --git a/src/lib/time/digxel.hpp b/src/lib/time/digxel.hpp index 95814bd37..53aac3456 100644 --- a/src/lib/time/digxel.hpp +++ b/src/lib/time/digxel.hpp @@ -195,6 +195,11 @@ namespace time { + using std::tr1::bind; + using std::tr1::function; + using std::tr1::placeholders::_1; + + /** * A number element for building structured numeric displays. * The purpose is to represent parts of a numeric format, like @@ -224,23 +229,35 @@ namespace time { FMT buffer_; NUM value_; - static NUM use_newValue_as_is (NUM n) { return n; } - typedef std::tr1::function _Mutator; + typedef Digxel _Digxel; + typedef function _Mutator; + + _Mutator mutator; ///< Functor for setting a new digxel value + public: - /** a functor to be applied on any new digxel value. + /** install an external functor to be applied on any new digxel value. * This allows individual instances to limit the possible digxel values, * or to update an compound value (e.g. a time comprised of hour, minute - * and second digxel elements). By default, new values can be set without - * any restrictions or side effects. + * and second digxel elements). The installed functor needs to accept + * a "this" pointer and actually perform any desired state change + * as sideeffect. The default is to accept any value as-is. + * @warning using a mutator creates significant overhead; + * measurements indicate a factor of 4 + * @see Digxel_test */ - _Mutator mutator; + template + void + installMutator (FUN mutate, THIS& self) + { + mutator = bind (mutate, &self, _1 ); + } Digxel () : buffer_() , value_ () - , mutator(use_newValue_as_is) + , mutator() { } // using the standard copy operations @@ -262,8 +279,10 @@ namespace time { operator= (NUM n) { if (n == value_) return; - NUM changedValue = mutator(n); - this->setValueRaw (changedValue); + if (mutator) + mutator (n); + else + setValueRaw (n); } void @@ -305,24 +324,22 @@ namespace time { class Signum : public Digxel { - static int - just_the_sign (int val) + typedef Digxel _Par; + + void + storeSign (int val) { - return val<0? -1:+1; + setValueRaw (val<0? -1:+1); } public: Signum() { setValueRaw(1); - mutator = just_the_sign; + installMutator (&Signum::storeSign, *this); } - void - operator= (int n) - { - this->setValueRaw (mutator(n)); - } + using _Par::operator=; friend int operator*= (Signum& s, int c) { s = c*s; return s; } }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 7b33f6521..8774d9f1a 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -221,10 +221,10 @@ namespace time { inline void setupComponentNormalisation (SmpteTC * thisTC) { - thisTC->hours.mutator = bind (wrapHours, thisTC, _1 ); - thisTC->mins.mutator = bind (wrapMinutes, thisTC, _1 ); - thisTC->secs.mutator = bind (wrapSeconds, thisTC, _1 ); - thisTC->frames.mutator = bind (wrapFrames, thisTC, _1 ); +// thisTC->hours.mutator = bind (wrapHours, thisTC, _1 ); +// thisTC->mins.mutator = bind (wrapMinutes, thisTC, _1 ); +// thisTC->secs.mutator = bind (wrapSeconds, thisTC, _1 ); +// thisTC->frames.mutator = bind (wrapFrames, thisTC, _1 ); } }//(End)implementation details diff --git a/tests/40components.tests b/tests/40components.tests index 2b426a360..b22c6025d 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -234,8 +234,7 @@ END TEST "A Digxel (numeric component)" Digxel_test < #include +#include +#include using lumiera::error::LUMIERA_ERROR_ASSERTION; using util::isSameObject; @@ -66,35 +67,6 @@ namespace test{ /* === special Digxel configuration for this test === */ - double sum(0), - checksum(0); - - double - sideeffectSum (double val) - { - sum += val; - return val; - } - - double preval(0), newval(0); - - double - protocollingMutator (double val) - { - preval = newval; - newval = val; - return val; - } - - double - limitingMutator (double value2set) - { - return (+1 < value2set) ? 1.0 - : (-1 > value2set) ? -1.0 - : value2set; - } - - struct VerySpecialFormat : digxel::PrintfFormatter { @@ -103,6 +75,47 @@ namespace test{ typedef Digxel TestDigxel; + + double sum(0), + checksum(0); + + void + sideeffectSum (TestDigxel* digxel, double val) + { + sum += val; + digxel->setValueRaw (val); + } + + double preval(0), newval(0); + + void + protocollingMutator (TestDigxel* digxel, double val) + { + preval = newval; + newval = val; + digxel->setValueRaw (val); + } + + void + limitingMutator (TestDigxel* digxel, double value2set) + { + digxel->setValueRaw ((+1 < value2set) ? +1.0 + : (-1 > value2set) ? -1.0 + : value2set); + } + + void + trivialMutator (TestDigxel* digxel, double value2set) + { + digxel->setValueRaw (value2set); + } + + void + emptyMutator (TestDigxel*, double) + { + /* do nothing */ + } + }//(End)Test setup @@ -158,7 +171,7 @@ namespace test{ TestDigxel digi; // configure what the Digxel does on "mutation" - digi.mutator = sideeffectSum; + digi.installMutator (sideeffectSum, digi); CHECK (0 == digi); sum = checksum = 0; @@ -187,7 +200,7 @@ namespace test{ CHECK (12.3 == digi); // a special mutator to limit the value - digi.mutator = limitingMutator; + digi.installMutator (limitingMutator, digi); CHECK (12.3 == digi); digi = 12.3; CHECK (12.3 == digi); // triggered on real change only @@ -215,7 +228,7 @@ namespace test{ verifyAssignMutatingOperators () { TestDigxel digi; - digi.mutator = protocollingMutator; + digi.installMutator (protocollingMutator, digi); digi = 12.3; CHECK ( 0.0 == preval && 12.3 == newval); @@ -312,7 +325,7 @@ namespace test{ * we'll take some timings. * @warning the results of such tests could be unreliable, * but in this case here I saw a significant difference, - * with values of about 0.1sec / 0.7sec */ + * with values of about 10ns / 45ns */ void verifyDisplayCaching () { @@ -320,26 +333,69 @@ namespace test{ digi = 1; clock_t start(0), stop(0); - start = clock(); + boost::format resultDisplay("timings(%s)%|36T.|%4.0fns\n"); + +#define START_TIMINGS start=clock(); +#define DISPLAY_TIMINGS(ID)\ + stop = clock(); \ + uint ID = stop-start;\ + cout << resultDisplay % STRINGIFY (ID) % (double(ID)/CLOCKS_PER_SEC/TIMING_CNT*1e9) ; + + + START_TIMINGS + for (uint i=0; i < TIMING_CNT; ++i) + { + isOdd (i); + } + DISPLAY_TIMINGS (empty_loop) + + + START_TIMINGS for (uint i=0; i < TIMING_CNT; ++i) { digi = 1; isOdd (i); } - stop = clock(); - uint without_reformatting = stop - start; + DISPLAY_TIMINGS (without_reformatting) - start = clock(); + START_TIMINGS for (uint i=0; i < TIMING_CNT; ++i) { digi = isOdd (i); } - stop = clock(); - uint with_reformatting = stop - start; + DISPLAY_TIMINGS (with_reformatting) + + + digi.installMutator (emptyMutator, digi); + + START_TIMINGS + for (uint i=0; i < TIMING_CNT; ++i) + { + digi = isOdd (i); + } + DISPLAY_TIMINGS (with_empty_mutator) + + + digi.installMutator (trivialMutator, digi); + + START_TIMINGS + for (uint i=0; i < TIMING_CNT; ++i) + { + digi = isOdd (i); + } + DISPLAY_TIMINGS (with_trivial_mutator) + + + digi.installMutator (&TestDigxel::setValueRaw, digi); + + START_TIMINGS + for (uint i=0; i < TIMING_CNT; ++i) + { + digi = isOdd (i); + } + DISPLAY_TIMINGS (with_memfun_mutator) - cout << "without reformatting = "<< double(without_reformatting)/CLOCKS_PER_SEC <<"sec"<< endl; - cout << "with reformatting = "<< double(with_reformatting )/CLOCKS_PER_SEC <<"sec"<< endl; CHECK (without_reformatting < with_reformatting); } From acc7a19fbd4db036dbb0aa5eb5ab693eb7e058ac Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 22 Jan 2011 14:41:58 +0100 Subject: [PATCH 097/140] Fix: now able to handle negative extended SMPTE changed the order of propagation when wrapping the individual digxels of the SMPTE Timecode --- src/lib/time/formats.hpp | 2 +- src/lib/time/timecode.cpp | 55 ++++++++++++++-------------- tests/lib/time/time-formats-test.cpp | 2 +- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index 05adaaecb..abe07eca4 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -84,7 +84,7 @@ namespace time { static void rebuild (SmpteTC&, QuantR, TimeValue const&); static TimeValue evaluate (SmpteTC const&, QuantR); static uint getFramerate (QuantR, TimeValue const&); - static void rangeLimitStrategy (SmpteTC&, int& rawHours); + static void rangeLimitStrategy (SmpteTC&, int rawHours); }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 8774d9f1a..070e8d1eb 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -116,27 +116,27 @@ namespace time { * This is an extension and configuration point to control how * to handle values beyond the official SMPTE timecode range of * 0:0:0:0 to 23:59:59:##. When this strategy function is invoked, - * the frames, seconds and minutes fields have already been processed - * under the assumption the overall value stays in range. After returning - * from this strategy function, the rawHours value will be returned to be - * stored into the hours field without any further adjustments. + * the frames, seconds, minutes and hours fields have already been processed + * and stored into the component digxels, under the assumption the overall + * value stays in range. * @note currently the range is extended "naturally" (i.e. mathematically). * The representation is flipped around the zero point and the value * of the hours is just allowed to increase beyond 23 * @todo If necessary, this extension point should be converted into a * configurable strategy. Possible variations * - clip values beyond the boundaries + * - throw an exception on illegal values * - wrap around from 23:59:59:## to 0:0:0:0 * - just make the hour negative, but continue with the same * orientation (0:0:0:0 - 1sec = -1:59:59:0) */ void - Smpte::rangeLimitStrategy (SmpteTC& tc, int& rawHours) + Smpte::rangeLimitStrategy (SmpteTC& tc, int hours) { - if ((rawHours^tc.sgn) >= 0) return; + if (hours >= 0) return; // no need to flip representation - tc.sgn = rawHours; // transfer sign into the sign field - rawHours = abs(rawHours); + tc.sgn *= hours; // transfer sign into the sign field + hours = abs(hours); REQUIRE (0 <= tc.frames && uint(tc.frames) < tc.getFps()); REQUIRE (0 <= tc.secs && tc.secs < 60 ); @@ -164,15 +164,16 @@ namespace time { secs = 0; ASSERT (mins <= 60); - ASSERT (0 < rawHours); + ASSERT (0 < hours); if (mins < 60) - --rawHours; + --hours; else mins = 0; tc.frames.setValueRaw (fr); - tc.secs.setValueRaw (secs); - tc.mins.setValueRaw (mins); + tc.secs.setValueRaw (secs); + tc.mins.setValueRaw (mins); + tc.hours.setValueRaw (hours); } } @@ -181,35 +182,35 @@ namespace time { typedef util::IDiv Div; - int + void wrapFrames (SmpteTC* thisTC, int rawFrames) { Div scaleRelation = floorwrap (rawFrames, thisTC->getFps()); + thisTC->frames.setValueRaw (scaleRelation.rem); thisTC->secs += scaleRelation.quot; - return scaleRelation.rem; } - int + void wrapSeconds (SmpteTC* thisTC, int rawSecs) { Div scaleRelation = floorwrap (rawSecs, 60); + thisTC->secs.setValueRaw (scaleRelation.rem); thisTC->mins += scaleRelation.quot; - return scaleRelation.rem; } - int + void wrapMinutes (SmpteTC* thisTC, int rawMins) { Div scaleRelation = floorwrap (rawMins, 60); + thisTC->mins.setValueRaw (scaleRelation.rem); thisTC->hours += scaleRelation.quot; - return scaleRelation.rem; } - int + void wrapHours (SmpteTC* thisTC, int rawHours) { + thisTC->hours.setValueRaw (rawHours); format::Smpte::rangeLimitStrategy (*thisTC, rawHours); - return rawHours; } @@ -219,12 +220,12 @@ namespace time { /** bind the individual Digxel mutation functors * to normalise raw component values */ inline void - setupComponentNormalisation (SmpteTC * thisTC) + setupComponentNormalisation (SmpteTC& thisTC) { -// thisTC->hours.mutator = bind (wrapHours, thisTC, _1 ); -// thisTC->mins.mutator = bind (wrapMinutes, thisTC, _1 ); -// thisTC->secs.mutator = bind (wrapSeconds, thisTC, _1 ); -// thisTC->frames.mutator = bind (wrapFrames, thisTC, _1 ); + thisTC.hours.installMutator (wrapHours, thisTC); + thisTC.mins.installMutator (wrapMinutes, thisTC); + thisTC.secs.installMutator (wrapSeconds, thisTC); + thisTC.frames.installMutator(wrapFrames, thisTC); } }//(End)implementation details @@ -244,7 +245,7 @@ namespace time { : TCode(quantisedTime) , effectiveFramerate_(Format::getFramerate (*quantiser_, quantisedTime)) { - setupComponentNormalisation (this); + setupComponentNormalisation (*this); quantisedTime.castInto (*this); } @@ -253,7 +254,7 @@ namespace time { : TCode(o) , effectiveFramerate_(o.effectiveFramerate_) { - setupComponentNormalisation (this); + setupComponentNormalisation (*this); sgn = o.sgn; hours = o.hours; mins = o.mins; diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index 79f5293d2..368e76524 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -151,7 +151,7 @@ namespace test{ CHECK (tx == smpte.getTime()); CHECK (tx < Time(0)); - smpte.mins -= 2*60; // now lets flip it again... + smpte.mins -= 2*60; // now lets flip the representation again... CHECK (" 1:38:00:01"== string(smpte)); CHECK (+1 == smpte.sgn); CHECK (smpte.getTime() > 0); From 69eb659d4e5676fe10fb925bf679a9535239309c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 22 Jan 2011 18:33:15 +0100 Subject: [PATCH 098/140] refactor to extract a special "flip representation" operation --- src/lib/time/formats.hpp | 2 +- src/lib/time/timecode.cpp | 83 ++++++++++++++++++--------------------- src/lib/time/timecode.hpp | 3 ++ 3 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/lib/time/formats.hpp b/src/lib/time/formats.hpp index abe07eca4..b82a8f631 100644 --- a/src/lib/time/formats.hpp +++ b/src/lib/time/formats.hpp @@ -84,7 +84,7 @@ namespace time { static void rebuild (SmpteTC&, QuantR, TimeValue const&); static TimeValue evaluate (SmpteTC const&, QuantR); static uint getFramerate (QuantR, TimeValue const&); - static void rangeLimitStrategy (SmpteTC&, int rawHours); + static void rangeLimitStrategy (SmpteTC&); }; diff --git a/src/lib/time/timecode.cpp b/src/lib/time/timecode.cpp index 070e8d1eb..dd4b67015 100644 --- a/src/lib/time/timecode.cpp +++ b/src/lib/time/timecode.cpp @@ -71,7 +71,7 @@ namespace time { { tc.clear(); tc.frames = quantiser.gridPoint (rawTime); - // will automatically wrap over to the secs, minutes and hour fields + // will automatically wrap over to the seconds, minutes and hour fields } /** calculate the time point denoted by this SMPTE timecode, @@ -79,7 +79,7 @@ namespace time { TimeValue Smpte::evaluate (SmpteTC const& tc, QuantR quantiser) { - uint frameRate = tc.getFps(); + long frameRate = tc.getFps(); long gridPoint = tc.frames + tc.secs * frameRate + tc.mins * frameRate * 60 @@ -131,49 +131,10 @@ namespace time { * orientation (0:0:0:0 - 1sec = -1:59:59:0) */ void - Smpte::rangeLimitStrategy (SmpteTC& tc, int hours) + Smpte::rangeLimitStrategy (SmpteTC& tc) { - if (hours >= 0) return; // no need to flip representation - - tc.sgn *= hours; // transfer sign into the sign field - hours = abs(hours); - - REQUIRE (0 <= tc.frames && uint(tc.frames) < tc.getFps()); - REQUIRE (0 <= tc.secs && tc.secs < 60 ); - REQUIRE (0 <= tc.mins && tc.mins < 60 ); - - // sign flip was detected: - // switch orientation of all timecode fields - // i.e. -h + (m+s+f) becomes - (h+m+s+f) - uint fr (tc.getFps() - tc.frames); - uint secs (60 - tc.secs); - uint mins (60 - tc.mins); - - ASSERT (fr <= tc.getFps()); - ASSERT (0 < secs); - if (fr < tc.getFps()) - --secs; - else - fr = 0; - - ASSERT (secs <= 60); - ASSERT (0 < mins); - if (secs < 60) - --mins; - else - secs = 0; - - ASSERT (mins <= 60); - ASSERT (0 < hours); - if (mins < 60) - --hours; - else - mins = 0; - - tc.frames.setValueRaw (fr); - tc.secs.setValueRaw (secs); - tc.mins.setValueRaw (mins); - tc.hours.setValueRaw (hours); + if (tc.hours < 0) + tc.invertOrientation(); } } @@ -210,7 +171,7 @@ namespace time { wrapHours (SmpteTC* thisTC, int rawHours) { thisTC->hours.setValueRaw (rawHours); - format::Smpte::rangeLimitStrategy (*thisTC, rawHours); + format::Smpte::rangeLimitStrategy (*thisTC); } @@ -314,6 +275,38 @@ namespace time { } + /** flip the orientation of min, sec, and frames. + * Besides changing the sign, this will flip the + * meaning of the component fields, which by + * definition are always oriented towards zero. + * + * Normalised value fields are defined positive, + * with automatic overflow to next higher field. + * This might cause the hours to become negative. + * When invoked in this case, the meaning changes + * from -h + (m+s+f) to -(h+m+s+f) + */ + void + SmpteTC::invertOrientation() + { + int fr (getFps()); + int f (fr - frames); // revert orientation + int s (60 - secs); // of the components + int m (60 - mins); // + int h = -hours; // assumed to be negative + sgn *= -1; // flip sign field + + if (f < fr) --s; else f -= fr; + if (s < 60) --m; else s -= 60; + if (m < 60) --h; else m -= 60; + + hours.setValueRaw(h); + mins = m; // invoking setters + secs = s; // ensures normalisation + frames = f; + } + + uint SmpteTC::getFps() const { diff --git a/src/lib/time/timecode.hpp b/src/lib/time/timecode.hpp index 8392aed9f..3b5f320a8 100644 --- a/src/lib/time/timecode.hpp +++ b/src/lib/time/timecode.hpp @@ -113,6 +113,7 @@ namespace time { virtual Literal tcID() const { return "SMPTE"; } virtual TimeValue value() const { return Format::evaluate (*this, *quantiser_); } + public: typedef format::Smpte Format; @@ -124,6 +125,8 @@ namespace time { void clear(); void rebuild(); + void invertOrientation(); + HourDigit hours; SexaDigit mins; From 7b783e885ae9cfe1fa020635a0748a3cdc9bac99 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 22 Jan 2011 18:35:04 +0100 Subject: [PATCH 099/140] more thorhoug testing of denormalised values ...uncovered yet more bugs.. yay! --- tests/lib/time/time-formats-test.cpp | 42 ++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index 368e76524..beda7e5af 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -105,6 +105,16 @@ namespace test{ } + /** @test detailed coverage of SMPTE timecode representation. + * Using a scale grid with PAL framerate; this test doesn't + * cover the handling of drop-frame timecode. + * - creating a timecode representation causes frame quantisation + * - the components of SMPTE timecode can be accessed and manipulated + * - timecode can be incremented/decremented as a whole + * - we allow extension of the scale towards negative values + * - for these, the representation is flipped and the negative + * orientation only indicated through the sign field. + */ void checkSmpte () { @@ -114,7 +124,7 @@ namespace test{ showTimeCode(smpte); CHECK (" 5:42:23:13" == string(smpte)); - CHECK (raw - Time(35,0) == smpte.getTime()); // quantisation to next lower frame + CHECK (raw - Time(35,0) == smpte.getTime()); // timecode value got quantised towards next lower frame CHECK (13 == smpte.frames); CHECK (23 == smpte.secs); CHECK (42 == smpte.mins); @@ -132,8 +142,10 @@ namespace test{ CHECK (--smpte.mins == 38); CHECK (" 5:38:00:01" == string(smpte)); - // extension beyond origin to negative values Time tx = smpte.getTime(); + CHECK (tx == Time(0,0,38,5) + Time(FSecs(1,25))); + + // Extended SMPTE: extension of the axis beyond origin towards negative values smpte.hours -= 6; CHECK ("- 0:21:59:24"== string(smpte)); // representation is symmetrical to origin CHECK (tx - Time(6*60*60) == smpte.getTime()); // Continuous time axis @@ -159,7 +171,7 @@ namespace test{ smpte.secs -= 2*60*60; // and again... CHECK (tx == smpte.getTime()); CHECK ("- 0:21:59:24"== string(smpte)); - + smpte.sgn += 123; // just flip the sign CHECK (" 0:21:59:24"== string(smpte)); CHECK (tx == -smpte.getTime()); @@ -174,6 +186,30 @@ namespace test{ CHECK (smpte.secs == 1); CHECK (smpte.mins == 22); CHECK (" 0:22:01:24"== string(smpte)); + + smpte.frames.setValueRaw (25); + CHECK (" 0:22:01:25"== string(smpte)); + smpte.hours = -1; // flipped representation handles denormalised values properly + CHECK ("- 0:37:58:00"== string(smpte)); + + smpte.mins.setValueRaw (59); + smpte.secs.setValueRaw (61); + smpte.frames.setValueRaw(-26); // provoke multiple over/underflows... + smpte.hours.setValueRaw (-2); + CHECK ("--2:59:61:-26"==string(smpte)); + tx = smpte.getTime(); + CHECK (tx == -1*(Time(0,61,59) - Time(0,0,0,2) - Time(FSecs(26,25)))); + smpte.invertOrientation(); + CHECK (" 1:00:00:01"== string(smpte)); + CHECK (tx == smpte.getTime()); // applying invertOrientation() doesn't change the value + + smpte.frames.setValueRaw(-1); + tx = tx - Time(FSecs(2,25)); + CHECK (tx == smpte.getTime()); + CHECK (" 1:00:00:-1"== string(smpte)); + smpte.invertOrientation(); // invoking on positive should create double negated representation + CHECK ("--1:00:00:01"== string(smpte)); // and here especially also causes a series of overflows + CHECK (tx == smpte.getTime()); // but without affecting the overall value } From dea026cfd96ef7bfc9bb52afcc360a9df3a40051 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 22 Jan 2011 18:44:03 +0100 Subject: [PATCH 100/140] omit the Digxel timing measurments from test suite --- tests/lib/time/digxel-test.cpp | 37 ++++++++++++++++++-------------- tests/lib/util-floordiv-test.cpp | 3 ++- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/tests/lib/time/digxel-test.cpp b/tests/lib/time/digxel-test.cpp index 470f1b228..b99656233 100644 --- a/tests/lib/time/digxel-test.cpp +++ b/tests/lib/time/digxel-test.cpp @@ -33,6 +33,7 @@ using lumiera::error::LUMIERA_ERROR_ASSERTION; using util::isSameObject; +using util::isnil; using std::rand; using std::cout; using std::endl; @@ -137,16 +138,18 @@ namespace test{ class Digxel_test : public Test { virtual void - run (Arg) + run (Arg arg) { - checkSimpleUsage (); + checkSimpleUsage(); checkMutation (); - verifyMutatorInfluence (); - verifyAssignMutatingOperators (); - verifyComparisons (); + verifyMutatorInfluence(); + verifyAssignMutatingOperators(); + verifyComparisons(); checkCopy (); - checkDisplayOverrun (); - verifyDisplayCaching (); + checkDisplayOverrun(); + + if (!isnil (arg)) + timingMeasurements(); } @@ -319,15 +322,17 @@ namespace test{ } - /** @test verify caching of formatted values. - * Digxel avoids reformatting unchanged values; - * to verify the effectivity of this measure, - * we'll take some timings. - * @warning the results of such tests could be unreliable, - * but in this case here I saw a significant difference, - * with values of about 10ns / 45ns */ + /** @test perform several timing measurements and + * especially verify the effect of caching formatted values. + * Digxel avoids reformatting unchanged values; besides that + * it is possible to install a "mutator" functor for invoking + * all kinds of special behaviour on value changes. Of course + * doing so comes with a (considerable) price tag.... + * @note the measurement especially show the effects of + * locality, which can vary largely over several runs. + */ void - verifyDisplayCaching () + timingMeasurements () { TestDigxel digi; digi = 1; @@ -335,7 +340,7 @@ namespace test{ clock_t start(0), stop(0); boost::format resultDisplay("timings(%s)%|36T.|%4.0fns\n"); -#define START_TIMINGS start=clock(); +#define START_TIMINGS start=clock(); #define DISPLAY_TIMINGS(ID)\ stop = clock(); \ uint ID = stop-start;\ diff --git a/tests/lib/util-floordiv-test.cpp b/tests/lib/util-floordiv-test.cpp index 2d8b79a14..015faaf64 100644 --- a/tests/lib/util-floordiv-test.cpp +++ b/tests/lib/util-floordiv-test.cpp @@ -32,6 +32,7 @@ using ::Test; using std::cout; using std::rand; +using util::isnil; using boost::format; @@ -110,7 +111,7 @@ namespace test { { verifyBehaviour (); - if (arg.size()) + if (!isnil (arg)) runPerformanceTest(); } From faf579c4c5fe5680a5942595895d8722b7df7442 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 22 Jan 2011 23:20:12 +0100 Subject: [PATCH 101/140] enforce generally imutable time values --- src/lib/time/timevalue.hpp | 15 ++++++++++++++- src/proc/asset/meta/time-grid.hpp | 3 ++- tests/lib/time/time-formats-test.cpp | 6 +++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index f8407717f..f0d7fc9c8 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -169,7 +169,14 @@ namespace time { class Offset : public TimeValue { - + protected: + Offset& + operator= (Offset const& o) ///< derived classes allow mutation + { + TimeValue::operator= (o); + return *this; + } + public: explicit Offset (TimeValue const& distance) @@ -246,6 +253,9 @@ namespace time { class Time : public TimeValue { + /// direct assignment prohibited + Time& operator= (Time const); + public: static const Time MAX ; static const Time MIN ; @@ -291,6 +301,9 @@ namespace time { class Duration : public Offset { + /// direct assignment prohibited + Duration& operator= (Duration const&); + public: Duration (Offset const& distance) : Offset(distance.abs()) diff --git a/src/proc/asset/meta/time-grid.hpp b/src/proc/asset/meta/time-grid.hpp index b8e768326..5b1f0486b 100644 --- a/src/proc/asset/meta/time-grid.hpp +++ b/src/proc/asset/meta/time-grid.hpp @@ -59,6 +59,7 @@ namespace meta { using lib::Symbol; using lib::time::Time; + using lib::time::TimeVar; using lib::time::TimeValue; using lib::time::FrameRate; using lib::time::FSecs; @@ -102,7 +103,7 @@ namespace meta { string id_; FrameRate fps_; - Time origin_; + TimeVar origin_; /** when building a compound or variable grid, * the predecessor is the grid active \em before diff --git a/tests/lib/time/time-formats-test.cpp b/tests/lib/time/time-formats-test.cpp index beda7e5af..daf7f6ca1 100644 --- a/tests/lib/time/time-formats-test.cpp +++ b/tests/lib/time/time-formats-test.cpp @@ -142,7 +142,7 @@ namespace test{ CHECK (--smpte.mins == 38); CHECK (" 5:38:00:01" == string(smpte)); - Time tx = smpte.getTime(); + TimeVar tx = smpte.getTime(); CHECK (tx == Time(0,0,38,5) + Time(FSecs(1,25))); // Extended SMPTE: extension of the axis beyond origin towards negative values @@ -157,7 +157,7 @@ namespace test{ tx = smpte.getTime(); ++smpte.frames; // now *increasing* the frame value CHECK ("- 0:22:00:00"== string(smpte)); // means decreasing the resulting time - CHECK (tx - Time(1000/25,0,0,0) == smpte.getTime()); + CHECK (smpte.getTime() == tx - Time(1000/25,0,0,0)); ++smpte; // but the orientation of the increment on the *whole* TC values is unaltered CHECK ("- 0:21:59:24"== string(smpte)); // so this actually *advanced* time by one frame CHECK (tx == smpte.getTime()); @@ -204,7 +204,7 @@ namespace test{ CHECK (tx == smpte.getTime()); // applying invertOrientation() doesn't change the value smpte.frames.setValueRaw(-1); - tx = tx - Time(FSecs(2,25)); + tx -= Time(FSecs(2,25)); CHECK (tx == smpte.getTime()); CHECK (" 1:00:00:-1"== string(smpte)); smpte.invertOrientation(); // invoking on positive should create double negated representation From 587f507292783b2ed0c380e524060436f133720f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 23 Jan 2011 22:44:26 +0100 Subject: [PATCH 102/140] analysis/design: changing time values --- wiki/renderengine.html | 105 +++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 10923eb49..1985b9c83 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6618,7 +6618,78 @@ Thus no server and no network connection is needed. Simply open the file in your * see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
-
+
+
Simple time points are just like values and thus easy to change. The difficulties arise when time values are to be //quantised to an existing time grid.// The first noticeable point to note is that now the effect can be decoupled from the cause. Small changes below the threshold might be accumulated, and a tiny change might trigger a jump to the next grid point. While this might be annoying, the yet more complex questions arise when we acknowledge that the change itself might be related to a time grid.
+
+The problem with modification of quantised values highlights an inner contradiction or conflicting goals within the design
+* the whole system should fit in naturally and just feel like using raw time values
+* quantisation should be added dynamically and //late// -- like a view
+* there should be a guidance towards the intended proper use
+
+!!!general assumptions in Lumiera
+At this point, we should recall some general assumptions within Lumiera
+* there are no hard-coded defaults regarding the specific nature of the manipulated objects (format, number of channels)
+* there is not "the" timeline, but we have multiple timelines
+* sequences might be nesetd and thus be brought into a different context
+* framerate is a property of the stream type of a channel; it depends on the output. Ther is no global format or framerate.
+
+!Usage situations
+The complexity arises from the mixture of several concerns, which often blend into a single usage situation.
+;Time span is an object prototype
+:Within the context of an NLE, a time interval located at a given time point can be considered as the least common denominator of all entities to be manipulated by the user. //Manipulating// such objects is the whole point of using such an application.
+:* an object or boundary point can be //dragged//
+:* we want to //nudge// by defined amounts
+:* we want to put it "somewhere"
+;dragging
+:as far as the GUI is concerned, dragging some element creates an //offset in display coordinates.//
+:Now the task is to tranform this into a change on the object, which typically is grid-aligned
+;nudging
+:the primary interaction in this case just sends an numeric offset of ±N steps
+:but the further processing might involve a global or maybe even local //nudge amount.//
+:the recieving object is typically grid-aligned, thus nudge-grid and object-grid need to cascade, i.e. be applied in sequence
+;placing
+:an object or boundary point gets relocated to some predefined time position, which in turn can be quantised (grid aligned).
+:now either we treat this target position as a value, to be applied, possibly with re-quantisation
+:or we might treat it as a //source for time values// -- in which case the object just gets attached and consumes time coordinates produced elsewhere.
+
+Considering the analysis this far, there seems to be a common denominator in all these operations: chaining of multiple grids.
+A mutation or an increment gets applied to a grid-aligned variable, which then (maybe after transformation into a different scale) in turn is used again as a mutation to be applied to another grid-aligned variable. Now, because at this point we don't need to solve the whole bunch of wide scale design problems, it is sufficient to extract the following operations
+::value &rArr; value
+::value &rArr; quantised
+::increment &rArr; quantised
+
+This results in the following combinations to be implemented:
+|  !mutation|!receiver ||!result |
+| ~TimeValue|Time || -- |
+|~|Duration || -- |
+|~|~TimeSpan ||set start |
+|~|quantised ||set raw value |
+| Offset|Time ||offset the value |
+|~|Duration ||adjust by offset |
+|~|~TimeSpan ||adjust start/length |
+|~|quantised ||adjust raw value |
+| ~QuTime|any raw ||treat as raw |
+|~|~QuTime ||materialise, then set raw |
+| increment|~QuTime ||snap to grid point |
+As rationale, consider the following
+* immutable values are bliss. 
+* by materialising a quantised change prior to applying it, we get the "chaining effect"
+
+----------
+!!! solutions ideas{{red{WIP 12/10}}}
+* time values are immutable (as far as possible)
+* only allow to assign a completely new setting
+* only accept raw time values for redefining a quantised interval
+* only accept an abstract //modification object.//
+
+!!!usage considerations
+Question is: how fine grained and configurable needs this to be?
+* for example, when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
+* likely we need a "nudge by unit(s)"
+
+
+
+
The term &raquo;Time&laquo; spans a variety of vastly different entities. Within a NLE we get to deal with various //flavours of time values.//
 ;continuous time
 :without any additional assumptions, ''points in time'' can be specified with arbitrary precision.
@@ -6680,7 +6751,7 @@ At the level of individual timecode formats, we're lacking a common denominator;
 &rarr; Quantiser [[implementation details|QuantiserImpl]]
 
-
+
the following collection of usage situations helps to shape the details of the time values and time quantisation design. &rarr; see also  [[time quantisation|TimeQuant]]
 
 ;time position of an object
@@ -6694,15 +6765,15 @@ At the level of individual timecode formats, we're lacking a common denominator;
 ;moving and resizing an object
 :this can in itself be done in two different ways, and each of them can be applied in a quantised flavour
 :which sums up to 8 possible combinations, considering that position and length are 2 degrees of freedom.
-:* a variable can be /changed/ by an offset
-:* a variable can be /defined/ to a new value
+:* a variable can be //changed// by an offset
+:* a variable can be //defined// to a new value
 :another (hidden) degree of freedom lies in how to apply an quantised offset to an unquantised value (and reversed)
 :because this operation might be done both in the quantised or non-quantised domain, and also the result might be (un)quantised
 ;updating the playback position
-:this can be seen as a practical application of the above; basically we can choose to show the wall clock time or we can advance the playback position in frame increments, thus denoting the frame currently in display. These distinctions may look mood for video, but are relevant for precise audio editing, especially when combined with loop playback (recall that audio is processed block wise, but the individual sample frames and thus the possible loop positions are way finer than the processing block size)
+:this can be seen as a practical application of the above; basically we can choose to show the wall clock time or we can advance the playback position in frame increments, thus denoting the frame currently in display. For video, these distinctions may look moot, but they are indeed relevant for precise audio editing, especially when combined with loop playback (recall that audio is processed block wise, but the individual sample frames and thus the possible loop positions are way finer than the processing block size)
 ;displaying time intervals
 :for display, time intervals get //re-quantised// into display array coordinates.
-:While evidently the display coordinates are themselves quantised and we obviously don't want to cancel out the effect of an quantisation of the values or intervals to be displayed (which means, we get two quantisations chained up after each other), there remains the question if the display array coordinates should be aligned to the grid of the //elements to be displayed,// and especially if the allowed zoom factors should be limited. This decision isn't an easy one, as it has an immediate and tangible effect on what can be showed, how reversible and reproducible a view is and (note) on the actual values which can be set and changed through the GUI.
+:While evidently the display coordinates are themselves quantised and we obviously don't want to cancel out the effect of an quantisation of the values or intervals to be displayed (which means, we get two quantisations chained up after each other), there remains the question if the display array coordinates should be aligned to the grid of the //elements to be displayed,// and especially if the allowed zoom factors should be limited. This decision isn't an easy one, as it has an immediate and tangible effect on what can be showed, how reversible and reproducible a view is and (especially note this!) on the actual values which can be set and changed through the GUI.
 ;time value arithmetic
 :Client code as well as the internal implementation of time handling needs to do arithmetic operations with time values. Time values are additive and totally ordered. Distance, as calculated by subtraction, can be made into a metric. Another and quite different question is to what extent a quantised variant of this arithmetics is required.
 ;relative placement
@@ -6724,26 +6795,10 @@ The ''problem with playback position'' is -- that it's an attempt to conceptuali
 Note that the ''display window might be treated as just an independent instance of quantisation''. This is similar to the approach taken above for modifying quantised time span values. We should provide a special kind of time grid, the display coordinates. The origin of these is always defined to the left (lower) side of the interval to be displayed, and they are gauged in screen units (pixels or similar, as used by the GUI toolkit set). The rest is handled by the general quantisation mechanisms. The problem of aligning the display should be transformed into a general facility to align grids, and solved for the general case. Doing so solves the remaining problems with quantised value changes and with ''specifying relative placements'' as well: If we choose to represent them as quantised values, we might (or might not) also choose to apply this //grid-alignment function.//
 
 !substantial problems to be solved
-* how to align multiple grids
-* how to integrate modifications of quantised values.
-* how to isolate the Time/Quantisation part from the grid MetaAsset in the session
+* how to [[align multiple grids|TimeGridAlignment]]
+* how to integrate [[modifications of quantised values|TimeMutation]].
+* how to isolate the Time/Quantisation part from the grid MetaAsset in the session &rarr; we use the [[Advice]] system
 * how to design the relation of Timecode, Timecode formatting and Quantisation &rarr; [[more here|TimecodeFormat]]
-
-The problem with modification of quantised values highlights an inner contratiction or conflicting goals
-* the whole system should fit in naturally and just feel like using raw time values
-* quantisation should be added //late// -- like a view
-* there should be a guidance towards the intended proper use
-
-!!! possible solutions{{red{WIP 12/10}}}
-* only allow to assign a completely new setting
-* only accept raw time values for redefining a quantised interval
-* only accept an abstract //modification object.//
-
-!!!usage considerations
-Question is: how fine grained and configurable needs this to be?
-* for example, when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
-* likely we need a "nudge by unit(s)"
-
 
From 764a38abe641fb52ae7c3f20498132554fc9436a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 28 Jan 2011 23:31:00 +0100 Subject: [PATCH 103/140] SCons: experimental support for some library lookup concerns - setting -rpath with $ORIGIN to build a relocatable package - fix missing DT_SONAME (likely just a problem of very old SCons version) --- SConstruct | 10 +++---- admin/scons/LumieraEnvironment.py | 46 +++++++++++++++++++++++++++++++ src/tool/SConscript | 8 +++--- tests/SConscript | 6 ++-- 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/SConstruct b/SConstruct index eaf03c54e..958ba3328 100644 --- a/SConstruct +++ b/SConstruct @@ -337,14 +337,14 @@ def defineBuildTargets(env, artifacts): - lApp = env.SharedLibrary('$LIBDIR/lumieracommon', srcSubtree(env,'$SRCDIR/common')) - lBack = env.SharedLibrary('$LIBDIR/lumierabackend', srcSubtree(env,'$SRCDIR/backend')) - lProc = env.SharedLibrary('$LIBDIR/lumieraproc', srcSubtree(env,'$SRCDIR/proc')) - lLib = env.SharedLibrary('$LIBDIR/lumiera', srcSubtree(env,'$SRCDIR/lib')) + lLib = env.LumieraLibrary('$LIBDIR/lumiera', srcSubtree(env,'$SRCDIR/lib')) + lApp = env.LumieraLibrary('$LIBDIR/lumieracommon', srcSubtree(env,'$SRCDIR/common'), LIBS=lLib) + lBack = env.LumieraLibrary('$LIBDIR/lumierabackend', srcSubtree(env,'$SRCDIR/backend')) + lProc = env.LumieraLibrary('$LIBDIR/lumieraproc', srcSubtree(env,'$SRCDIR/proc')) core = lLib+lApp+lBack+lProc - artifacts['lumiera'] = env.Program('$BINDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) + artifacts['lumiera'] = env.LumieraExe('$BINDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) artifacts['corelib'] = lLib+lApp artifacts['support'] = lLib diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 8b6a4b594..6f2f188b3 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -27,6 +27,7 @@ import SCons.SConf from SCons.Environment import Environment from Buildhelper import * +from os import path @@ -79,6 +80,51 @@ class LumieraEnvironment(Environment): if alias: self.libInfo[alias] = libInfo return libInfo + + + def LumieraLibrary (self, *args,**kw): + """ add some tweaks missing in SCons 1.0 + like proper handling for SONAME + """ + print "hurgha" + if 'soname' in kw: + soname = self.subst(kw['soname']) + else: + if len(args) > 0: + pathname = args[0] + elif 'target' in kw: + pathname = kw['target'] + else: + raise SyntaxError("Library builder requires target spec. Arguments: %s %s" % (args,kw)) + print "SharedLib: path=%s" % pathname + (dirprefix, libname) = path.split(pathname) + if not libname: + raise ValueError("Library name missing. Only got a directory: "+pathname) + libname = "${SHLIBPREFIX}%s$SHLIBSUFFIX" % libname + print "name = "+libname + soname = self.subst(libname) + print "konstruierter name "+soname + + assert soname + subEnv = self.Clone() + subEnv.Append(LINKFLAGS = "-Wl,-soname="+soname ) + + libBuilder = self.get_builder('SharedLibrary') + print "libBuilder=%s" % libBuilder + print "args = %s, kw = %s" % (args,kw) + return libBuilder(subEnv, *args,**kw); + + + def LumieraExe (self, *args,**kw): + """ add handling for rpath with $ORIGIN + """ + print "progrom" + + subEnv = self.Clone() + subEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$LIBDIR,--enable-new-dtags" ) + + programBuilder = self.get_builder('Program') + return programBuilder (subEnv, *args,**kw); diff --git a/src/tool/SConscript b/src/tool/SConscript index bae8d7d12..0930ffbe3 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -12,12 +12,12 @@ envSvg.mergeConf(['librsvg-2.0']) envSvg.Append(LIBS=support_lib) -luidgen = env.Program('#$BINDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs -rsvg = envSvg.Program('#$BINDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) +luidgen = env.LumieraExe('#$BINDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs +rsvg = envSvg.LumieraExe('#$BINDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) # build additional test and administrative tools.... -artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') #### hello world (checks C build) - + env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature... +artifacts['tools'] = [ env.LumieraExe('#$BINDIR/hello-world','hello.c') #### hello world (checks C build) + + env.LumieraExe('#$BINDIR/try', 'try.cpp') #### to try out some feature... # + luidgen + rsvg ] diff --git a/tests/SConscript b/tests/SConscript index 5e97f03e6..8dcfe4bf3 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -35,7 +35,7 @@ def testExecutable(env,tree, exeName=None, obj=None): obj = srcSubtree(env,tree, isShared=False) # use all sourcefiles found in subtree if not exeName: exeName = 'test-%s' % tree - return env.Program('#$BINDIR/'+exeName, obj + core) + return env.LumieraExe('#$BINDIR/'+exeName, obj + core) def testCollection(env,dir): @@ -44,7 +44,7 @@ def testCollection(env,dir): """ srcpatt = ['test-*.c'] exeName = lambda p: path.basename(path.splitext(p)[0]) - buildIt = lambda p: env.Program("#$BINDIR/"+exeName(p), [p] + core) + buildIt = lambda p: env.LumieraExe("#$BINDIR/"+exeName(p), [p] + core) return [buildIt(f) for f in scanSubtree(dir,srcpatt)] @@ -67,7 +67,7 @@ artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # for creating a Valgrind-Suppression file -vgsuppr = env.Program('#$BINDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms +vgsuppr = env.LumieraExe('#$BINDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms artifacts['tools'] += [vgsuppr] Depends(ts,vgsuppr) From 35953b335b6482c37722932a4d093b10f2d2e533 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 29 Jan 2011 02:06:21 +0100 Subject: [PATCH 104/140] SCons: rearrange output directory to target/modules --- .gitignore | 2 +- SConstruct | 16 ++++++++-------- admin/scons/Buildhelper.py | 8 ++++---- admin/scons/LumieraEnvironment.py | 26 ++++++++++++-------------- bin/.libs | 1 - bin/DIR_INFO | 1 - modules | 1 + src/common/dummy-func.cpp | 2 +- src/tool/SConscript | 8 ++++---- target/DIR_INFO | 1 + target/modules/DIR_INFO | 1 + tests/SConscript | 10 +++++----- 12 files changed, 38 insertions(+), 39 deletions(-) delete mode 120000 bin/.libs delete mode 100644 bin/DIR_INFO create mode 120000 modules create mode 100644 target/DIR_INFO create mode 100644 target/modules/DIR_INFO diff --git a/.gitignore b/.gitignore index f66bd5b09..bc696a9a2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ optcache Makefile.in build/* -bin/* +target/* autom4te.cache/* scripts/* configure diff --git a/SConstruct b/SConstruct index 958ba3328..465abdee7 100644 --- a/SConstruct +++ b/SConstruct @@ -26,9 +26,9 @@ OPTIONSCACHEFILE = 'optcache' CUSTOPTIONSFILE = 'custom-options' SRCDIR = 'src' -BINDIR = 'bin' -LIBDIR = '.libs' -PLUGDIR = '.libs' +TARDIR = 'target' +LIBDIR = 'modules' +PLUGDIR = 'modules' TESTDIR = 'tests' ICONDIR = 'icons' VERSION = '0.1+pre.01' @@ -76,7 +76,7 @@ def setupBasicEnvironment(): env.Append ( SHCCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honour CFLAGS, only CCFLAGS env.Replace( VERSION=VERSION , SRCDIR=SRCDIR - , BINDIR=BINDIR + , TARDIR=TARDIR , LIBDIR=LIBDIR , PLUGDIR=PLUGDIR , ICONDIR=ICONDIR @@ -96,8 +96,8 @@ def setupBasicEnvironment(): appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') # setup search path for Lumiera plugins - appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:./.libs\\"' - ,'LUMIERA_PLUGIN_PATH=\\"$DESTDIR/lib/lumiera/:./.libs\\"') + appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:./modules\\"' + ,'LUMIERA_PLUGIN_PATH=\\"$DESTDIR/lib/lumiera/:./modules\\"') appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') @@ -344,7 +344,7 @@ def defineBuildTargets(env, artifacts): core = lLib+lApp+lBack+lProc - artifacts['lumiera'] = env.LumieraExe('$BINDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) + artifacts['lumiera'] = env.LumieraExe('$TARDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) artifacts['corelib'] = lLib+lApp artifacts['support'] = lLib @@ -368,7 +368,7 @@ def defineBuildTargets(env, artifacts): objgui = srcSubtree(envGtk,'$SRCDIR/gui') guimodule = envGtk.LoadableModule('$LIBDIR/gtk_gui', objgui, SHLIBPREFIX='', SHLIBSUFFIX='.lum') artifacts['gui'] = ( guimodule - + env.Install('$BINDIR', env.Glob('$SRCDIR/gui/*.rc')) + + env.Install('$TARDIR', env.Glob('$SRCDIR/gui/*.rc')) + artifacts['icons'] ) diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index f8e349a5e..f781aa730 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -203,11 +203,11 @@ def RegisterIcon_Builder(env): """ import render_icon as renderer # load Joel's python script for invoking the rsvg-convert (SVG render) - renderer.rsvgPath = env.subst("$BINDIR/rsvg-convert") + renderer.rsvgPath = env.subst("$TARDIR/rsvg-convert") def invokeRenderer(target, source, env): source = str(source[0]) - targetdir = env.subst("$BINDIR") + targetdir = env.subst("$TARDIR") renderer.main([source,targetdir]) return 0 @@ -216,12 +216,12 @@ def RegisterIcon_Builder(env): 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) + return (["$TARDIR/%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) + return env.Install("$TARDIR/%s" % subdir, source) buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS") diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 6f2f188b3..e7f4e7076 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -83,12 +83,12 @@ class LumieraEnvironment(Environment): def LumieraLibrary (self, *args,**kw): - """ add some tweaks missing in SCons 1.0 - like proper handling for SONAME + """ augments the built-in SharedLibrary() builder to add + some tweaks missing in SCons 1.0, like setting a SONAME proper + instead of just passing the relative pathname to the linker """ - print "hurgha" if 'soname' in kw: - soname = self.subst(kw['soname']) + soname = self.subst(kw['soname']) # explicitely defined by user else: if len(args) > 0: pathname = args[0] @@ -96,30 +96,28 @@ class LumieraEnvironment(Environment): pathname = kw['target'] else: raise SyntaxError("Library builder requires target spec. Arguments: %s %s" % (args,kw)) - print "SharedLib: path=%s" % pathname (dirprefix, libname) = path.split(pathname) if not libname: raise ValueError("Library name missing. Only got a directory: "+pathname) + libname = "${SHLIBPREFIX}%s$SHLIBSUFFIX" % libname - print "name = "+libname - soname = self.subst(libname) - print "konstruierter name "+soname + soname = self.subst(libname) # else: use the library filename as DT_SONAME assert soname subEnv = self.Clone() subEnv.Append(LINKFLAGS = "-Wl,-soname="+soname ) libBuilder = self.get_builder('SharedLibrary') - print "libBuilder=%s" % libBuilder - print "args = %s, kw = %s" % (args,kw) - return libBuilder(subEnv, *args,**kw); + return libBuilder(subEnv, *args,**kw); # invoke the predefined builder on the augmented environment def LumieraExe (self, *args,**kw): - """ add handling for rpath with $ORIGIN + """ augments the built-in Program() builder to add a fixed rpath based on $ORIGIN + That is: after searching LD_LIBRARY_PATH, but before the standard linker search, + the directory relative to the position of the executable ($ORIGIN) is searched. + This search path is active not only for the executable, but for all libraries + it is linked with """ - print "progrom" - subEnv = self.Clone() subEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$LIBDIR,--enable-new-dtags" ) diff --git a/bin/.libs b/bin/.libs deleted file mode 120000 index 2e321a8e9..000000000 --- a/bin/.libs +++ /dev/null @@ -1 +0,0 @@ -../.libs \ No newline at end of file diff --git a/bin/DIR_INFO b/bin/DIR_INFO deleted file mode 100644 index 9dafe61c3..000000000 --- a/bin/DIR_INFO +++ /dev/null @@ -1 +0,0 @@ -Lumiera executable(s) and libraries will be built here diff --git a/modules b/modules new file mode 120000 index 000000000..eb149d644 --- /dev/null +++ b/modules @@ -0,0 +1 @@ +target/modules \ No newline at end of file diff --git a/src/common/dummy-func.cpp b/src/common/dummy-func.cpp index 08b7f69a0..b6250ee75 100644 --- a/src/common/dummy-func.cpp +++ b/src/common/dummy-func.cpp @@ -15,7 +15,7 @@ namespace lumiera { - const char * const GUI_MODULE_NAME = ".libs/gtk_gui.lum"; + const char * const GUI_MODULE_NAME = "modules/gtk_gui.lum"; typedef void (*VoidFunc)(void); diff --git a/src/tool/SConscript b/src/tool/SConscript index 0930ffbe3..725248ca8 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -12,12 +12,12 @@ envSvg.mergeConf(['librsvg-2.0']) envSvg.Append(LIBS=support_lib) -luidgen = env.LumieraExe('#$BINDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs -rsvg = envSvg.LumieraExe('#$BINDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) +luidgen = env.LumieraExe('#$TARDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs +rsvg = envSvg.LumieraExe('#$TARDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) # build additional test and administrative tools.... -artifacts['tools'] = [ env.LumieraExe('#$BINDIR/hello-world','hello.c') #### hello world (checks C build) - + env.LumieraExe('#$BINDIR/try', 'try.cpp') #### to try out some feature... +artifacts['tools'] = [ env.LumieraExe('#$TARDIR/hello-world','hello.c') #### hello world (checks C build) + + env.LumieraExe('#$TARDIR/try', 'try.cpp') #### to try out some feature... # + luidgen + rsvg ] diff --git a/target/DIR_INFO b/target/DIR_INFO new file mode 100644 index 000000000..8764b3989 --- /dev/null +++ b/target/DIR_INFO @@ -0,0 +1 @@ +Lumiera program package tree, holding executable(s) and libraries to be built diff --git a/target/modules/DIR_INFO b/target/modules/DIR_INFO new file mode 100644 index 000000000..ffdc4efcb --- /dev/null +++ b/target/modules/DIR_INFO @@ -0,0 +1 @@ +Lumiera subsystems and other dynamically loadable application components diff --git a/tests/SConscript b/tests/SConscript index 8dcfe4bf3..b9e204676 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -35,7 +35,7 @@ def testExecutable(env,tree, exeName=None, obj=None): obj = srcSubtree(env,tree, isShared=False) # use all sourcefiles found in subtree if not exeName: exeName = 'test-%s' % tree - return env.LumieraExe('#$BINDIR/'+exeName, obj + core) + return env.LumieraExe('#$TARDIR/'+exeName, obj + core) def testCollection(env,dir): @@ -44,7 +44,7 @@ def testCollection(env,dir): """ srcpatt = ['test-*.c'] exeName = lambda p: path.basename(path.splitext(p)[0]) - buildIt = lambda p: env.LumieraExe("#$BINDIR/"+exeName(p), [p] + core) + buildIt = lambda p: env.LumieraExe("#$TARDIR/"+exeName(p), [p] + core) return [buildIt(f) for f in scanSubtree(dir,srcpatt)] @@ -67,7 +67,7 @@ artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # for creating a Valgrind-Suppression file -vgsuppr = env.LumieraExe('#$BINDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms +vgsuppr = env.LumieraExe('#$TARDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms artifacts['tools'] += [vgsuppr] Depends(ts,vgsuppr) @@ -103,10 +103,10 @@ if testsuites: testEnv['ENV']['TEST_CONF'] = env.File("test.conf").abspath -testDir = env.Dir('#$BINDIR') +testDir = env.Dir('#$TARDIR') runTest = env.File("test.sh").abspath -runTs = testEnv.Command('#$BINDIR/,testlog', ts, runTest, chdir=testDir) +runTs = testEnv.Command('#$TARDIR/,testlog', ts, runTest, chdir=testDir) From 2bcc8d9ae3a9a7d7fc1508c87d635237ce099dda Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 29 Jan 2011 02:33:13 +0100 Subject: [PATCH 105/140] SCons: build loadable modules immediately to the target destination --- SConstruct | 10 +++++----- admin/scons/LumieraEnvironment.py | 2 +- modules | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) delete mode 120000 modules diff --git a/SConstruct b/SConstruct index 465abdee7..a633e0583 100644 --- a/SConstruct +++ b/SConstruct @@ -27,8 +27,8 @@ OPTIONSCACHEFILE = 'optcache' CUSTOPTIONSFILE = 'custom-options' SRCDIR = 'src' TARDIR = 'target' -LIBDIR = 'modules' -PLUGDIR = 'modules' +LIBDIR = 'target/modules' +MODULES = 'modules' TESTDIR = 'tests' ICONDIR = 'icons' VERSION = '0.1+pre.01' @@ -78,7 +78,7 @@ def setupBasicEnvironment(): , SRCDIR=SRCDIR , TARDIR=TARDIR , LIBDIR=LIBDIR - , PLUGDIR=PLUGDIR + , MODULES=MODULES , ICONDIR=ICONDIR , CPPPATH=["#"+SRCDIR] # used to find includes, "#" means always absolute to build-root , CPPDEFINES=['-DLUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines @@ -96,8 +96,8 @@ def setupBasicEnvironment(): appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') # setup search path for Lumiera plugins - appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:./modules\\"' - ,'LUMIERA_PLUGIN_PATH=\\"$DESTDIR/lib/lumiera/:./modules\\"') + appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:./$MODULES\\"' + ,'LUMIERA_PLUGIN_PATH=\\"$DESTDIR/lib/lumiera/:./$MODULES\\"') appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index e7f4e7076..3a08695f9 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -119,7 +119,7 @@ class LumieraEnvironment(Environment): it is linked with """ subEnv = self.Clone() - subEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$LIBDIR,--enable-new-dtags" ) + subEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$MODULES,--enable-new-dtags" ) programBuilder = self.get_builder('Program') return programBuilder (subEnv, *args,**kw); diff --git a/modules b/modules deleted file mode 120000 index eb149d644..000000000 --- a/modules +++ /dev/null @@ -1 +0,0 @@ -target/modules \ No newline at end of file From a6810957b49896634175d1cf5349296b3d5b419d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 29 Jan 2011 14:10:19 +0100 Subject: [PATCH 106/140] Install: change organisation of target and output directory variables now supporting a PREFIX in addition to the INSTALLDIR. The latter is intended for package building --- SConstruct | 8 +++++--- admin/scons/Buildhelper.py | 2 +- admin/vg-run.sh | 5 +++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/SConstruct b/SConstruct index a633e0583..974b3d7c9 100644 --- a/SConstruct +++ b/SConstruct @@ -80,6 +80,7 @@ def setupBasicEnvironment(): , LIBDIR=LIBDIR , MODULES=MODULES , ICONDIR=ICONDIR + , DESTDIR=env.subst('$INSTALLDIR/$PREFIX') , CPPPATH=["#"+SRCDIR] # used to find includes, "#" means always absolute to build-root , CPPDEFINES=['-DLUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines , CCFLAGS='-Wall -Wextra ' @@ -164,9 +165,10 @@ def defineCmdlineVariables(): # ,BoolVariable('OPENGL', 'Include support for OpenGL preview rendering', False) # ,EnumVariable('DIST_TARGET', 'Build target architecture', 'auto', # allowed_values=('auto', 'i386', 'i686', 'x86_64' ), ignorecase=2) - ,PathVariable('DESTDIR', 'Installation dir prefix', '/usr/local') - ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to DESTDIR/lib/lumiera', '',PathVariable.PathAccept) - ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually DESTDIR/share/lumiera', '',PathVariable.PathAccept) + ,PathVariable('PREFIX', 'Installation dir prefix', 'usr/local', PathVariable.PathAccept) + ,PathVariable('INSTALLDIR', 'Root output directory for install. Final installation will happen in INSTALLDIR/PREFIX/... ', '/', PathVariable.PathIsDir) + ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) + ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) ,PathVariable('SRCTAR', 'Create source tarball prior to compiling', '..', PathVariable.PathAccept) ,PathVariable('DOCTAR', 'Create tarball with developer documentation', '..', PathVariable.PathAccept) ) diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index f781aa730..f555489dd 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -151,7 +151,7 @@ def createPlugins(env, dir): """ investigate the given source directory to identify all contained source trees. @return: a list of build nodes defining a plugin for each of these source trees. """ - return [env.LoadableModule( '#$PLUGDIR/%s' % getDirname(tree) + return [env.LoadableModule( '#$TARDIR/$MODULES/%s' % getDirname(tree) , srcSubtree(env, tree) , SHLIBPREFIX='', SHLIBSUFFIX='.lum' ) diff --git a/admin/vg-run.sh b/admin/vg-run.sh index f8d82040b..3a3aa1f57 100644 --- a/admin/vg-run.sh +++ b/admin/vg-run.sh @@ -12,6 +12,7 @@ # LOGFILE=,valgrind.log SUPPRESS=vgsuppression +MODULEDIR=modules VALGRINDFLAGS=${VALGRINDFLAGS:---leak-check=yes --show-reachable=yes --demangle=yes} EXECUTABLE=$1 @@ -24,6 +25,9 @@ fi PATHPREFIX=${EXECUTABLE%/*} SUPPRESS="$PATHPREFIX/$SUPPRESS" +# need to set a library search path, because valgrind doesn't handle DT_RUNPATH and $ORIGIN properly +export LD_LIBRARY_PATH=$PATHPREFIX/$MODULEDIR:$LD_LIBRARY_PATH + if [[ -x $SUPPRESS ]]; then if [[ $SUPPRESS -nt $SUPPRESS.supp ]]; then echo 'generating valgrind supression file...' @@ -37,6 +41,7 @@ else echo 'no suppression.' fi + echo "running......$@" valgrind $VALGRINDFLAGS --log-file=$LOGFILE.%p $SUPPRESSIONFLAG $@ & From 7993759f8ead6e56cf41aa5f69ebb3d642d50f64 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 29 Jan 2011 14:16:15 +0100 Subject: [PATCH 107/140] SCons: remove the homebrew tarballer that was a nice SCons learning exercise, but never used moreover, packaing is not the concern of a build system --- SConstruct | 12 +----- admin/scons/Buildhelper.py | 76 -------------------------------------- 2 files changed, 1 insertion(+), 87 deletions(-) diff --git a/SConstruct b/SConstruct index 974b3d7c9..b2e9d803a 100644 --- a/SConstruct +++ b/SConstruct @@ -169,8 +169,6 @@ def defineCmdlineVariables(): ,PathVariable('INSTALLDIR', 'Root output directory for install. Final installation will happen in INSTALLDIR/PREFIX/... ', '/', PathVariable.PathIsDir) ,PathVariable('PKGLIBDIR', 'Installation dir for plugins, defaults to PREFIX/lib/lumiera/modules', '',PathVariable.PathAccept) ,PathVariable('PKGDATADIR', 'Installation dir for default config, usually PREFIX/share/lumiera', '',PathVariable.PathAccept) - ,PathVariable('SRCTAR', 'Create source tarball prior to compiling', '..', PathVariable.PathAccept) - ,PathVariable('DOCTAR', 'Create tarball with developer documentation', '..', PathVariable.PathAccept) ) return vars @@ -314,15 +312,7 @@ def definePackagingTargets(env, artifacts): """ build operations and targets to be done /before/ compiling. things like creating a source tarball or preparing a version header. """ - t = Tarball(env,location='$SRCTAR',dirs='$SRCDIR') - artifacts['src.tar'] = t - env.Alias('src.tar', t) - env.Alias('tar', t) - - t = Tarball(env,location='$DOCTAR',suffix='-doc',dirs='admin doc wiki uml tests') - artifacts['doc.tar'] = t - env.Alias('doc.tar', t) - env.Alias('tar', t) + pass diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index f555489dd..2fef49cf3 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -231,79 +231,3 @@ def RegisterIcon_Builder(env): env.Append(BUILDERS = {'IconRender' : buildIcon}) env.AddMethod(IconCopy) - - -def Tarball(env,location,dirs,suffix=''): - """ Custom Command: create Tarball of some subdirs - location: where to create the tar (may optionally include filename.tar.gz) - suffix: (optional) suffix to include in the tar name - dirs: directories to include in the tar - - This is a bit of a hack, because we want to be able to include arbitrary directories, - without creating new dependencies on those dirs. Esp. we want to tar the source tree - prior to compiling. Solution is - - use the Command-Builder, but pass all target specifications as custom build vars - - create a pseudo-target located in the parent directory (not built by default) - """ - targetID = '../extern-tar%s' % suffix - versionID = env['VERSION'] - defaultName = 'lumiera%s_%s' % (suffix, versionID) - nameprefix = 'lumiera-%s/' % (versionID) - location = env.subst(location) - dirs = env.subst(dirs) - return env.Command(targetID,None, createTarball, - location=location, defaultName=defaultName, dirs=dirs, nameprefix=nameprefix) - - -def createTarball(target,source,env): - """ helper, builds the tar using the python2.3 tarfile lib. - This allows us to prefix all paths, thus moving the tree - into a virtual subdirectory containing the Version number, - as needed by common packaging systems. - """ - name = getTarName( location = env['location'] - , defaultName = env['defaultName']) - targetspec = env['dirs'] - nameprefix = env['nameprefix'] or '' - print 'Running: tar -czf %s %s ...' % (name,targetspec) - if os.path.isfile(name): - os.remove(name) - tar = tarfile.open(name,'w:gz') - for name in targetspec.split(): - tar.add(name,nameprefix+name) - tar.close() -# -# old version using shell command: -# -# cmd = 'tar -czf %s %s' % (name,targetspec) -# print 'running ', cmd, ' ... ' -# pipe = os.popen (cmd) -# return pipe.close () - - - - -def getTarName(location, defaultName): - """ create a suitable name for the tarball. - - if location contains a name (*.tar.gz) then use this - - otherwise append the defaultName to the specified dir - """ - spec = os.path.abspath(location) - (head,tail) = os.path.split(spec) - if not os.path.isdir(head): - print 'Target dir "%s" for Tar doesn\'t exist.' % head - Exit(1) - mat = re.match(r'([\w\.\-\+:\~]+)\.((tar)|(tar\.gz)|(tgz))', tail) - if mat: - name = mat.group(1) - ext = '.'+mat.group(2) - else: - ext = '.tar.gz' - if os.path.isdir(spec): - head = spec - name = defaultName - else: - name = tail - return os.path.join(head,name+ext) - - From bc22ec7faaf0d30c08a9470cafb4b1a1182e4a1a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 29 Jan 2011 16:45:22 +0100 Subject: [PATCH 108/140] Install: first preliminary working installation setup the installed lumiera exe can even be started... ...well with a bit of cheating: you need to cd into the lib/lumiera because the PLUGINPATH problem isn't solved yet --- SConstruct | 32 +++++++++++++------ admin/scons/Buildhelper.py | 8 ++--- admin/scons/LumieraEnvironment.py | 52 ++++++++++++++++++++++++++----- data/config/dummy_lumiera.ini | 4 +++ 4 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 data/config/dummy_lumiera.ini diff --git a/SConstruct b/SConstruct index b2e9d803a..3ae0c4fcb 100644 --- a/SConstruct +++ b/SConstruct @@ -336,9 +336,9 @@ def defineBuildTargets(env, artifacts): core = lLib+lApp+lBack+lProc - artifacts['lumiera'] = env.LumieraExe('$TARDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) - artifacts['corelib'] = lLib+lApp + artifacts['corelib'] = core artifacts['support'] = lLib + artifacts['lumiera'] = env.LumieraExe('$TARDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) # building Lumiera Plugins envPlu = env.Clone() @@ -351,6 +351,7 @@ def defineBuildTargets(env, artifacts): artifacts['icons'] = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + [env.IconCopy(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] ) + ##TODO make that into a resource builder # the Lumiera GTK GUI envGtk = env.Clone() @@ -363,6 +364,7 @@ def defineBuildTargets(env, artifacts): + env.Install('$TARDIR', env.Glob('$SRCDIR/gui/*.rc')) + artifacts['icons'] ) + artifacts['guimodule'] = guimodule ###TODO better organisation of GUI components # call subdir SConscript(s) for independent components SConscript(dirs=[SRCDIR+'/tool'], exports='env artifacts core') @@ -376,10 +378,10 @@ def definePostBuildTargets(env, artifacts): """ ib = env.Alias('install-bin', '$DESTDIR/bin') il = env.Alias('install-lib', '$DESTDIR/lib') - env.Alias('install', [ib, il]) + id = env.Alias('install-dat', '$DESTDIR/share') + env.Alias('install', [ib, il, id]) build = env.Alias('build', artifacts['lumiera']+artifacts['gui']+artifacts['plugins']+artifacts['tools']) - allbu = env.Alias('allbuild', build+artifacts['testsuite']) env.Default('build') # additional files to be cleaned when cleaning 'build' env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) @@ -388,17 +390,27 @@ def definePostBuildTargets(env, artifacts): doxydoc = artifacts['doxydoc'] = env.Doxygen('doc/devel/Doxyfile') env.Alias ('doc', doxydoc) env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) + + allbu = env.Alias('allbuild', build+artifacts['testsuite']+doxydoc) def defineInstallTargets(env, artifacts): - """ define some artifacts to be installed into target locations. + """ define artifacts to be installed into target locations. """ - env.Install(dir = '$DESTDIR/bin', source=artifacts['lumiera']) - env.Install(dir = '$DESTDIR/lib', source=artifacts['corelib']) - env.Install(dir = '$DESTDIR/lib', source=artifacts['plugins']) - env.Install(dir = '$DESTDIR/bin', source=artifacts['tools']) + binDir = '$DESTDIR/bin/' + lumDir = '$DESTDIR/lib/lumiera/' + modDir = '$DESTDIR/lib/lumiera/$MODULES/' + shaDir = '$DESTDIR/share/lumiera/' + env.Install(dir = modDir, source=artifacts['corelib']) + env.Install(dir = modDir, source=artifacts['plugins']) + env.Install(dir = modDir, source=artifacts['guimodule']) + lumi = env.Install(dir = lumDir, source=artifacts['lumiera']) + tool = env.Install(dir = lumDir, source=artifacts['tools']) + print "Aufruf LINK DESTDIR=" + env.get('DESTDIR') + env.SymLink(binDir+"lumiera",lumi,"../lib/lumiera/lumiera") - env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) + env.Install(dir = shaDir, source="data/config/dummy_lumiera.ini") ### TODO should become a resource builder +# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) ##################################################################### diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 2fef49cf3..8e7356271 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -151,10 +151,10 @@ def createPlugins(env, dir): """ investigate the given source directory to identify all contained source trees. @return: a list of build nodes defining a plugin for each of these source trees. """ - return [env.LoadableModule( '#$TARDIR/$MODULES/%s' % getDirname(tree) - , srcSubtree(env, tree) - , SHLIBPREFIX='', SHLIBSUFFIX='.lum' - ) + return [env.LumieraPlugin( '#$TARDIR/$MODULES/%s' % getDirname(tree) + , srcSubtree(env, tree) + , SHLIBPREFIX='', SHLIBSUFFIX='.lum' + ) for tree in findSrcTrees(dir) ] diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 3a08695f9..b9a018c08 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -22,12 +22,14 @@ ##################################################################### +import os +from os import path + import SCons import SCons.SConf from SCons.Environment import Environment from Buildhelper import * -from os import path @@ -82,10 +84,29 @@ class LumieraEnvironment(Environment): return libInfo - def LumieraLibrary (self, *args,**kw): - """ augments the built-in SharedLibrary() builder to add - some tweaks missing in SCons 1.0, like setting a SONAME proper - instead of just passing the relative pathname to the linker + def SymLink(self, target, source, linktext=None): + """ use python to create a symlink + """ + def makeLink(target,source,env): + if linktext: + dest = linktext + else: + dest = str(source[0]) + link = str(target[0]) + os.symlink(dest, link) + def reportLink(target,source,env): + dest = str(source[0]) + link = str(target[0]) + return "Install link %s -> %s" % (link,dest) + + action = Action(makeLink,reportLink) + self.Command (target,source, action) + + + def defineSoname (self, *args,**kw): + """ internal helper to extract or guess + a suitable library SONAME, either using an + explicit spec, falling back on the lib filename """ if 'soname' in kw: soname = self.subst(kw['soname']) # explicitely defined by user @@ -102,15 +123,32 @@ class LumieraEnvironment(Environment): libname = "${SHLIBPREFIX}%s$SHLIBSUFFIX" % libname soname = self.subst(libname) # else: use the library filename as DT_SONAME - assert soname + return soname + + + def LumieraLibrary (self, *args,**kw): + """ augments the built-in SharedLibrary() builder to add + some tweaks missing in SCons 1.0, like setting a SONAME proper + instead of just passing the relative pathname to the linker + """ subEnv = self.Clone() - subEnv.Append(LINKFLAGS = "-Wl,-soname="+soname ) + subEnv.Append(LINKFLAGS = "-Wl,-soname="+self.defineSoname(*args,**kw)) libBuilder = self.get_builder('SharedLibrary') return libBuilder(subEnv, *args,**kw); # invoke the predefined builder on the augmented environment + def LumieraPlugin (self, *args,**kw): + """ builds a shared library, autmented by some defaults for lumiera plugins. + """ + subEnv = self.Clone() + subEnv.Append(LINKFLAGS = "-Wl,-soname="+self.defineSoname(*args,**kw)) + + libBuilder = self.get_builder('LoadableModule') + return libBuilder(subEnv, *args,**kw); # invoke the predefined builder on the augmented environment + + def LumieraExe (self, *args,**kw): """ augments the built-in Program() builder to add a fixed rpath based on $ORIGIN That is: after searching LD_LIBRARY_PATH, but before the standard linker search, diff --git a/data/config/dummy_lumiera.ini b/data/config/dummy_lumiera.ini new file mode 100644 index 000000000..cfe91da1e --- /dev/null +++ b/data/config/dummy_lumiera.ini @@ -0,0 +1,4 @@ +/* This is an dummy Lumiera config file + * + * Actually Lumiera can't yet load any config, as of 1/2011 + */ From 9e56434c7ec4130098bf374649103bc5c071b50b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 29 Jan 2011 23:09:02 +0100 Subject: [PATCH 109/140] SCons: start concentrating all custom builders into LumieraEnvironment --- SConstruct | 1 - admin/scons/LumieraEnvironment.py | 40 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 3ae0c4fcb..3711f489e 100644 --- a/SConstruct +++ b/SConstruct @@ -86,7 +86,6 @@ def setupBasicEnvironment(): , CCFLAGS='-Wall -Wextra ' , CFLAGS='-std=gnu99' ) - RegisterIcon_Builder(env) handleNoBugSwitches(env) env.Append(CPPDEFINES = '_GNU_SOURCE') diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index b9a018c08..a1e050156 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -41,6 +41,7 @@ class LumieraEnvironment(Environment): def __init__(self,*args,**kw): Environment.__init__ (self,*args,**kw) self.libInfo = {} + RegisterIcon_Builder(self) def Configure (self, *args, **kw): kw['env'] = self @@ -184,3 +185,42 @@ class LumieraConfigContext(ConfigBase): return self.env.addLibInfo (libID, minVersion, alias) + +###### Lumiera custom tools and builders ######################## + + +def RegisterIcon_Builder(env): + """ Registers 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. + """ + + import render_icon as renderer # load Joel's python script for invoking the rsvg-convert (SVG render) + renderer.rsvgPath = env.subst("$TARDIR/rsvg-convert") + + def invokeRenderer(target, source, env): + source = str(source[0]) + targetdir = env.subst("$TARDIR") + 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 (["$TARDIR/%s" % name for name in targetfiles], source) + + def IconCopy(env, source): + """Copy icon to corresponding icon dir. """ + subdir = getDirname(source) + return env.Install("$TARDIR/%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) + From 014c22b40aad03af017c2b7eca41acd98cb74f3a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Jan 2011 15:27:21 +0100 Subject: [PATCH 110/140] SCons: rework build directory configuration. All customisations to LuimieraEnvironment --- SConstruct | 60 +++++++++++++++----------- admin/scons/BuilderDoxygen.py | 4 +- admin/scons/Buildhelper.py | 72 ++++++++++++++++--------------- admin/scons/LumieraEnvironment.py | 19 +++++--- src/tool/SConscript | 8 ++-- tests/SConscript | 10 ++--- 6 files changed, 94 insertions(+), 79 deletions(-) diff --git a/SConstruct b/SConstruct index 3711f489e..8cf377233 100644 --- a/SConstruct +++ b/SConstruct @@ -23,17 +23,29 @@ #-----------------------------------Configuration -OPTIONSCACHEFILE = 'optcache' -CUSTOPTIONSFILE = 'custom-options' +TARGDIR = 'target' +VERSION = '0.1+pre.01' +TOOLDIR = './admin/scons' +SCRIPTDIR = './admin' +OPTCACHE = 'optcache' +CUSTOPTFILE = 'custom-options' +####### SRCDIR = 'src' -TARDIR = 'target' -LIBDIR = 'target/modules' -MODULES = 'modules' TESTDIR = 'tests' ICONDIR = 'icons' -VERSION = '0.1+pre.01' -TOOLDIR = './admin/scons' -SCRIPTDIR = './admin' +MODULES = 'modules' +LIBDIR = 'target/modules' +####### +buildExe = '#$TARGDIR' +buildLib = '#$TARGDIR/modules' +buildIcon = '#$TARGDIR/icons' +buildConf = '#$TARGDIR/config' +installExe = '#$DESTDIR/lib/lumiera' +installLib = '#$DESTDIR/lib/lumiera/modules' +installIcon = '#$DESTDIR/share/lumiera/icons' +installConf = '#$DESTDIR/share/lumiera/config' + +localDefinitions = locals() #-----------------------------------Configuration # NOTE: scons -h for help. @@ -55,10 +67,10 @@ from LumieraEnvironment import * ##################################################################### -def setupBasicEnvironment(): +def setupBasicEnvironment(localDefinitions): """ define cmdline options, build type decisions """ - EnsurePythonVersion(2,3) + EnsurePythonVersion(2,4) EnsureSConsVersion(1,0) Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 @@ -66,21 +78,19 @@ def setupBasicEnvironment(): vars = defineCmdlineVariables() env = LumieraEnvironment(variables=vars ,toolpath = [TOOLDIR] - ,tools = ["default", "BuilderGCH", "BuilderDoxygen"] - ) - env.Tool("ToolDistCC") - env.Tool("ToolCCache") + ,pathConfig = extract_localPathDefs(localDefinitions) + ,TARGDIR = TARGDIR + ,DESTDIR = '$INSTALLDIR/$PREFIX' + ,VERSION = VERSION + ) handleVerboseMessages(env) env.Append ( CCCOM=' -std=gnu99') env.Append ( SHCCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honour CFLAGS, only CCFLAGS - env.Replace( VERSION=VERSION - , SRCDIR=SRCDIR - , TARDIR=TARDIR + env.Replace( SRCDIR=SRCDIR , LIBDIR=LIBDIR , MODULES=MODULES , ICONDIR=ICONDIR - , DESTDIR=env.subst('$INSTALLDIR/$PREFIX') , CPPPATH=["#"+SRCDIR] # used to find includes, "#" means always absolute to build-root , CPPDEFINES=['-DLUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines , CCFLAGS='-Wall -Wextra ' @@ -102,7 +112,7 @@ def setupBasicEnvironment(): ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') prepareOptionsHelp(vars,env) - vars.Save(OPTIONSCACHEFILE, env) + vars.Save(OPTCACHE, env) return env def appendCppDefine(env,var,cppVar, elseVal=''): @@ -148,7 +158,7 @@ def defineCmdlineVariables(): you may define custom variable settings in a separate file. Commandline will override both. """ - vars = Variables([OPTIONSCACHEFILE, CUSTOPTIONSFILE]) + vars = Variables([OPTCACHE, CUSTOPTFILE]) vars.AddVariables( ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') ,('CC', 'Set the C compiler to use.', 'gcc') @@ -311,7 +321,7 @@ def definePackagingTargets(env, artifacts): """ build operations and targets to be done /before/ compiling. things like creating a source tarball or preparing a version header. """ - pass + pass ## currently none @@ -337,7 +347,7 @@ def defineBuildTargets(env, artifacts): artifacts['corelib'] = core artifacts['support'] = lLib - artifacts['lumiera'] = env.LumieraExe('$TARDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) + artifacts['lumiera'] = env.LumieraExe('$TARGDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) # building Lumiera Plugins envPlu = env.Clone() @@ -360,7 +370,7 @@ def defineBuildTargets(env, artifacts): objgui = srcSubtree(envGtk,'$SRCDIR/gui') guimodule = envGtk.LoadableModule('$LIBDIR/gtk_gui', objgui, SHLIBPREFIX='', SHLIBSUFFIX='.lum') artifacts['gui'] = ( guimodule - + env.Install('$TARDIR', env.Glob('$SRCDIR/gui/*.rc')) + + env.Install('$TARGDIR', env.Glob('$SRCDIR/gui/*.rc')) + artifacts['icons'] ) artifacts['guimodule'] = guimodule ###TODO better organisation of GUI components @@ -405,7 +415,6 @@ def defineInstallTargets(env, artifacts): env.Install(dir = modDir, source=artifacts['guimodule']) lumi = env.Install(dir = lumDir, source=artifacts['lumiera']) tool = env.Install(dir = lumDir, source=artifacts['tools']) - print "Aufruf LINK DESTDIR=" + env.get('DESTDIR') env.SymLink(binDir+"lumiera",lumi,"../lib/lumiera/lumiera") env.Install(dir = shaDir, source="data/config/dummy_lumiera.ini") ### TODO should become a resource builder @@ -419,8 +428,7 @@ def defineInstallTargets(env, artifacts): ### === MAIN === #################################################### - -env = setupBasicEnvironment() +env = setupBasicEnvironment(localDefinitions) if not (isCleanupOperation(env) or isHelpRequest()): env = configurePlatform(env) diff --git a/admin/scons/BuilderDoxygen.py b/admin/scons/BuilderDoxygen.py index a397ef32e..7c9514f2c 100644 --- a/admin/scons/BuilderDoxygen.py +++ b/admin/scons/BuilderDoxygen.py @@ -217,8 +217,8 @@ def generate(env): 'Doxygen': doxyfile_builder, }) - env.AppendUnique( - DOXYGEN = 'doxygen', + env.Replace( + DOXYGEN = 'doxygen' ) diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 8e7356271..387e0fccb 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -28,6 +28,7 @@ import fnmatch import re import tarfile +from SCons import Util from SCons.Action import Action @@ -151,7 +152,7 @@ def createPlugins(env, dir): """ investigate the given source directory to identify all contained source trees. @return: a list of build nodes defining a plugin for each of these source trees. """ - return [env.LumieraPlugin( '#$TARDIR/$MODULES/%s' % getDirname(tree) + return [env.LumieraPlugin( '#$TARGDIR/$MODULES/%s' % getDirname(tree) , srcSubtree(env, tree) , SHLIBPREFIX='', SHLIBSUFFIX='.lum' ) @@ -196,38 +197,39 @@ def checkCommandOption(env, optID, val=None, cmdName=None): -def RegisterIcon_Builder(env): - """ Registers 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. - """ - - import render_icon as renderer # load Joel's python script for invoking the rsvg-convert (SVG render) - renderer.rsvgPath = env.subst("$TARDIR/rsvg-convert") - - def invokeRenderer(target, source, env): - source = str(source[0]) - targetdir = env.subst("$TARDIR") - 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 (["$TARDIR/%s" % name for name in targetfiles], source) - - def IconCopy(env, source): - """Copy icon to corresponding icon dir. """ - subdir = getDirname(source) - return env.Install("$TARDIR/%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) +class Record(dict): + """ a set of properties with map style access. + Record is a dictionary, but the elements can be accessed + conveniently as if they where object fields + """ + def __init__(self, defaults=None, **props): + if defaults: + defaults.update(props) + props = defaults + dict.__init__(self,props) + + def __getattr__(self,key): + if key=='__get__' or key=='__set__': + raise AttributeError + return self.setdefault(key) + + def __setattr__(self,key,val): + self[key] = val + + +def extract_localPathDefs (localDefs): + """ extracts the directory configuration values. + For sake of simplicity, paths and directories are defined + immediately as global variables in the SConstruct. This helper + extracts from the given dict the variables matching some magical + pattern and returns them wrapped into a Record for convenient access + """ + def relevantPathDefs (mapping): + for (k,v) in mapping.items(): + if k.startswith('build') or k.startswith('install') and Util.is_String(v): + v = v.strip() + if not v.endswith('/'): v += '/' + yield (k,v) + + return dict(relevantPathDefs(localDefs)) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index a1e050156..fa1320451 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -38,9 +38,14 @@ class LumieraEnvironment(Environment): This allows us to carry structured config data without using global vars. Idea inspired by Ardour. """ - def __init__(self,*args,**kw): - Environment.__init__ (self,*args,**kw) + def __init__(self, pathConfig, **kw): + Environment.__init__ (self,**kw) + self.path = Record (pathConfig) self.libInfo = {} + self.Tool("BuilderGCH") + self.Tool("BuilderDoxygen") + self.Tool("ToolDistCC") + self.Tool("ToolCCache") RegisterIcon_Builder(self) def Configure (self, *args, **kw): @@ -77,7 +82,7 @@ class LumieraEnvironment(Environment): print "Problems configuring the Library %s (>= %s)" % (libID,minVersion) return False - self.libInfo[libID] = libInfo = LumieraEnvironment() + self.libInfo[libID] = libInfo = Environment() libInfo["ENV"]["PKG_CONFIG_PATH"] = os.environ.get("PKG_CONFIG_PATH") libInfo.ParseConfig ('pkg-config --cflags --libs '+ libID ) if alias: @@ -196,11 +201,11 @@ def RegisterIcon_Builder(env): """ import render_icon as renderer # load Joel's python script for invoking the rsvg-convert (SVG render) - renderer.rsvgPath = env.subst("$TARDIR/rsvg-convert") + renderer.rsvgPath = env.subst("$TARGDIR/rsvg-convert") def invokeRenderer(target, source, env): source = str(source[0]) - targetdir = env.subst("$TARDIR") + targetdir = env.subst("$TARGDIR") renderer.main([source,targetdir]) return 0 @@ -209,12 +214,12 @@ def RegisterIcon_Builder(env): source = str(source[0]) targetdir = os.path.basename(str(target[0])) targetfiles = renderer.getTargetNames(source) # parse SVG - return (["$TARDIR/%s" % name for name in targetfiles], source) + return (["$TARGDIR/%s" % name for name in targetfiles], source) def IconCopy(env, source): """Copy icon to corresponding icon dir. """ subdir = getDirname(source) - return env.Install("$TARDIR/%s" % subdir, source) + return env.Install("$TARGDIR/%s" % subdir, source) buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS") diff --git a/src/tool/SConscript b/src/tool/SConscript index 725248ca8..bab7e6497 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -12,12 +12,12 @@ envSvg.mergeConf(['librsvg-2.0']) envSvg.Append(LIBS=support_lib) -luidgen = env.LumieraExe('#$TARDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs -rsvg = envSvg.LumieraExe('#$TARDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) +luidgen = env.LumieraExe('#$TARGDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs +rsvg = envSvg.LumieraExe('#$TARGDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) # build additional test and administrative tools.... -artifacts['tools'] = [ env.LumieraExe('#$TARDIR/hello-world','hello.c') #### hello world (checks C build) - + env.LumieraExe('#$TARDIR/try', 'try.cpp') #### to try out some feature... +artifacts['tools'] = [ env.LumieraExe('#$TARGDIR/hello-world','hello.c') #### hello world (checks C build) + + env.LumieraExe('#$TARGDIR/try', 'try.cpp') #### to try out some feature... # + luidgen + rsvg ] diff --git a/tests/SConscript b/tests/SConscript index b9e204676..a3c4d8a82 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -35,7 +35,7 @@ def testExecutable(env,tree, exeName=None, obj=None): obj = srcSubtree(env,tree, isShared=False) # use all sourcefiles found in subtree if not exeName: exeName = 'test-%s' % tree - return env.LumieraExe('#$TARDIR/'+exeName, obj + core) + return env.LumieraExe('#$TARGDIR/'+exeName, obj + core) def testCollection(env,dir): @@ -44,7 +44,7 @@ def testCollection(env,dir): """ srcpatt = ['test-*.c'] exeName = lambda p: path.basename(path.splitext(p)[0]) - buildIt = lambda p: env.LumieraExe("#$TARDIR/"+exeName(p), [p] + core) + buildIt = lambda p: env.LumieraExe("#$TARGDIR/"+exeName(p), [p] + core) return [buildIt(f) for f in scanSubtree(dir,srcpatt)] @@ -67,7 +67,7 @@ artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # for creating a Valgrind-Suppression file -vgsuppr = env.LumieraExe('#$TARDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms +vgsuppr = env.LumieraExe('#$TARGDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms artifacts['tools'] += [vgsuppr] Depends(ts,vgsuppr) @@ -103,10 +103,10 @@ if testsuites: testEnv['ENV']['TEST_CONF'] = env.File("test.conf").abspath -testDir = env.Dir('#$TARDIR') +testDir = env.Dir('#$TARGDIR') runTest = env.File("test.sh").abspath -runTs = testEnv.Command('#$TARDIR/,testlog', ts, runTest, chdir=testDir) +runTs = testEnv.Command('#$TARGDIR/,testlog', ts, runTest, chdir=testDir) From ab481a80b8b02ef4e0b98fa68a028758c684e0b5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Jan 2011 16:47:03 +0100 Subject: [PATCH 111/140] better implementation of Lumiera custom SCons builders, wrapping the predefined builders --- admin/scons/LumieraEnvironment.py | 92 +++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index fa1320451..b7d406ed8 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -47,6 +47,7 @@ class LumieraEnvironment(Environment): self.Tool("ToolDistCC") self.Tool("ToolCCache") RegisterIcon_Builder(self) + register_LumieraCustomBuilders(self) def Configure (self, *args, **kw): kw['env'] = self @@ -229,3 +230,94 @@ def RegisterIcon_Builder(env): env.Append(BUILDERS = {'IconRender' : buildIcon}) env.AddMethod(IconCopy) + + + +class WrappedStandardExeBuilder(SCons.Util.Proxy): + """ Helper to add customisations and default configurations to SCons standard builders. + The original builder object is wrapped and most calls are simply forwarded to this + wrapped object by Python magic. But some calls are intecepted in order to inject + suitalbe default configuration based on the project setup. + """ + + def __init__(self, originalBuilder): + SCons.Util.Proxy.__init__ (self, originalBuilder) + + def __call__(self, env, target=None, source=None, **kw): + """ when the builder gets invoked from the SConscript... + create a clone environment for specific configuration + and then pass on the call to the wrapped original builder + """ + customisedEnv = self.getCustomEnvironment(env, target=target, **kw) # defined in subclasses + return self.get().__call__ (customisedEnv, target, source, **kw) + + +class LumieraExeBuilder(WrappedStandardExeBuilder): + + def getCustomEnvironment(self, lumiEnv, **kw): + """ augments the built-in Program() builder to add a fixed rpath based on $ORIGIN + That is: after searching LD_LIBRARY_PATH, but before the standard linker search, + the directory relative to the position of the executable ($ORIGIN) is searched. + This search path is active not only for the executable, but for all libraries + it is linked with. + @note: enabling the new ELF dynamic tags. This causes a DT_RUNPATH to be set, + which results in LD_LIBRARY_PATH being searched *before* the RPATH + """ + custEnv = lumiEnv.Clone() + custEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$MODULES,--enable-new-dtags" ) + return custEnv + + +class LumieraModuleBuilder(WrappedStandardExeBuilder): + + def getCustomEnvironment(self, lumiEnv, target, **kw): + """ augments the built-in SharedLibrary() builder to add some tweaks missing in SCons 1.0, + like setting a SONAME proper instead of just passing the relative pathname to the linker + """ + custEnv = lumiEnv.Clone() + custEnv.Append(LINKFLAGS = "-Wl,-soname="+self.defineSoname(target,**kw)) + return custEnv + + + def defineSoname (self, target, **kw): + """ internal helper to extract or guess + a suitable library SONAME, either using an + explicit spec, falling back on the lib filename + """ + if 'soname' in kw: + soname = self.subst(kw['soname']) # explicitely defined by user + else: # else: use the library filename as DT_SONAME + if SCons.Util.is_String(target): + pathname = target.strip() + elif 1 == len(target): + pathname = str(target[0]).strip() + else: + raise SyntaxError("Lumiera Library builder requires exactly one target spec. Found target="+str(target)) + + assert pathname + (dirprefix, libname) = path.split(pathname) + if not libname: + raise ValueError("Library name missing. Only got a directory: "+pathname) + + soname = "${SHLIBPREFIX}%s$SHLIBSUFFIX" % libname + + assert soname + return soname + + +def register_LumieraCustomBuilders (lumiEnv): + """ install the customised builder versions tightly integrated with our buildsystem. + Especially, these builders automatically add the build and installation locations + and set the RPATH and SONAME in a way to allow a relocatable Lumiera directory structure + """ + programBuilder = lumiEnv['BUILDERS']['Program'] + libraryBuilder = lumiEnv['BUILDERS']['SharedLibrary'] + smoduleBuilder = lumiEnv['BUILDERS']['LoadableModule'] + + programBuilder = LumieraExeBuilder (programBuilder) + libraryBuilder = LumieraModuleBuilder (libraryBuilder) + smoduleBuilder = LumieraModuleBuilder (smoduleBuilder) + + lumiEnv['BUILDERS']['Program'] = programBuilder lumiEnv['BUILDERS']['SharedLibrary'] = libraryBuilder lumiEnv['BUILDERS']['LoadableModule'] = smoduleBuilder + + \ No newline at end of file From 609873d90bfa852ccd1dd7fb111d4dc4d9c9264a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Jan 2011 17:00:15 +0100 Subject: [PATCH 112/140] switch to use the new (better) builder implementation especially this means to use the common well-known names again, like "Program" "SharedLibrary". The customisation now happens invisible in LumieraEnvironment. --- SConstruct | 14 +++---- admin/scons/LumieraEnvironment.py | 61 +------------------------------ src/tool/SConscript | 8 ++-- tests/SConscript | 6 +-- 4 files changed, 15 insertions(+), 74 deletions(-) diff --git a/SConstruct b/SConstruct index 8cf377233..4cceb8331 100644 --- a/SConstruct +++ b/SConstruct @@ -338,16 +338,16 @@ def defineBuildTargets(env, artifacts): - lLib = env.LumieraLibrary('$LIBDIR/lumiera', srcSubtree(env,'$SRCDIR/lib')) - lApp = env.LumieraLibrary('$LIBDIR/lumieracommon', srcSubtree(env,'$SRCDIR/common'), LIBS=lLib) - lBack = env.LumieraLibrary('$LIBDIR/lumierabackend', srcSubtree(env,'$SRCDIR/backend')) - lProc = env.LumieraLibrary('$LIBDIR/lumieraproc', srcSubtree(env,'$SRCDIR/proc')) + lLib = env.SharedLibrary('$LIBDIR/lumiera', srcSubtree(env,'$SRCDIR/lib')) + lApp = env.SharedLibrary('$LIBDIR/lumieracommon', srcSubtree(env,'$SRCDIR/common'), LIBS=lLib) + lBack = env.SharedLibrary('$LIBDIR/lumierabackend', srcSubtree(env,'$SRCDIR/backend')) + lProc = env.SharedLibrary('$LIBDIR/lumieraproc', srcSubtree(env,'$SRCDIR/proc')) core = lLib+lApp+lBack+lProc artifacts['corelib'] = core artifacts['support'] = lLib - artifacts['lumiera'] = env.LumieraExe('$TARGDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) + artifacts['lumiera'] = env.Program('$TARGDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) # building Lumiera Plugins envPlu = env.Clone() @@ -368,7 +368,7 @@ def defineBuildTargets(env, artifacts): envGtk.Append(CPPDEFINES='LUMIERA_PLUGIN', LIBS=core) objgui = srcSubtree(envGtk,'$SRCDIR/gui') - guimodule = envGtk.LoadableModule('$LIBDIR/gtk_gui', objgui, SHLIBPREFIX='', SHLIBSUFFIX='.lum') + guimodule = envGtk.LumieraPlugin('$LIBDIR/gtk_gui', objgui, SHLIBPREFIX='', SHLIBSUFFIX='.lum') artifacts['gui'] = ( guimodule + env.Install('$TARGDIR', env.Glob('$SRCDIR/gui/*.rc')) + artifacts['icons'] @@ -441,8 +441,6 @@ artifacts = {} # 'gui' : the GTK UI (plugin) # 'plugins' : plugin shared lib # 'tools' : small tool applications (e.g mpegtoc) -# 'src,tar' : source tree as tarball (without doc) -# 'doc.tar' : uml model, wiki, dev docu (no src) definePackagingTargets(env, artifacts) defineBuildTargets(env, artifacts) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index b7d406ed8..4e3a590f4 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -109,65 +109,6 @@ class LumieraEnvironment(Environment): action = Action(makeLink,reportLink) self.Command (target,source, action) - - def defineSoname (self, *args,**kw): - """ internal helper to extract or guess - a suitable library SONAME, either using an - explicit spec, falling back on the lib filename - """ - if 'soname' in kw: - soname = self.subst(kw['soname']) # explicitely defined by user - else: - if len(args) > 0: - pathname = args[0] - elif 'target' in kw: - pathname = kw['target'] - else: - raise SyntaxError("Library builder requires target spec. Arguments: %s %s" % (args,kw)) - (dirprefix, libname) = path.split(pathname) - if not libname: - raise ValueError("Library name missing. Only got a directory: "+pathname) - - libname = "${SHLIBPREFIX}%s$SHLIBSUFFIX" % libname - soname = self.subst(libname) # else: use the library filename as DT_SONAME - assert soname - return soname - - - def LumieraLibrary (self, *args,**kw): - """ augments the built-in SharedLibrary() builder to add - some tweaks missing in SCons 1.0, like setting a SONAME proper - instead of just passing the relative pathname to the linker - """ - subEnv = self.Clone() - subEnv.Append(LINKFLAGS = "-Wl,-soname="+self.defineSoname(*args,**kw)) - - libBuilder = self.get_builder('SharedLibrary') - return libBuilder(subEnv, *args,**kw); # invoke the predefined builder on the augmented environment - - - def LumieraPlugin (self, *args,**kw): - """ builds a shared library, autmented by some defaults for lumiera plugins. - """ - subEnv = self.Clone() - subEnv.Append(LINKFLAGS = "-Wl,-soname="+self.defineSoname(*args,**kw)) - - libBuilder = self.get_builder('LoadableModule') - return libBuilder(subEnv, *args,**kw); # invoke the predefined builder on the augmented environment - - - def LumieraExe (self, *args,**kw): - """ augments the built-in Program() builder to add a fixed rpath based on $ORIGIN - That is: after searching LD_LIBRARY_PATH, but before the standard linker search, - the directory relative to the position of the executable ($ORIGIN) is searched. - This search path is active not only for the executable, but for all libraries - it is linked with - """ - subEnv = self.Clone() - subEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$MODULES,--enable-new-dtags" ) - - programBuilder = self.get_builder('Program') - return programBuilder (subEnv, *args,**kw); @@ -317,7 +258,9 @@ def register_LumieraCustomBuilders (lumiEnv): programBuilder = LumieraExeBuilder (programBuilder) libraryBuilder = LumieraModuleBuilder (libraryBuilder) smoduleBuilder = LumieraModuleBuilder (smoduleBuilder) + lpluginBuilder = LumieraModuleBuilder (smoduleBuilder) lumiEnv['BUILDERS']['Program'] = programBuilder lumiEnv['BUILDERS']['SharedLibrary'] = libraryBuilder lumiEnv['BUILDERS']['LoadableModule'] = smoduleBuilder + lumiEnv['BUILDERS']['LumieraPlugin'] = lpluginBuilder \ No newline at end of file diff --git a/src/tool/SConscript b/src/tool/SConscript index bab7e6497..e1ca2394b 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -12,12 +12,12 @@ envSvg.mergeConf(['librsvg-2.0']) envSvg.Append(LIBS=support_lib) -luidgen = env.LumieraExe('#$TARGDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs -rsvg = envSvg.LumieraExe('#$TARGDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) +luidgen = env.Program('#$TARGDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs +rsvg = envSvg.Program('#$TARGDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) # build additional test and administrative tools.... -artifacts['tools'] = [ env.LumieraExe('#$TARGDIR/hello-world','hello.c') #### hello world (checks C build) - + env.LumieraExe('#$TARGDIR/try', 'try.cpp') #### to try out some feature... +artifacts['tools'] = [ env.Program('#$TARGDIR/hello-world','hello.c') #### hello world (checks C build) + + env.Program('#$TARGDIR/try', 'try.cpp') #### to try out some feature... # + luidgen + rsvg ] diff --git a/tests/SConscript b/tests/SConscript index a3c4d8a82..f9079f404 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -35,7 +35,7 @@ def testExecutable(env,tree, exeName=None, obj=None): obj = srcSubtree(env,tree, isShared=False) # use all sourcefiles found in subtree if not exeName: exeName = 'test-%s' % tree - return env.LumieraExe('#$TARGDIR/'+exeName, obj + core) + return env.Program('#$TARGDIR/'+exeName, obj + core) def testCollection(env,dir): @@ -44,7 +44,7 @@ def testCollection(env,dir): """ srcpatt = ['test-*.c'] exeName = lambda p: path.basename(path.splitext(p)[0]) - buildIt = lambda p: env.LumieraExe("#$TARGDIR/"+exeName(p), [p] + core) + buildIt = lambda p: env.Program("#$TARGDIR/"+exeName(p), [p] + core) return [buildIt(f) for f in scanSubtree(dir,srcpatt)] @@ -67,7 +67,7 @@ artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # for creating a Valgrind-Suppression file -vgsuppr = env.LumieraExe('#$TARGDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms +vgsuppr = env.Program('#$TARGDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms artifacts['tools'] += [vgsuppr] Depends(ts,vgsuppr) From abf1bc776b21f87fc0682adc1ec3efa97b13c4e6 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Jan 2011 18:56:51 +0100 Subject: [PATCH 113/140] SCons: remove all explicit target and install specifications now superfluous, because our custom builder handles that automatically --- SConstruct | 33 ++++++------- admin/scons/Buildhelper.py | 6 +-- admin/scons/LumieraEnvironment.py | 80 ++++++++++++++++++++++++------- src/tool/SConscript | 8 ++-- tests/SConscript | 6 +-- 5 files changed, 86 insertions(+), 47 deletions(-) diff --git a/SConstruct b/SConstruct index 4cceb8331..011fc6996 100644 --- a/SConstruct +++ b/SConstruct @@ -38,10 +38,12 @@ LIBDIR = 'target/modules' ####### buildExe = '#$TARGDIR' buildLib = '#$TARGDIR/modules' +buildPlug = '#$TARGDIR/modules' buildIcon = '#$TARGDIR/icons' buildConf = '#$TARGDIR/config' installExe = '#$DESTDIR/lib/lumiera' installLib = '#$DESTDIR/lib/lumiera/modules' +installPlug = '#$DESTDIR/lib/lumiera/modules' installIcon = '#$DESTDIR/share/lumiera/icons' installConf = '#$DESTDIR/share/lumiera/config' @@ -338,16 +340,16 @@ def defineBuildTargets(env, artifacts): - lLib = env.SharedLibrary('$LIBDIR/lumiera', srcSubtree(env,'$SRCDIR/lib')) - lApp = env.SharedLibrary('$LIBDIR/lumieracommon', srcSubtree(env,'$SRCDIR/common'), LIBS=lLib) - lBack = env.SharedLibrary('$LIBDIR/lumierabackend', srcSubtree(env,'$SRCDIR/backend')) - lProc = env.SharedLibrary('$LIBDIR/lumieraproc', srcSubtree(env,'$SRCDIR/proc')) + lLib = env.SharedLibrary('lumiera', srcSubtree(env,'$SRCDIR/lib'), install=True) + lApp = env.SharedLibrary('lumieracommon', srcSubtree(env,'$SRCDIR/common'), install=True, LIBS=lLib) + lBack = env.SharedLibrary('lumierabackend', srcSubtree(env,'$SRCDIR/backend'),install=True) + lProc = env.SharedLibrary('lumieraproc', srcSubtree(env,'$SRCDIR/proc'), install=True) core = lLib+lApp+lBack+lProc artifacts['corelib'] = core artifacts['support'] = lLib - artifacts['lumiera'] = env.Program('$TARGDIR/lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core) + artifacts['lumiera'] = env.Program('lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core, install=True) # building Lumiera Plugins envPlu = env.Clone() @@ -365,10 +367,10 @@ def defineBuildTargets(env, artifacts): # the Lumiera GTK GUI envGtk = env.Clone() envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm']) - envGtk.Append(CPPDEFINES='LUMIERA_PLUGIN', LIBS=core) + envGtk.Append(LIBS=core) objgui = srcSubtree(envGtk,'$SRCDIR/gui') - guimodule = envGtk.LumieraPlugin('$LIBDIR/gtk_gui', objgui, SHLIBPREFIX='', SHLIBSUFFIX='.lum') + guimodule = envGtk.LumieraPlugin('gtk_gui', objgui, install=True) artifacts['gui'] = ( guimodule + env.Install('$TARGDIR', env.Glob('$SRCDIR/gui/*.rc')) + artifacts['icons'] @@ -404,20 +406,13 @@ def definePostBuildTargets(env, artifacts): def defineInstallTargets(env, artifacts): - """ define artifacts to be installed into target locations. + """ define additional artifacts to be installed into target locations. + @note: we use customised SCons builders defining install targets + for all executables automatically. see LumieraEnvironment.py """ - binDir = '$DESTDIR/bin/' - lumDir = '$DESTDIR/lib/lumiera/' - modDir = '$DESTDIR/lib/lumiera/$MODULES/' - shaDir = '$DESTDIR/share/lumiera/' - env.Install(dir = modDir, source=artifacts['corelib']) - env.Install(dir = modDir, source=artifacts['plugins']) - env.Install(dir = modDir, source=artifacts['guimodule']) - lumi = env.Install(dir = lumDir, source=artifacts['lumiera']) - tool = env.Install(dir = lumDir, source=artifacts['tools']) - env.SymLink(binDir+"lumiera",lumi,"../lib/lumiera/lumiera") + env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera',"../lib/lumiera/lumiera") - env.Install(dir = shaDir, source="data/config/dummy_lumiera.ini") ### TODO should become a resource builder + env.Install(dir = env.path.installConf, source="data/config/dummy_lumiera.ini") ### TODO should become a resource builder # env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) ##################################################################### diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 387e0fccb..f491f03a0 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -148,13 +148,13 @@ def getDirname(dir): -def createPlugins(env, dir): +def createPlugins(env, dir, **kw): """ investigate the given source directory to identify all contained source trees. @return: a list of build nodes defining a plugin for each of these source trees. """ - return [env.LumieraPlugin( '#$TARGDIR/$MODULES/%s' % getDirname(tree) + return [env.LumieraPlugin( getDirname(tree) , srcSubtree(env, tree) - , SHLIBPREFIX='', SHLIBSUFFIX='.lum' + , **kw ) for tree in findSrcTrees(dir) ] diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 4e3a590f4..b32f1af1f 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -43,7 +43,7 @@ class LumieraEnvironment(Environment): self.path = Record (pathConfig) self.libInfo = {} self.Tool("BuilderGCH") - self.Tool("BuilderDoxygen") + self.Tool("BuilderDoxygen") self.Tool("ToolDistCC") self.Tool("ToolCCache") RegisterIcon_Builder(self) @@ -108,7 +108,6 @@ class LumieraEnvironment(Environment): action = Action(makeLink,reportLink) self.Command (target,source, action) - @@ -133,7 +132,8 @@ class LumieraConfigContext(ConfigBase): -###### Lumiera custom tools and builders ######################## +############################################################################### +####### Lumiera custom tools and builders ##################################### def RegisterIcon_Builder(env): @@ -163,12 +163,12 @@ def RegisterIcon_Builder(env): subdir = getDirname(source) return env.Install("$TARGDIR/%s" % subdir, source) - + buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS") , single_source = True - , emitter = createIconTargets + , emitter = createIconTargets ) - env.Append(BUILDERS = {'IconRender' : buildIcon}) + env.Append(BUILDERS = {'IconRender' : buildIcon}) env.AddMethod(IconCopy) @@ -190,7 +190,31 @@ class WrappedStandardExeBuilder(SCons.Util.Proxy): and then pass on the call to the wrapped original builder """ customisedEnv = self.getCustomEnvironment(env, target=target, **kw) # defined in subclasses - return self.get().__call__ (customisedEnv, target, source, **kw) + buildTarget = self.buildLocation(customisedEnv, target) + buildTarget = self.invokeOriginalBuilder (customisedEnv, buildTarget, source, **kw) + return buildTarget + self.installTarget(customisedEnv, buildTarget, **kw) + + + def invokeOriginalBuilder(self, env, target, source, **kw): + return self.get().__call__ (env, target, source, **kw) + + def buildLocation(self, env, target): + """ prefix project output directory """ + prefix = self.getBuildDestination(env) + return list(prefix+str(name) for name in target) + + def installTarget(self, env, buildTarget, **kw): + """ create an additional installation target + for the generated executable artifact + """ + indeedInstall = lambda p: p and p.get('install') + + if indeedInstall(kw): + return env.Install (dir = self.getInstallDestination(env), source=buildTarget) + else: + return [] + + class LumieraExeBuilder(WrappedStandardExeBuilder): @@ -207,6 +231,11 @@ class LumieraExeBuilder(WrappedStandardExeBuilder): custEnv = lumiEnv.Clone() custEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$MODULES,--enable-new-dtags" ) return custEnv + + def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildExe + def getInstallDestination(self, lumiEnv): return lumiEnv.path.installExe + + class LumieraModuleBuilder(WrappedStandardExeBuilder): @@ -219,6 +248,9 @@ class LumieraModuleBuilder(WrappedStandardExeBuilder): custEnv.Append(LINKFLAGS = "-Wl,-soname="+self.defineSoname(target,**kw)) return custEnv + def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildLib + def getInstallDestination(self, lumiEnv): return lumiEnv.path.installLib + def defineSoname (self, target, **kw): """ internal helper to extract or guess @@ -246,21 +278,33 @@ class LumieraModuleBuilder(WrappedStandardExeBuilder): return soname + +class LumieraPluginBuilder(LumieraModuleBuilder): + + def getCustomEnvironment(self, lumiEnv, target, **kw): + """ in addition to the ModuleBuilder, define the Lumiera plugin suffix + """ + custEnv = LumieraModuleBuilder.getCustomEnvironment(self, lumiEnv, target, **kw) + custEnv.Append (CPPDEFINES='LUMIERA_PLUGIN') + custEnv.Replace(SHLIBPREFIX='', SHLIBSUFFIX='.lum') + return custEnv + + def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildPlug + def getInstallDestination(self, lumiEnv): return lumiEnv.path.installPlug + + + def register_LumieraCustomBuilders (lumiEnv): """ install the customised builder versions tightly integrated with our buildsystem. Especially, these builders automatically add the build and installation locations and set the RPATH and SONAME in a way to allow a relocatable Lumiera directory structure """ - programBuilder = lumiEnv['BUILDERS']['Program'] - libraryBuilder = lumiEnv['BUILDERS']['SharedLibrary'] - smoduleBuilder = lumiEnv['BUILDERS']['LoadableModule'] + programBuilder = LumieraExeBuilder (lumiEnv['BUILDERS']['Program']) + libraryBuilder = LumieraModuleBuilder (lumiEnv['BUILDERS']['SharedLibrary']) + smoduleBuilder = LumieraModuleBuilder (lumiEnv['BUILDERS']['LoadableModule']) + lpluginBuilder = LumieraPluginBuilder (lumiEnv['BUILDERS']['LoadableModule']) - programBuilder = LumieraExeBuilder (programBuilder) - libraryBuilder = LumieraModuleBuilder (libraryBuilder) - smoduleBuilder = LumieraModuleBuilder (smoduleBuilder) - lpluginBuilder = LumieraModuleBuilder (smoduleBuilder) - - lumiEnv['BUILDERS']['Program'] = programBuilder lumiEnv['BUILDERS']['SharedLibrary'] = libraryBuilder lumiEnv['BUILDERS']['LoadableModule'] = smoduleBuilder + lumiEnv['BUILDERS']['Program'] = programBuilder + lumiEnv['BUILDERS']['SharedLibrary'] = libraryBuilder + lumiEnv['BUILDERS']['LoadableModule'] = smoduleBuilder lumiEnv['BUILDERS']['LumieraPlugin'] = lpluginBuilder - - \ No newline at end of file diff --git a/src/tool/SConscript b/src/tool/SConscript index e1ca2394b..4fe3a17c4 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -12,12 +12,12 @@ envSvg.mergeConf(['librsvg-2.0']) envSvg.Append(LIBS=support_lib) -luidgen = env.Program('#$TARGDIR/luidgen', 'luidgen.c', LIBS=support_lib) ## for generating Lumiera-UIDs -rsvg = envSvg.Program('#$TARGDIR/rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) +#luidgen = env.Program('luidgen', 'luidgen.c', LIBS=support_lib, install=True) ## for generating Lumiera-UIDs +rsvg = envSvg.Program('rsvg-convert','rsvg-convert.c') ## for rendering SVG icons (uses librsvg) # build additional test and administrative tools.... -artifacts['tools'] = [ env.Program('#$TARGDIR/hello-world','hello.c') #### hello world (checks C build) - + env.Program('#$TARGDIR/try', 'try.cpp') #### to try out some feature... +artifacts['tools'] = [ env.Program('hello-world','hello.c', install=True) #### hello world (checks C build) + + env.Program('try', 'try.cpp') #### to try out some feature... # + luidgen + rsvg ] diff --git a/tests/SConscript b/tests/SConscript index f9079f404..ac00d1f13 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -35,7 +35,7 @@ def testExecutable(env,tree, exeName=None, obj=None): obj = srcSubtree(env,tree, isShared=False) # use all sourcefiles found in subtree if not exeName: exeName = 'test-%s' % tree - return env.Program('#$TARGDIR/'+exeName, obj + core) + return env.Program(exeName, obj + core) def testCollection(env,dir): @@ -44,7 +44,7 @@ def testCollection(env,dir): """ srcpatt = ['test-*.c'] exeName = lambda p: path.basename(path.splitext(p)[0]) - buildIt = lambda p: env.Program("#$TARGDIR/"+exeName(p), [p] + core) + buildIt = lambda p: env.Program(exeName(p), [p] + core) return [buildIt(f) for f in scanSubtree(dir,srcpatt)] @@ -67,7 +67,7 @@ artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # for creating a Valgrind-Suppression file -vgsuppr = env.Program('#$TARGDIR/vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms +vgsuppr = env.Program('vgsuppression','tool/vgsuppression.c', LIBS=core) ## for suppressing false valgrind alarms artifacts['tools'] += [vgsuppr] Depends(ts,vgsuppr) From daef6c767625fa1ddd93db2f76a413688593a406 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Jan 2011 19:20:02 +0100 Subject: [PATCH 114/140] SCons: remove unnecessary indirections $SRCDIR, $MODULES, $ICONDIR etc these don't create real flexibility and make definitions less readable --- SConstruct | 58 +++++++++++++------------------ admin/scons/Buildhelper.py | 2 +- admin/scons/LumieraEnvironment.py | 2 +- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/SConstruct b/SConstruct index 011fc6996..ae291d2d3 100644 --- a/SConstruct +++ b/SConstruct @@ -29,13 +29,9 @@ TOOLDIR = './admin/scons' SCRIPTDIR = './admin' OPTCACHE = 'optcache' CUSTOPTFILE = 'custom-options' -####### -SRCDIR = 'src' -TESTDIR = 'tests' -ICONDIR = 'icons' -MODULES = 'modules' -LIBDIR = 'target/modules' -####### + +srcIcon = 'icons' +srcConf = 'data/config' buildExe = '#$TARGDIR' buildLib = '#$TARGDIR/modules' buildPlug = '#$TARGDIR/modules' @@ -89,11 +85,7 @@ def setupBasicEnvironment(localDefinitions): env.Append ( CCCOM=' -std=gnu99') env.Append ( SHCCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honour CFLAGS, only CCFLAGS - env.Replace( SRCDIR=SRCDIR - , LIBDIR=LIBDIR - , MODULES=MODULES - , ICONDIR=ICONDIR - , CPPPATH=["#"+SRCDIR] # used to find includes, "#" means always absolute to build-root + env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root , CPPDEFINES=['-DLUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines , CCFLAGS='-Wall -Wextra ' , CFLAGS='-std=gnu99' @@ -108,8 +100,8 @@ def setupBasicEnvironment(localDefinitions): appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') # setup search path for Lumiera plugins - appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:./$MODULES\\"' - ,'LUMIERA_PLUGIN_PATH=\\"$DESTDIR/lib/lumiera/:./$MODULES\\"') + appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:./modules\\"' + ,'LUMIERA_PLUGIN_PATH=\\"$DESTDIR/lib/lumiera/:./modules\\"') appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') @@ -198,10 +190,8 @@ Special Targets: testcode: additionally compile the Testsuite check : build and run the Testsuite doc : generate documentation (Doxygen) + all : build and testcode and doc install : install created artifacts at PREFIX - src.tar : create source tarball - doc.tar : create developer doc tarball - tar : create all tarballs Configuration Options: """ @@ -334,22 +324,22 @@ def defineBuildTargets(env, artifacts): """ # use PCH to speed up building // disabled for now due to strange failures -# env['GCH'] = ( env.PrecompiledHeader('$SRCDIR/pre.hpp') -# + env.PrecompiledHeader('$SRCDIR/pre_a.hpp') +# env['GCH'] = ( env.PrecompiledHeader('src/pre.hpp') +# + env.PrecompiledHeader('src/pre_a.hpp') # ) - lLib = env.SharedLibrary('lumiera', srcSubtree(env,'$SRCDIR/lib'), install=True) - lApp = env.SharedLibrary('lumieracommon', srcSubtree(env,'$SRCDIR/common'), install=True, LIBS=lLib) - lBack = env.SharedLibrary('lumierabackend', srcSubtree(env,'$SRCDIR/backend'),install=True) - lProc = env.SharedLibrary('lumieraproc', srcSubtree(env,'$SRCDIR/proc'), install=True) + lLib = env.SharedLibrary('lumiera', srcSubtree(env,'src/lib'), install=True) + lApp = env.SharedLibrary('lumieracommon', srcSubtree(env,'src/common'), install=True, LIBS=lLib) + lBack = env.SharedLibrary('lumierabackend', srcSubtree(env,'src/backend'),install=True) + lProc = env.SharedLibrary('lumieraproc', srcSubtree(env,'src/proc'), install=True) core = lLib+lApp+lBack+lProc artifacts['corelib'] = core artifacts['support'] = lLib - artifacts['lumiera'] = env.Program('lumiera', ['$SRCDIR/lumiera/main.cpp'], LIBS=core, install=True) + artifacts['lumiera'] = env.Program('lumiera', ['src/lumiera/main.cpp'], LIBS=core, install=True) # building Lumiera Plugins envPlu = env.Clone() @@ -357,8 +347,8 @@ def defineBuildTargets(env, artifacts): artifacts['plugins'] = [] # currently none # render and install Icons - vector_icon_dir = env.subst('$ICONDIR/svg') - prerendered_icon_dir = env.subst('$ICONDIR/prerendered') + vector_icon_dir = env.path.srcIcon+'svg' + prerendered_icon_dir = env.path.srcIcon+'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'])] ) @@ -369,17 +359,17 @@ def defineBuildTargets(env, artifacts): envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm']) envGtk.Append(LIBS=core) - objgui = srcSubtree(envGtk,'$SRCDIR/gui') + objgui = srcSubtree(envGtk,'src/gui') guimodule = envGtk.LumieraPlugin('gtk_gui', objgui, install=True) artifacts['gui'] = ( guimodule - + env.Install('$TARGDIR', env.Glob('$SRCDIR/gui/*.rc')) + + env.Install('$TARGDIR', env.Glob('src/gui/*.rc')) + artifacts['icons'] ) artifacts['guimodule'] = guimodule ###TODO better organisation of GUI components # call subdir SConscript(s) for independent components - SConscript(dirs=[SRCDIR+'/tool'], exports='env artifacts core') - SConscript(dirs=[TESTDIR], exports='env envPlu artifacts core') + SConscript(dirs=['src/tool'], exports='env artifacts core') + SConscript(dirs=['tests'], exports='env envPlu artifacts core') @@ -396,13 +386,13 @@ def definePostBuildTargets(env, artifacts): env.Default('build') # additional files to be cleaned when cleaning 'build' env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) - env.Clean ('build', [ '$SRCDIR/pre.gch' ]) + env.Clean ('build', [ 'src/pre.gch' ]) doxydoc = artifacts['doxydoc'] = env.Doxygen('doc/devel/Doxyfile') env.Alias ('doc', doxydoc) env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - allbu = env.Alias('allbuild', build+artifacts['testsuite']+doxydoc) + allbu = env.Alias('all', build+artifacts['testsuite']+doxydoc) def defineInstallTargets(env, artifacts): @@ -410,9 +400,9 @@ def defineInstallTargets(env, artifacts): @note: we use customised SCons builders defining install targets for all executables automatically. see LumieraEnvironment.py """ - env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera',"../lib/lumiera/lumiera") + env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') - env.Install(dir = env.path.installConf, source="data/config/dummy_lumiera.ini") ### TODO should become a resource builder + env.Install(dir = env.path.installConf, source=env.path.srcConf+'dummy_lumiera.ini') ### TODO should become a resource builder # env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) ##################################################################### diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index f491f03a0..2c5156863 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -227,7 +227,7 @@ def extract_localPathDefs (localDefs): """ def relevantPathDefs (mapping): for (k,v) in mapping.items(): - if k.startswith('build') or k.startswith('install') and Util.is_String(v): + if (k.startswith('src') or k.startswith('build') or k.startswith('install')) and Util.is_String(v): v = v.strip() if not v.endswith('/'): v += '/' yield (k,v) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index b32f1af1f..c4bf30172 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -229,7 +229,7 @@ class LumieraExeBuilder(WrappedStandardExeBuilder): which results in LD_LIBRARY_PATH being searched *before* the RPATH """ custEnv = lumiEnv.Clone() - custEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/$MODULES,--enable-new-dtags" ) + custEnv.Append( LINKFLAGS = "-Wl,-rpath=\\$$ORIGIN/modules,--enable-new-dtags" ) return custEnv def getBuildDestination(self, lumiEnv): return lumiEnv.path.buildExe From 9cb03c7015352ddce33dbcb26529fe2bc1ffacb3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Jan 2011 19:43:51 +0100 Subject: [PATCH 115/140] Fix installation triggered already by build target --- SConstruct | 7 ++----- admin/scons/LumieraEnvironment.py | 9 ++++++--- tests/SConscript | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/SConstruct b/SConstruct index ae291d2d3..95797748d 100644 --- a/SConstruct +++ b/SConstruct @@ -377,11 +377,6 @@ def definePostBuildTargets(env, artifacts): """ define further actions after the core build (e.g. Documentaion). define alias targets to trigger the installing. """ - ib = env.Alias('install-bin', '$DESTDIR/bin') - il = env.Alias('install-lib', '$DESTDIR/lib') - id = env.Alias('install-dat', '$DESTDIR/share') - env.Alias('install', [ib, il, id]) - build = env.Alias('build', artifacts['lumiera']+artifacts['gui']+artifacts['plugins']+artifacts['tools']) env.Default('build') # additional files to be cleaned when cleaning 'build' @@ -404,6 +399,8 @@ def defineInstallTargets(env, artifacts): env.Install(dir = env.path.installConf, source=env.path.srcConf+'dummy_lumiera.ini') ### TODO should become a resource builder # env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) + + env.Alias('install', '$DESTDIR') ##################################################################### diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index c4bf30172..12f5dbcc7 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -187,12 +187,15 @@ class WrappedStandardExeBuilder(SCons.Util.Proxy): def __call__(self, env, target=None, source=None, **kw): """ when the builder gets invoked from the SConscript... create a clone environment for specific configuration - and then pass on the call to the wrapped original builder + and then pass on the call to the wrapped original builder. + Automatically define installation targets for build results. + @note only returning the build targets, not the install targets """ customisedEnv = self.getCustomEnvironment(env, target=target, **kw) # defined in subclasses buildTarget = self.buildLocation(customisedEnv, target) - buildTarget = self.invokeOriginalBuilder (customisedEnv, buildTarget, source, **kw) - return buildTarget + self.installTarget(customisedEnv, buildTarget, **kw) + buildTarget = self.invokeOriginalBuilder (customisedEnv, buildTarget, source, **kw) + self.installTarget(customisedEnv, buildTarget, **kw) + return buildTarget def invokeOriginalBuilder(self, env, target, source, **kw): diff --git a/tests/SConscript b/tests/SConscript index ac00d1f13..cc6078878 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -23,8 +23,8 @@ envPlu.Append(CPPPATH='#/.') def testExecutable(env,tree, exeName=None, obj=None): """ declare all targets needed to create a standalone - Test executable of the given Sub-tree. Note that - each subdir is built in its own Environment. + Test executable of the given Sub-tree. + @note this tree uses separate Environment/Includepath """ env = env.Clone() env.Append(CPPPATH=tree) # add Subdir to Includepath From d9f90c2c0419cc6ff01f38c8652d58d83ab38eab Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 30 Jan 2011 22:12:55 +0100 Subject: [PATCH 116/140] SCons: finish reworking buildsystem to rely on custom builders. All target paths and install targets now defined automatically, most of the buildscript just using plain sourcefile names --- SConstruct | 67 ++++++++++++----------- admin/scons/Buildhelper.py | 15 ++++-- admin/scons/LumieraEnvironment.py | 90 ++++++++++++++++++++----------- tests/SConscript | 6 +-- 4 files changed, 108 insertions(+), 70 deletions(-) diff --git a/SConstruct b/SConstruct index 95797748d..6eadc765b 100644 --- a/SConstruct +++ b/SConstruct @@ -22,35 +22,39 @@ ##################################################################### +# NOTE: scons -h for help. +# Read more about the SCons build system at: http://www.scons.org +# Basically, this script just /defines/ the components and how they +# fit together. SCons will derive the necessary build steps. + + #-----------------------------------Configuration TARGDIR = 'target' VERSION = '0.1+pre.01' -TOOLDIR = './admin/scons' +TOOLDIR = './admin/scons' # SCons plugins SCRIPTDIR = './admin' OPTCACHE = 'optcache' CUSTOPTFILE = 'custom-options' +# these are accessible via env.path.xxxx srcIcon = 'icons' srcConf = 'data/config' buildExe = '#$TARGDIR' buildLib = '#$TARGDIR/modules' buildPlug = '#$TARGDIR/modules' buildIcon = '#$TARGDIR/icons' +buildUIRes = '#$TARGDIR/' buildConf = '#$TARGDIR/config' installExe = '#$DESTDIR/lib/lumiera' installLib = '#$DESTDIR/lib/lumiera/modules' installPlug = '#$DESTDIR/lib/lumiera/modules' installIcon = '#$DESTDIR/share/lumiera/icons' -installConf = '#$DESTDIR/share/lumiera/config' +installUIRes = '#$DESTDIR/share/lumiera/' +installConf = '#$DESTDIR/lib/lumiera/config' localDefinitions = locals() #-----------------------------------Configuration -# NOTE: scons -h for help. -# Read more about the SCons build system at: http://www.scons.org -# Basically, this script just /defines/ the components and how they -# fit together. SCons will derive the necessary build steps. - import os @@ -76,7 +80,7 @@ def setupBasicEnvironment(localDefinitions): vars = defineCmdlineVariables() env = LumieraEnvironment(variables=vars ,toolpath = [TOOLDIR] - ,pathConfig = extract_localPathDefs(localDefinitions) + ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe ,TARGDIR = TARGDIR ,DESTDIR = '$INSTALLDIR/$PREFIX' ,VERSION = VERSION @@ -239,10 +243,10 @@ def configurePlatform(env): conf.env.mergeConf('nobugmt') if not conf.CheckCXXHeader('tr1/memory'): - problems.append('We rely on the std::tr1 proposed standard extension for shared_ptr.') + problems.append('We rely on the std::tr1 standard C++ extension for shared_ptr.') if not conf.CheckCXXHeader('boost/config.hpp'): - problems.append('We need the C++ boost-lib.') + problems.append('We need the C++ boost-libraries.') else: if not conf.CheckCXXHeader('boost/shared_ptr.hpp'): problems.append('We need boost::shared_ptr (shared_ptr.hpp).') @@ -264,16 +268,16 @@ def configurePlatform(env): conf.env.mergeConf('gavl') if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): - problems.append('Unable to configure GTK--, exiting.') + problems.append('Unable to configure GTK--') if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): - problems.append('Unable to configure Lib glib--, exiting.') + problems.append('Unable to configure Lib glib--') if not conf.CheckPkgConfig('gthread-2.0', '2.12.4'): problems.append('Need gthread support lib for glib-- based thread handling.') if not conf.CheckPkgConfig('cairomm-1.0', 0.6): - problems.append('Unable to configure Cairo--, exiting.') + problems.append('Unable to configure Cairo--') verGDL = '2.27.1' if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): @@ -309,7 +313,7 @@ def configurePlatform(env): -def definePackagingTargets(env, artifacts): +def defineSetupTargets(env, artifacts): """ build operations and targets to be done /before/ compiling. things like creating a source tarball or preparing a version header. """ @@ -339,20 +343,19 @@ def defineBuildTargets(env, artifacts): artifacts['corelib'] = core artifacts['support'] = lLib - artifacts['lumiera'] = env.Program('lumiera', ['src/lumiera/main.cpp'], LIBS=core, install=True) + artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'], LIBS=core, install=True) + + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') + ) # building Lumiera Plugins - envPlu = env.Clone() - envPlu.Append(CPPDEFINES='LUMIERA_PLUGIN') artifacts['plugins'] = [] # currently none # render and install Icons vector_icon_dir = env.path.srcIcon+'svg' prerendered_icon_dir = env.path.srcIcon+'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['icons'] = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] ) - ##TODO make that into a resource builder # the Lumiera GTK GUI envGtk = env.Clone() @@ -362,14 +365,13 @@ def defineBuildTargets(env, artifacts): objgui = srcSubtree(envGtk,'src/gui') guimodule = envGtk.LumieraPlugin('gtk_gui', objgui, install=True) artifacts['gui'] = ( guimodule - + env.Install('$TARGDIR', env.Glob('src/gui/*.rc')) + + [env.GuiResource(f) for f in env.Glob('src/gui/*.rc')] + artifacts['icons'] ) - artifacts['guimodule'] = guimodule ###TODO better organisation of GUI components - + # call subdir SConscript(s) for independent components - SConscript(dirs=['src/tool'], exports='env artifacts core') - SConscript(dirs=['tests'], exports='env envPlu artifacts core') + SConscript(dirs=['src/tool'], exports='env artifacts core') + SConscript(dirs=['tests'], exports='env artifacts core') @@ -377,8 +379,11 @@ def definePostBuildTargets(env, artifacts): """ define further actions after the core build (e.g. Documentaion). define alias targets to trigger the installing. """ - build = env.Alias('build', artifacts['lumiera']+artifacts['gui']+artifacts['plugins']+artifacts['tools']) - env.Default('build') + build = env.Alias('build', ( artifacts['lumiera'] + + artifacts['plugins'] + + artifacts['tools'] + + artifacts['gui'] + )) # additional files to be cleaned when cleaning 'build' env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) env.Clean ('build', [ 'src/pre.gch' ]) @@ -387,7 +392,9 @@ def definePostBuildTargets(env, artifacts): env.Alias ('doc', doxydoc) env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - allbu = env.Alias('all', build+artifacts['testsuite']+doxydoc) + env.Alias ('all', build+artifacts['testsuite']+doxydoc) + env.Default('build') + # SCons default target def defineInstallTargets(env, artifacts): @@ -396,8 +403,6 @@ def defineInstallTargets(env, artifacts): for all executables automatically. see LumieraEnvironment.py """ env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') - - env.Install(dir = env.path.installConf, source=env.path.srcConf+'dummy_lumiera.ini') ### TODO should become a resource builder # env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) env.Alias('install', '$DESTDIR') @@ -424,7 +429,7 @@ artifacts = {} # 'plugins' : plugin shared lib # 'tools' : small tool applications (e.g mpegtoc) -definePackagingTargets(env, artifacts) +defineSetupTargets(env, artifacts) defineBuildTargets(env, artifacts) definePostBuildTargets(env, artifacts) defineInstallTargets(env, artifacts) diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 2c5156863..aed9c07da 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -99,7 +99,7 @@ def findSrcTrees(location, patterns=SRCPATTERNS): After having initially expanded the given location with #globRootdirs, each directory is examined depth first, until encountering a directory containing source files, which then yields a result. Especially, this can be used to traverse - an organisational directory structure and find out all possible source trees of + an organisational directory structure and find out all possible source trees to be built into packages, plugins, individual tool executables etc. @return: the relative path names of all source root dirs found (generator function). """ @@ -138,12 +138,19 @@ def filterNodes(nlist, removeName=None): -def getDirname(dir): - """ extract directory name without leading path """ +def getDirname(dir, basePrefix=None): + """ extract directory name without leading path, + or without the explicitly given basePrefix + """ dir = os.path.realpath(dir) if not os.path.isdir(dir): dir,_ = os.path.split(dir) - _, name = os.path.split(dir) + if basePrefix: + basePrefix = os.path.realpath(basePrefix) + if str(dir).startswith(basePrefix): + name = str(dir)[len(basePrefix):] + else: + _, name = os.path.split(dir) return name diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 12f5dbcc7..82a89b6ae 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -46,7 +46,7 @@ class LumieraEnvironment(Environment): self.Tool("BuilderDoxygen") self.Tool("ToolDistCC") self.Tool("ToolCCache") - RegisterIcon_Builder(self) + register_LumieraResourceBuilder(self) register_LumieraCustomBuilders(self) def Configure (self, *args, **kw): @@ -89,25 +89,6 @@ class LumieraEnvironment(Environment): if alias: self.libInfo[alias] = libInfo return libInfo - - - def SymLink(self, target, source, linktext=None): - """ use python to create a symlink - """ - def makeLink(target,source,env): - if linktext: - dest = linktext - else: - dest = str(source[0]) - link = str(target[0]) - os.symlink(dest, link) - def reportLink(target,source,env): - dest = str(source[0]) - link = str(target[0]) - return "Install link %s -> %s" % (link,dest) - - action = Action(makeLink,reportLink) - self.Command (target,source, action) @@ -136,7 +117,7 @@ class LumieraConfigContext(ConfigBase): ####### Lumiera custom tools and builders ##################################### -def RegisterIcon_Builder(env): +def register_LumieraResourceBuilder(env): """ Registers 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. @@ -147,21 +128,39 @@ def RegisterIcon_Builder(env): def invokeRenderer(target, source, env): source = str(source[0]) - targetdir = env.subst("$TARGDIR") + targetdir = env.subst(env.path.buildIcon) + if targetdir.startswith('#'): targetdir = targetdir[1:] 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])) + targetdir = env.path.buildIcon targetfiles = renderer.getTargetNames(source) # parse SVG - return (["$TARGDIR/%s" % name for name in targetfiles], source) + return ([targetdir+name for name in targetfiles], source) - def IconCopy(env, source): - """Copy icon to corresponding icon dir. """ - subdir = getDirname(source) - return env.Install("$TARGDIR/%s" % subdir, source) + def IconResource(env, source): + """Copy icon pixmap to corresponding icon dir. """ + subdir = getDirname(str(source)) + toBuild = env.path.buildIcon+subdir + toInstall = env.path.installIcon+subdir + env.Install (toInstall, source) + return env.Install(toBuild, source) + + def GuiResource(env, source): + subdir = getDirname(str(source)) + toBuild = env.path.buildUIRes+subdir + toInstall = env.path.installUIRes+subdir + env.Install (toInstall, source) + return env.Install(toBuild, source) + + def ConfigData(env, source): + subdir = getDirname(str(source), env.path.srcConf) # removes source location path prefix + toBuild = env.path.buildConf+subdir + toInstall = env.path.installConf+subdir + env.Install (toInstall, source) + return env.Install(toBuild, source) buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS") @@ -169,7 +168,9 @@ def RegisterIcon_Builder(env): , emitter = createIconTargets ) env.Append(BUILDERS = {'IconRender' : buildIcon}) - env.AddMethod(IconCopy) + env.AddMethod(IconResource) + env.AddMethod(GuiResource) + env.AddMethod(ConfigData) @@ -191,9 +192,9 @@ class WrappedStandardExeBuilder(SCons.Util.Proxy): Automatically define installation targets for build results. @note only returning the build targets, not the install targets """ - customisedEnv = self.getCustomEnvironment(env, target=target, **kw) # defined in subclasses + customisedEnv = self.getCustomEnvironment(env, target=target, **kw) # defined in subclasses buildTarget = self.buildLocation(customisedEnv, target) - buildTarget = self.invokeOriginalBuilder (customisedEnv, buildTarget, source, **kw) + buildTarget = self.invokeOriginalBuilder(customisedEnv, buildTarget, source, **kw) self.installTarget(customisedEnv, buildTarget, **kw) return buildTarget @@ -297,6 +298,9 @@ class LumieraPluginBuilder(LumieraModuleBuilder): + + + def register_LumieraCustomBuilders (lumiEnv): """ install the customised builder versions tightly integrated with our buildsystem. Especially, these builders automatically add the build and installation locations @@ -311,3 +315,27 @@ def register_LumieraCustomBuilders (lumiEnv): lumiEnv['BUILDERS']['SharedLibrary'] = libraryBuilder lumiEnv['BUILDERS']['LoadableModule'] = smoduleBuilder lumiEnv['BUILDERS']['LumieraPlugin'] = lpluginBuilder + + + def SymLink(env, target, source, linktext=None): + """ use python to create a symlink + """ + def makeLink(target,source,env): + if linktext: + dest = linktext + else: + dest = str(source[0]) + link = str(target[0]) + os.symlink(dest, link) + + if linktext: srcSpec=linktext + else: srcSpec='$SOURCE' + action = Action(makeLink, "Install link: $TARGET -> "+srcSpec) + env.Command (target,source, action) + + # adding SymLink direclty as method on the environment object + # Probably that should better be a real builder, but I couldn't figure out + # how to get the linktext through literally, which is necessary for relative links. + # Judging from the sourcecode of SCons.Builder.BuilderBase, there seems to be no way + # to set the executor_kw, which are passed through to the action object. + lumiEnv.AddMethod(SymLink) diff --git a/tests/SConscript b/tests/SConscript index cc6078878..2e7980c68 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -12,13 +12,11 @@ from Buildhelper import scanSubtree from Buildhelper import globRootdirs from Buildhelper import createPlugins -Import('env','envPlu','artifacts','core') +Import('env','artifacts','core') # temp fix to add test.h -- wouldn't it be better to put this header be into src/lib ? env = env.Clone() env.Append(CPPPATH='#/.') # add Rootdir to Includepath, so test/test.h is found -envPlu = envPlu.Clone() -envPlu.Append(CPPPATH='#/.') # temp fix------------- def testExecutable(env,tree, exeName=None, obj=None): @@ -59,7 +57,7 @@ moduledirs = globRootdirs('*') artifacts['testsuite'] = ts = ( [ testExecutable(env, dir) for dir in ['bugs'] ] # was: ['lib','components'] + [ testCollection(env, dir) for dir in moduledirs if not dir in specials] - + createPlugins(envPlu, 'plugin') + + createPlugins(env, 'plugin') + env.File(glob('*.tests')) # depending on the test definition files for test.sh ) From 29e2233e6aed3f1bacbf21183c8c825341abf534 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 31 Jan 2011 05:35:43 +0100 Subject: [PATCH 117/140] an exercise: how to get the path of the current executable Note: this is a non-portable Linux solution --- src/tool/SConscript | 2 +- src/tool/try.cpp | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/tool/SConscript b/src/tool/SConscript index 4fe3a17c4..82b20d96f 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -17,7 +17,7 @@ rsvg = envSvg.Program('rsvg-convert','rsvg-convert.c') # build additional test and administrative tools.... artifacts['tools'] = [ env.Program('hello-world','hello.c', install=True) #### hello world (checks C build) - + env.Program('try', 'try.cpp') #### to try out some feature... + + env.Program('try', 'try.cpp', LIBS=support_lib) #### to try out some feature... # + luidgen + rsvg ] diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 16ad0dad2..112aafaaa 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -15,15 +15,45 @@ // 6/09 - investigating how to build a mixin template providing an operator bool() // 12/9 - tracking down a strange "warning: type qualifiers ignored on function return type" // 1/10 - can we determine at compile time the presence of a certain function (for duck-typing)? +// 1/11 - how to fetch the path of the own executable -- at least under Linux? #include "include/nobugcfg.h" #include +#include + +extern "C" { +#include +} +//#include "lib/error.hpp" +//#include "lib/symbol.hpp" using std::string; using std::cout; +//using lib::Literal; +//using lib::STRING_MAX_RELEVANT; +const size_t STRING_MAX_RELEVANT = 1000; + +//namespace error = lumiera::error; + +//Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); +const char * const GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); + +string +catchMyself () +{ + string buff(STRING_MAX_RELEVANT+1, '\0' ); + ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT); + + if (0 > chars_read || chars_read == ssize_t(STRING_MAX_RELEVANT)) +// throw error::Fatal ("unable to discover path of running executable") + throw string("unable to discover path of running executable"); + + buff.resize(chars_read); + return buff; +} int @@ -32,6 +62,7 @@ main (int, char**) //(int argc, char* argv[]) NOBUG_INIT; + cout << "\n\nich bin :" << catchMyself(); cout << "\n.gulp.\n"; return 0; From 208b5529556824127afcbe91de7870f77fb9209f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 1 Feb 2011 05:10:45 +0100 Subject: [PATCH 118/140] Demonstration how the application could resolve the module loading location The real application likely will consult the configuration file to search alternative module installation locations. --- SConstruct | 12 +++-- src/common/dummy-func.cpp | 111 +++++++++++++++++++++++++++++++++----- src/common/dummy-func.hpp | 15 ++++++ 3 files changed, 122 insertions(+), 16 deletions(-) diff --git a/SConstruct b/SConstruct index 6eadc765b..c176dd6f3 100644 --- a/SConstruct +++ b/SConstruct @@ -104,8 +104,8 @@ def setupBasicEnvironment(localDefinitions): appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') # setup search path for Lumiera plugins - appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:./modules\\"' - ,'LUMIERA_PLUGIN_PATH=\\"$DESTDIR/lib/lumiera/:./modules\\"') + appendCppDefine(env,'PKGLIBDIR','LUMIERA_PLUGIN_PATH=\\"$PKGLIBDIR/:ORIGIN/modules\\"' + ,'LUMIERA_PLUGIN_PATH=\\"ORIGIN/modules\\"') appendCppDefine(env,'PKGDATADIR','LUMIERA_CONFIG_PATH=\\"$PKGLIBDIR/:.\\"' ,'LUMIERA_CONFIG_PATH=\\"$DESTDIR/share/lumiera/:.\\"') @@ -248,10 +248,14 @@ def configurePlatform(env): if not conf.CheckCXXHeader('boost/config.hpp'): problems.append('We need the C++ boost-libraries.') else: - if not conf.CheckCXXHeader('boost/shared_ptr.hpp'): - problems.append('We need boost::shared_ptr (shared_ptr.hpp).') + if not conf.CheckCXXHeader('boost/scoped_ptr.hpp'): + problems.append('We need boost::scoped_ptr (scoped_ptr.hpp).') + if not conf.CheckCXXHeader('boost/format.hpp'): + problems.append('We need boost::format (header).') if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'): problems.append('We need boost::program_options (including binary lib for linking).') + if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): + problems.append('We need the boost::filesystem (including binary lib for linking).') if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): problems.append('We need the boost regular expression lib (incl. binary lib for linking).') diff --git a/src/common/dummy-func.cpp b/src/common/dummy-func.cpp index b6250ee75..3892e6737 100644 --- a/src/common/dummy-func.cpp +++ b/src/common/dummy-func.cpp @@ -1,42 +1,129 @@ /* - dummy-func.cpp - placeholder with dummy functions to demonstrate building shared modules + dummy-func.cpp - placeholder with dummy functions to demonstrate building/loading shared modules -* ******************************************************************************************/ +* *************************************************************************************************/ + + + +#ifndef LUMIERA_PLUGIN_PATH +#error please define the plugin search path as -DLUMIERA_PLUGIN_PATH, e.g. as $INSTALL_PREFIX/lib/lumiera +#endif #include "common/dummy-func.hpp" +extern "C" { +#include +} #include #include - - - +#include +#include +#include namespace lumiera { - - const char * const GUI_MODULE_NAME = "modules/gtk_gui.lum"; - typedef void (*VoidFunc)(void); + using std::string; + using boost::regex; + using boost::smatch; + using boost::regex_search; + + namespace fsys = boost::filesystem; + + namespace { // Implementation helpers + + const size_t STRING_MAX_RELEVANT = 1000; + + const char * const GUI_MODULE_TO_LOAD = "gtk_gui"; + const char * const GET_PATH_TO_EXECUTABLE = "/proc/self/exe"; + + regex EXTRACT_RELATIVE_PATHSPEC ("\\$?ORIGIN/([^:]+)"); + + + /** the real application would throw a custom exception... */ + void + dieHard (string msg) + { + NOBUG_ERROR (NOBUG_ON, "Fatal Error: %s ", msg.c_str()); + abort(); + } + + + /** figure out the absolute path + * of the currently running executable + */ + string + catchMyself () + { + string buff(STRING_MAX_RELEVANT+1, '\0' ); + ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT); + + if (0 > chars_read || chars_read == ssize_t(STRING_MAX_RELEVANT)) + dieHard ("unable to discover path of running executable"); + + buff.resize(chars_read); + return buff; + } + + + /** extract from the PLUGIN_PATH a path specification + * given relative to the location of the executable, + * as denoted by the 'ORIGIN' token + */ + string + getRelativeModuleLocation() + { + smatch match; + if (regex_search (string(LUMIERA_PLUGIN_PATH), match, EXTRACT_RELATIVE_PATHSPEC)) + return (match[1]); + else + dieHard ("no valid module loading location relative to executable defined in LUMIERA_PLUGIN_PATH"); + } + }//(End) implementation helpers + + + + + void loadDummyGui() { - void* handle = dlopen (GUI_MODULE_NAME, RTLD_LAZY|RTLD_LOCAL); + string moduleLocation = resolveModulePath (GUI_MODULE_TO_LOAD); + + void* handle = dlopen (moduleLocation.c_str(), RTLD_LAZY|RTLD_LOCAL); if (handle) { + typedef void (*VoidFunc)(void); + VoidFunc entryPoint = (VoidFunc) dlsym (handle, "start_dummy_gui"); if (!entryPoint) - ERROR (lumiera, "unable to resolve the entry point symbol after loading the GUI module."); + dieHard ("unable to resolve the entry point symbol after loading the GUI module."); else - (*entryPoint) (); + (*entryPoint) (); // activate loaded module } else - ERROR (lumiera, "unable to load %s", GUI_MODULE_NAME); + dieHard ("unable to load "+moduleLocation); } + string + resolveModulePath (string moduleName) + { + fsys::path exePathName (catchMyself()); + fsys::path modPathName (exePathName.remove_leaf() / getRelativeModuleLocation() / (moduleName+".lum") ); + + if (!fsys::exists (modPathName)) + dieHard ("Module "+modPathName.string()+" doesn't exist."); + + INFO (config, "found module %s", modPathName.string().c_str()); + return modPathName.string(); + } + + + } // namespace lumiera diff --git a/src/common/dummy-func.hpp b/src/common/dummy-func.hpp index cbf3a96bb..dde394a6b 100644 --- a/src/common/dummy-func.hpp +++ b/src/common/dummy-func.hpp @@ -4,11 +4,18 @@ * ******************************************************************************************/ +#ifndef COMMON_DUMMY_FUNC_H +#define COMMON_DUMMY_FUNC_H + #include "include/nobugcfg.h" +#include + namespace lumiera { + using std::string; + /** this is a function located in the liblumieracore.so, * which attempts to load the "pseudo-gui" as shared module * and invoke the gui-main. The sole purpose of this function @@ -16,5 +23,13 @@ namespace lumiera { */ void loadDummyGui(); + /** helper to establish the location to search for loadable modules. + * This is a simple demonstration of the basic technique used in the + * real application source to establish a plugin search path, based + * on the actual executable position plus compiled in and configured + * relative and absolute path specifications. + */ + string resolveModulePath (string moduleName); } // namespace lumiera +#endif From a42e6a5a8f803d9c47eec59197b3face6f96b4e8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 3 Feb 2011 17:34:24 +0100 Subject: [PATCH 119/140] draft solution loading an INI file for module path resolution this would remove the need to compile any path into the EXE --- src/common/dummy-func.cpp | 176 +++++++++++++++++++++++++++++++++----- src/common/dummy-func.hpp | 9 +- 2 files changed, 154 insertions(+), 31 deletions(-) diff --git a/src/common/dummy-func.cpp b/src/common/dummy-func.cpp index 3892e6737..47250def3 100644 --- a/src/common/dummy-func.cpp +++ b/src/common/dummy-func.cpp @@ -17,28 +17,37 @@ extern "C" { } #include #include +#include #include +#include #include +#include +#include #include namespace lumiera { using std::string; + using std::ifstream; using boost::regex; using boost::smatch; using boost::regex_search; + using boost::sregex_iterator; namespace fsys = boost::filesystem; - + namespace opt = boost::program_options; + namespace { // Implementation helpers const size_t STRING_MAX_RELEVANT = 1000; - const char * const GUI_MODULE_TO_LOAD = "gtk_gui"; + const char * const BOOTSTRAP_INI = "$ORIGIN/setup.ini"; + const char * const GUI_MODULE_TO_LOAD = "gtk_gui.lum"; const char * const GET_PATH_TO_EXECUTABLE = "/proc/self/exe"; regex EXTRACT_RELATIVE_PATHSPEC ("\\$?ORIGIN/([^:]+)"); + regex EXTRACT_PATHSPEC ("(\\$?ORIGIN)?([^:]+)"); /** the real application would throw a custom exception... */ @@ -56,13 +65,16 @@ namespace lumiera { string catchMyself () { - string buff(STRING_MAX_RELEVANT+1, '\0' ); - ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT); - - if (0 > chars_read || chars_read == ssize_t(STRING_MAX_RELEVANT)) - dieHard ("unable to discover path of running executable"); - - buff.resize(chars_read); + static string buff(STRING_MAX_RELEVANT+1, '\0' ); + if (!buff.size()) + { + ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT); + + if (0 > chars_read || chars_read == ssize_t(STRING_MAX_RELEVANT)) + dieHard ("unable to discover path of running executable"); + + buff.resize(chars_read); + } return buff; } @@ -80,6 +92,134 @@ namespace lumiera { else dieHard ("no valid module loading location relative to executable defined in LUMIERA_PLUGIN_PATH"); } + + class SearchPathSplitter + : boost::noncopyable + { + sregex_iterator pos_, + end_; + + public: + SearchPathSplitter (string searchPath) + : pos_(searchPath.begin(),searchPath.end(), EXTRACT_PATHSPEC) + , end_() + { } + + bool + hasNext() + { + return pos_ != end_; + } + + string + next() + { + ++pos_; + if (!hasNext()) + dieHard ("Search path exhausted."); + + return resolveRelative(); + } + + private: + /** maybe resolve a path spec given relative to + * the current Executable location ($ORIGIN) */ + string + resolveRelative () + { + if (containsORIGINToken()) + return asAbsolutePath(); + else + return getFullPath(); + } + + smatch::value_type found(int group=0) { return (*pos_)[group]; } + + bool containsORIGINToken() { return found(1).matched; } + string getRelativePath() { return found(2); } + string getFullPath() { return found(); } + + string + asAbsolutePath() + { + fsys::path exePathName (catchMyself()); + fsys::path modPathName (exePathName.remove_leaf() / getRelativePath()); + + if (fsys::exists(modPathName) && !fsys::is_directory (modPathName)) + dieHard ("Error in search path: component"+modPathName.string()+"is not an existing directory"); + + return modPathName.string(); + } + }; + + + /** helper to establish the location to search for loadable modules. + * This is a simple demonstration of the basic technique used in the + * real application source to establish a plugin search path, based + * on the actual executable position plus compiled in and configured + * relative and absolute path specifications. + */ + string + resolveModulePath (string moduleName, string searchPath = "") + { + fsys::path modulePathName (moduleName); + SearchPathSplitter searchLocation(searchPath); + + while (true) + { + if (fsys::exists (modulePathName)) + { + INFO (config, "found module %s", modulePathName.string().c_str()); + return modulePathName.string(); + } + + // try / continue search path + if (searchLocation.hasNext()) + modulePathName = fsys::path() / searchLocation.next() / moduleName; + else + dieHard ("Module \""+moduleName+"\" not found" + + (searchPath.empty()? ".":" in search path: "+searchPath)); + } + } + + + + class Config + : boost::noncopyable + { + opt::options_description syntax; + opt::variables_map settings; + + public: + Config (string bootstrapIni) + : syntax("Lumiera installation and platform configuration") + , settings() + { + syntax.add_options() + ("BuildsystemDemo.gui", opt::value(), + "name of the Lumiera GUI plugin to load") + ("BuildsystemDemo.modulepath", opt::value(), + "search path for loadable modules. " + "May us $ORIGIN to refer to the EXE location") + ; + + ifstream configIn (resolveModulePath (bootstrapIni).c_str()); + + + opt::parsed_options parsed = + opt::parse_config_file (configIn, syntax); + + opt::store (parsed, settings); + opt::notify(settings); + } + + string + operator[] (const string key) const + { + return settings[key].as(); + } + }; + }//(End) implementation helpers @@ -91,7 +231,10 @@ namespace lumiera { void loadDummyGui() { - string moduleLocation = resolveModulePath (GUI_MODULE_TO_LOAD); + Config appConfig(BOOTSTRAP_INI); + string guiModule = appConfig["BuildsystemDemo.gui"]; + string moduleSearch = appConfig["BuildsystemDemo.modulepath"]; + string moduleLocation = resolveModulePath (guiModule, moduleSearch); void* handle = dlopen (moduleLocation.c_str(), RTLD_LAZY|RTLD_LOCAL); if (handle) @@ -111,19 +254,6 @@ namespace lumiera { } - string - resolveModulePath (string moduleName) - { - fsys::path exePathName (catchMyself()); - fsys::path modPathName (exePathName.remove_leaf() / getRelativeModuleLocation() / (moduleName+".lum") ); - - if (!fsys::exists (modPathName)) - dieHard ("Module "+modPathName.string()+" doesn't exist."); - - INFO (config, "found module %s", modPathName.string().c_str()); - return modPathName.string(); - } - } // namespace lumiera diff --git a/src/common/dummy-func.hpp b/src/common/dummy-func.hpp index dde394a6b..d15383b88 100644 --- a/src/common/dummy-func.hpp +++ b/src/common/dummy-func.hpp @@ -19,17 +19,10 @@ namespace lumiera { /** this is a function located in the liblumieracore.so, * which attempts to load the "pseudo-gui" as shared module * and invoke the gui-main. The sole purpose of this function - * is to demonstarte that the SCons build system is working. + * is to demonstrate that the SCons build system is working. */ void loadDummyGui(); - /** helper to establish the location to search for loadable modules. - * This is a simple demonstration of the basic technique used in the - * real application source to establish a plugin search path, based - * on the actual executable position plus compiled in and configured - * relative and absolute path specifications. - */ - string resolveModulePath (string moduleName); } // namespace lumiera #endif From babbe33d1d2190548ab3e5a36562837b6c98788b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 4 Feb 2011 16:10:59 +0100 Subject: [PATCH 120/140] Demonstration of complete bootstrap, loading INI and resolving GUI module path --- SConstruct | 1 + data/config/dummy_lumiera.ini | 8 +-- data/config/setup.ini | 20 ++++++++ src/common/dummy-func.cpp | 91 +++++++++++++++++++---------------- src/common/dummy-func.hpp | 11 +++++ 5 files changed, 86 insertions(+), 45 deletions(-) create mode 100644 data/config/setup.ini diff --git a/SConstruct b/SConstruct index c176dd6f3..99ee764bb 100644 --- a/SConstruct +++ b/SConstruct @@ -348,6 +348,7 @@ def defineBuildTargets(env, artifacts): artifacts['corelib'] = core artifacts['support'] = lLib artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'], LIBS=core, install=True) + + env.ConfigData(env.path.srcConf+'setup.ini') + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') ) diff --git a/data/config/dummy_lumiera.ini b/data/config/dummy_lumiera.ini index cfe91da1e..a8408c41b 100644 --- a/data/config/dummy_lumiera.ini +++ b/data/config/dummy_lumiera.ini @@ -1,4 +1,4 @@ -/* This is an dummy Lumiera config file - * - * Actually Lumiera can't yet load any config, as of 1/2011 - */ +# This is an dummy Lumiera config file +# +# Actually Lumiera can't yet load any extended config, as of 1/2011 +# diff --git a/data/config/setup.ini b/data/config/setup.ini new file mode 100644 index 000000000..e4a00f4e2 --- /dev/null +++ b/data/config/setup.ini @@ -0,0 +1,20 @@ +########################################################### +# ### Lumiera installation and platform configuration ### +# +# (file located relative to the Lumiera executable) +# + +[BuildsystemDemo] +# This is dummy/demonstration executable +# built from the "scons" git tree. It serves to +# document and evolve the SCons buildsystem in isolation +# and for tests of the packaging and build process. +# +gui = gtk_gui.lum +modulepath = $ORIGIN/modules + +[Lumiera] +# Lumiera video editor main application +# +# Nothing configurable as of 1/2011 +# diff --git a/src/common/dummy-func.cpp b/src/common/dummy-func.cpp index 47250def3..e4ed350b2 100644 --- a/src/common/dummy-func.cpp +++ b/src/common/dummy-func.cpp @@ -5,10 +5,6 @@ -#ifndef LUMIERA_PLUGIN_PATH -#error please define the plugin search path as -DLUMIERA_PLUGIN_PATH, e.g. as $INSTALL_PREFIX/lib/lumiera -#endif - #include "common/dummy-func.hpp" @@ -35,19 +31,22 @@ namespace lumiera { using boost::regex_search; using boost::sregex_iterator; + typedef smatch::value_type const& SubMatch; + + + namespace fsys = boost::filesystem; namespace opt = boost::program_options; - + + namespace { // Implementation helpers const size_t STRING_MAX_RELEVANT = 1000; - const char * const BOOTSTRAP_INI = "$ORIGIN/setup.ini"; - const char * const GUI_MODULE_TO_LOAD = "gtk_gui.lum"; + const char * const BOOTSTRAP_INI = "$ORIGIN/config/setup.ini"; const char * const GET_PATH_TO_EXECUTABLE = "/proc/self/exe"; - regex EXTRACT_RELATIVE_PATHSPEC ("\\$?ORIGIN/([^:]+)"); - regex EXTRACT_PATHSPEC ("(\\$?ORIGIN)?([^:]+)"); + regex EXTRACT_PATHSPEC ("(\\$?ORIGIN/)?([^:]+)"); /** the real application would throw a custom exception... */ @@ -66,7 +65,7 @@ namespace lumiera { catchMyself () { static string buff(STRING_MAX_RELEVANT+1, '\0' ); - if (!buff.size()) + if (!buff[0]) { ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT); @@ -79,20 +78,16 @@ namespace lumiera { } - /** extract from the PLUGIN_PATH a path specification - * given relative to the location of the executable, - * as denoted by the 'ORIGIN' token - */ - string - getRelativeModuleLocation() - { - smatch match; - if (regex_search (string(LUMIERA_PLUGIN_PATH), match, EXTRACT_RELATIVE_PATHSPEC)) - return (match[1]); - else - dieHard ("no valid module loading location relative to executable defined in LUMIERA_PLUGIN_PATH"); - } + /** + * Helper: Access a path Specification as a sequence of filesystem Paths. + * This iterator class dissects a ':'-separated path list. The individual + * components may use the symbol \c $ORIGIN to denote the directory holding + * the current executable. After resolving this symbol, a valid absolute or + * relative filesystem path should result, which must not denote an existing + * file (directory is OK). + * @note #fetch picks the current component and advances the iteration. + */ class SearchPathSplitter : boost::noncopyable { @@ -100,25 +95,26 @@ namespace lumiera { end_; public: - SearchPathSplitter (string searchPath) + SearchPathSplitter (string const& searchPath) : pos_(searchPath.begin(),searchPath.end(), EXTRACT_PATHSPEC) , end_() { } bool - hasNext() + isValid() const { return pos_ != end_; } string - next() + fetch () { - ++pos_; - if (!hasNext()) + if (!isValid()) dieHard ("Search path exhausted."); - return resolveRelative(); + string currentPathElement = resolveRelative(); + ++pos_; + return currentPathElement; } private: @@ -133,7 +129,7 @@ namespace lumiera { return getFullPath(); } - smatch::value_type found(int group=0) { return (*pos_)[group]; } + SubMatch found(int group=0) { return (*pos_)[group]; } bool containsORIGINToken() { return found(1).matched; } string getRelativePath() { return found(2); } @@ -146,13 +142,13 @@ namespace lumiera { fsys::path modPathName (exePathName.remove_leaf() / getRelativePath()); if (fsys::exists(modPathName) && !fsys::is_directory (modPathName)) - dieHard ("Error in search path: component"+modPathName.string()+"is not an existing directory"); + dieHard ("Error in search path: component \""+modPathName.string()+"\" is not a directory"); - return modPathName.string(); + return modPathName.directory_string(); } }; - + /** helper to establish the location to search for loadable modules. * This is a simple demonstration of the basic technique used in the * real application source to establish a plugin search path, based @@ -174,22 +170,26 @@ namespace lumiera { } // try / continue search path - if (searchLocation.hasNext()) - modulePathName = fsys::path() / searchLocation.next() / moduleName; + if (searchLocation.isValid()) + modulePathName = fsys::path() / searchLocation.fetch() / moduleName; else dieHard ("Module \""+moduleName+"\" not found" + (searchPath.empty()? ".":" in search path: "+searchPath)); - } - } + } } + /** + * Encapsulate an INI-style configuration file. + * The acceptable settings are defined in the ctor. + * Implementation based on boost::program_options + */ class Config : boost::noncopyable { opt::options_description syntax; opt::variables_map settings; - + public: Config (string bootstrapIni) : syntax("Lumiera installation and platform configuration") @@ -203,8 +203,8 @@ namespace lumiera { "May us $ORIGIN to refer to the EXE location") ; - ifstream configIn (resolveModulePath (bootstrapIni).c_str()); - + ifstream configIn (resolve(bootstrapIni).c_str()); + opt::parsed_options parsed = opt::parse_config_file (configIn, syntax); @@ -218,6 +218,15 @@ namespace lumiera { { return settings[key].as(); } + + private: + string + resolve (fsys::path iniSpec) + { + string file = iniSpec.leaf(); + string searchpath = iniSpec.branch_path().string(); + return resolveModulePath (file, searchpath); + } }; }//(End) implementation helpers @@ -233,7 +242,7 @@ namespace lumiera { { Config appConfig(BOOTSTRAP_INI); string guiModule = appConfig["BuildsystemDemo.gui"]; - string moduleSearch = appConfig["BuildsystemDemo.modulepath"]; + string moduleSearch = appConfig["BuildsystemDemo.modulepath"]; string moduleLocation = resolveModulePath (guiModule, moduleSearch); void* handle = dlopen (moduleLocation.c_str(), RTLD_LAZY|RTLD_LOCAL); diff --git a/src/common/dummy-func.hpp b/src/common/dummy-func.hpp index d15383b88..43e43726a 100644 --- a/src/common/dummy-func.hpp +++ b/src/common/dummy-func.hpp @@ -20,6 +20,17 @@ namespace lumiera { * which attempts to load the "pseudo-gui" as shared module * and invoke the gui-main. The sole purpose of this function * is to demonstrate that the SCons build system is working. + * + * \par requirements + * While this isn't the actual implementation used in Lumiera, + * we try to mimic or demonstrate the techniques used to resolve + * the actual module to be loaded. So there are some requirements + * - \c $ORIGIN/config/setup.ini exists and defines... + * - a section [BuildsystemDemo], which holds + * - a setting gui = gtk_gui.lum + * - a module search path, typically: modulepath = $ORIGIN/modules + * Here the token \c $ORIGIN is automatically resolved to the directory + * holding the current executable, by reading the symlink \c /proc/self/exe */ void loadDummyGui(); From 0ac014913765177dbfdd09f8f5f9bf002b08aefa Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 4 Feb 2011 22:19:25 +0100 Subject: [PATCH 121/140] Define first preview version number 0.pre.01 Rewrite README, INSTALL and AUTHORS as foundation for the Debian package --- AUTHORS | 168 ++++++++++++++++++++++++++++++----------- INSTALL | 218 +++++++++++++++++++++++++++++++++++++++-------------- README | 75 ++++++++++++------ SConstruct | 2 +- 4 files changed, 336 insertions(+), 127 deletions(-) diff --git a/AUTHORS b/AUTHORS index e950124df..8f909c0eb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,58 +1,134 @@ +Lumiera Authors and Credits +=========================== +Version: 0.pre.0.1 +Date: 2/2011 -Cinelerra(2) Authors -==================== -hv=Jack Crossfire -j6t=Johannes Sixt -minmax=Andraz Tori -herman=Herman Robak -baver=Richard Baverstock -pere=Petter Reinholdtsen -tfheen=Tollef Fog Heen -andreask=Andreas Kielb -theraz=Tyler Geddes -dyce=Gergely Erdelyi -dreamlx=David Arendt -ga=Gustavo Iniguez -memenk=Michael Eric Menk -benjif=Benjamin Flaming -cobra=Kevin Brosius -taraba=Mark Taraba -nate=Nathan Kurz -mammique=Camille Harang -kbielefe=Karl Bielefeldt -alexf=Alex Ferrer -pmdumuid=Pierre Dumuid -giskard=Riccardo Setti -jstewart=Joe Stewart -doudou=Sylvain Joyeux -rafael2k=Rafael Diniz -nicolasm=Nicolas Maufrais +'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Architecture and Design, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +graphical user interface, JOEL HOLDSWORTH + , CLAY BARNES +processing layer , HERMANN VOSSELER +backend , CHRISTIAN THÄTER +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Involved in Lumiera development -=============================== -cehteh=Christian Thaeter -ichthyo=Hermann Vosseler -plouj=Michael Ploujnikov +'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Programming, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +rendering engine, HERMANN VOSSELER +backend, CHRISTIAN THÄTER +GTK-GUI, JOEL HOLDSWORTH + , STEFAN KANGAS +library, HERMANN VOSSELER + , CHRISTIAN THÄTER + , ANTON YAKOVLEV + , SIMEON VÖLKEL +testsuite, CHRISTIAN THÄTER + , NICHOLAS SINNOTT-ARMSTRONG +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Programming Documentation, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +, MICHAEL PLOUJNIKOV +, ODIN HØRTHE OMDAL +, SIMON LARCHER +, SIMEON VÖLKEL +, MANO STIENEN +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Used Software and Libraries, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +boost libraries, +gavl libraries, +gtk+ toolkit, +lua, +nobug, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Builddrone, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +idea and implementation, CHRISTIAN THÄTER +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +//'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ,Testing +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// , +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Workflow design, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +concept , NIKOLA DUPER + , BRIAN RYTEL + , MIKE PROVOST +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Server Administration, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +, CHRISTIAN THÄTER +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +Cinelerra +--------- + +Lumiera is an offspring of the Cinelerra video editor. + +It started out as a partial rewrite of Cinelerra in 2007 +and turned into a complete rewrite and redesign in Spring 2008 + + +'`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The Lumiera project feels especially obliged to, +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Cinelerra's original Author, ADAM WILLIAMS / aka. "Jack Crossfire" / aka "HV" +Cinelerra-CV , all the numerous contributors + , of the "Community Version" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -_______________________________________________________________ Further Credits +--------------- Parts of the Proc-Layer Implementation are heavily inspired by -The Loki Library - Copyright (c) 2001 by Andrei Alexandrescu - Loki is governed by a MIT-License. - See: http://loki-lib.sourceforge.net - and the book: - Alexandrescu, Andrei. - "Modern C++ Design: Generic Programming and Design - Patterns Applied". Copyright (c) 2001. Addison-Wesley. - ISBN 0201704315 - - Loki Copyright Notice: - Permission to use, copy, modify, distribute and sell this software for any + + +.The Loki Library +[quote] +__________________________________________________________________________ +Copyright (c) 2001 by *Andrei Alexandrescu* + + +.Loki Copyright Notice +* Loki is governed by a MIT-License. See: http://loki-lib.sourceforge.net +* Permission to use, copy, modify, distribute and sell this software for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The author makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. + + +.the ``Loki Book'' +[verse] +Alexandrescu, Andrei. + "Modern C++ Design: Generic Programming and Design Patterns Applied". + Copyright (c) 2001. Addison-Wesley. + ISBN 0201704315 +_____________________________________________________________________________ + + diff --git a/INSTALL b/INSTALL index 3ecc05196..e2c2dbf39 100644 --- a/INSTALL +++ b/INSTALL @@ -1,46 +1,71 @@ -Running / Installing Lumiera Prototype -====================================== - -From: -http://www.pipapo.org/pipawiki/Lumiera/NewbiesTutorials - - Newbies Tutorials +================= -This page contains some tutorials for beginners that want to help to the -developers of Lumiera. +This page contains some tutorials for beginners interested in Lumiera development. -Trying the Lumiera GUI from Joel -You need to have: +Building Lumiera from source +---------------------------- +Currently, after building the application, you can try out the Lumiera GUI +and you can run the testsuite. - * libboost - * libtool - * git - * libgavl - * nobug(see below) +For building Lumiera, besides the GNU C/C++ compiler (Version 4.X), you'll need: -For Ubuntu Hardy also: + * http://git-scm.com/[git] + * http://www.gnu.org/software/libtool/[libtool] + * http://www.boost.org/[Boost libraries] + * http://gmerlin.sourceforge.net/[GAVL library] + * *NoBug* (see below) - * libglade2-dev - * libgdl-1-dev - * libgtkmm-2.4-dev - * libxv-dev +TIP: Generally speaking, when you want to build software, you need the _development_ version +of the packages, containing the headers and pre-built libraries to link against. Usually, +these packages are named `-devel` or `-dev` -also check http://gnuradio.org/trac/wiki/UbuntuInstall#InstallationOptions to -see that all your installations options are met. +.Notes for Ubuntu: -Installing nobug from git +On Hardy you need also: -Create a temp directory where you can have the sourcecode of the libraries you -will use as well as lumieras branches you want to try. Example ~/temp. cd to -that directory and run: + * libglade2-dev + * libgtkmm-2.4-dev + * libxv-dev +For most Debian based systems, e.g. Ubuntu Intrepid and Jaunty, you can install these dependencies with: + +------------------------------------------------------------ +sudo apt-get install build-essential autoconf libboost-dev libboost-program-options-dev libboost-regex-dev \ + libtool libgavl-dev libglade2-dev libgdl-1-dev libgtkmm-2.4-dev libxv-dev scons valgrind librsvg2-dev git-core +------------------------------------------------------------ + + +Build directory +~~~~~~~~~~~~~~~ +You need a directory for checking out the sources and running the build. This could be a temp directory, +or some "workspace" directory below your home directory. We'll call it _workspace directory_ from now on. + +Lumiera specific libraries +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Now that you have your basic build system setup, Lumiera needs a few more special support libraries. +Currently you need to build those from source and install them, so the Lumiera build process can +pick them up. + +WARNING: Note that the following procedures will try to install files into your base system below `/usr/local`. +To do so, you'll need administrative permissions for the machine you're working on. These additions might interfere +with other libraries installed by your package manager (if you get into trouble updating your system later on, +sometimes you need to remove these manually built librararies). + +Installing NoBug +^^^^^^^^^^^^^^^^ + +*NoBug* is an instrumentation and diagnostics library. + +Go into the mentioned _workspace direcory_ (maybe create a new directory). First, let's get the NoBug source code: + +------------------------------------------------------------ git clone git://git.pipapo.org/nobug +------------------------------------------------------------ +This will create a (sub)directory called nobug with sourcecode in your current directory. + +Compile NoBug with the following commands -This will create a directory called nobug with sourcecode in your current -directory. - +------------------------------------------------------------ cd nobug autoreconf -i mkdir build @@ -48,11 +73,78 @@ cd build ../configure make make install +------------------------------------------------------------ -Getting the Lumiera source code +Installing GDL +^^^^^^^^^^^^^^ +The *GNOME Docking library* is generally available through your package manager, but +we contributed some improvements, which are only available in the very recent development +versions of GDL. Thus, for now we created a special package, which doesn't interfere with +an existing (older) installation of GDL. -In the temp directory +Ubuntu 9.04 note: intltool-update is not patched, you must add /usr/share/intltool-debian/ to get the gdl-package to configure correctly (JSC). +------------------------------------------------------------ +git clone git://git.lumiera.org/gdl-package +cd gdl-package +./configure +make +sudo make install +------------------------------------------------------------ + +For more detailed instructions on how to build gdl (also how to build it into a debian package) see +http://lists.lumiera.org/pipermail/lumiera/2009-April/000891.html[this message on the Lumiera Mailinglist]. + + +check library linkage +^^^^^^^^^^^^^^^^^^^^^ +The compile will warn you to add various directories to /etc/ld.so.conf and then run ldconfig. This +will allow your dynamic liker to pick up the newly built libraries later when you try to start Lumiera. +If you don't want to reconfigure your system and add `/usr/local/lib` to the mentioned linker configuration, +you may alternatively just add the directories to your LD_LIBRARY_PATH environment variable. + +Either way, check that all libraries are accessible and ok: + +------------------------------------------------------------ +sudo ldconfig -v | grep 'gdl-lum\|nobug' +------------------------------------------------------------ + +and you should get a list of the libraries, part of which should look like this: + +------------------------------------------------------------ + libnobug.so.0 -> /usr/local/lib/libnobug.so.0.0.0 + libnobugmt.so.0 -> /usr/local/lib/libnobugmt.so.0.0.0 + libgdl-lum.so.0 -> /usr/local/lib/libgdl-lum.so.0.0.0 +------------------------------------------------------------ + +or similar. If any if this libs are not listed, investigate why before continuing. + + +Building Lumiera +~~~~~~~~~~~~~~~~ +Lumiera has two maintained (and equivalent) build systems: *scons* and *autotools*. You can pick the one you +feel more comfortable with. + +When trying to build a development version of Lumiera, it might well be that at times one of the builds doesn't work temporarily. It is always a good idea to check the current build stats from our *builddrone*, which automatically builds the latest version from master repository. + +Please have a look at this http://lumiera.org/builddrone/table.html[current build stats]-page. + +Next, after having built and installed the prerequisite libraries, go into the _workspace directory_ to retrieve the Lumiera source code and build it. + + + * to retrieve the sourcecode with git and then build with *scons*: ++ +(see available build options by issuing `scons -h` ) ++ +----------------- +git clone git://git.lumiera.org/LUMIERA +cd LUMIERA +scons +----------------- ++ + + * alternatively, if you prefer building with *autotools*: ++ +----------------- git clone git://git.lumiera.org/LUMIERA cd LUMIERA autoreconf -fi @@ -60,40 +152,52 @@ mkdir build cd build ../configure make +----------------- -maybe run the testsuite by +maybe build and run the testsuite by issuing `scons check` or `make check` -make check +This will take some time. -This will take some time. After it has finished: +NOTE: you can't _install_ Lumiera currently. Don't try it, it won't work. +Just run it from the build directory. -./lumigui +After the build has finished successfully, you should be able to start Lumiera. +Currently, this will bring up the GUI, without any further functionality -[NewbiesTut] + * for autotools build: issue `./lumiera` (from within the `build` subdirectory) + * for scons build: ++ +------------------- +cd bin +./lumiera +------------------- -Contibuting +you should see something like (screenshot from 1/2009): -Nothing is easier, follow the basic instructions at http://www.kernel.org/pub/ -software/scm/git/docs/gittutorial.html, notably the +image:http://www.pipapo.org/pipawiki/Lumiera/GuiBrainstorming?action=AttachFile&do=get&target=screenshot090124-resources.jpg[] + + +Contributing +------------ + +Nothing is easier, follow the http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html[the basic instructions], +notably the following parts: +------------------------------------------------------------ $ git config --global user.name "Your Name Comes Here" $ git config --global user.email you@yourdomain.example.com - -parts. - -Then you are ready to go, you can edit and commit the lumiera code locally in -your cloned repository. Please do small commits which fix/improve only one -single thing and use meaningful commit messages. Check that you didn't broke -anything - - * by running 'make check'. - -Finally you can push your changes to the lumiera server to the 'mob' -repository: - -$ git push git://git.lumiera.org/lumiera/mob master:refs/heads/YOURNAME - -This creates a new branch 'YOURNAME' on the mob repository. Then you notify the -other devs on the mailinglist and they may merge your code into the mainline. +------------------------------------------------------------ + + +Then you are ready to go, you can edit and commit the lumiera code locally in your cloned repository. Please do small commits which fix/improve only one single thing and use meaningful commit messages. + +Check that you didn't break anything, by running the testsuite (see above) + +Finally you can push your changes to the lumiera server to the 'mob' repository: + +------------------------------------------------------------ +$ git push git://git.lumiera.org/lumiera/mob master:refs/heads/YOURNAME +------------------------------------------------------------ +This creates a new branch 'YOURNAME' on the mob repository. Then you notify the other devs on the mailinglist and they may merge your code into the mainline. diff --git a/README b/README index 7913094ee..3d62663eb 100644 --- a/README +++ b/README @@ -1,44 +1,73 @@ -==================================== Lumiera -- the video NLE for Linux ==================================== - Lumiera is a nonlinear video editing and compositing tool. - It understands some of the common multimedia formats - (quicktime, avi, ogg) and audio/video compression - codecs (divx, xvid, mpeg1/2/4, ...) - . - It features non-destructive editing, compositing tools, - a selection of effects plugins, processing in RGB, YUV - and RGB-float colormodels and the ability to mix media - with differing sizes and framerates. +************************************************************* +Lumiera is a nonlinear video editing and compositing tool. +It understands some of the common multimedia formats +(quicktime, avi, ogg) and audio/video compression +codecs (divx, xvid, mpeg1/2/4, ...) -For more information about Lumiera visit http://lumiera.org/ -For more information about Cinelerra visit http://cinelerra.org/ +It features non-destructive editing, compositing tools, +a selection of effects plugins, processing in RGB, YUV +and RGB-float colormodels and the ability to mix media +with differing sizes and framerates. +More Informations at http://lumiera.org/[Lumiera.org] +**************************************************************** ----------------------------- -"Lumiera" prototype code ----------------------------- +Lumiera pre-Alpha Versions +-------------------------- -**This source tree doesn't yet contain a working video editing application** -Rather, it contains the initial framework and core modules of the lower and -middle layer of the envisioned new Application "Lumiera". +**This source tree doesn't yet contain a working video editing application** + +Rather, it contains the framework and technology core of the envisioned Application ``Lumiera''. -As of 7/2007, we start here with some backend and render engine modules +As of _7/2007_:: +we start here with the backend and render engine modules together with some unit tests. You should find a wiki with detailed design considerations and developer documentation and a UML model (usable with BOUML 2.29) in the sibling directories. -As of 2/2008 the project has been separated completely from his ancestor "Cinelerra" +As of _2/2008_:: +the project has been separated completely from his ancestor ``Cinelerra'' The Community, which is largely identical to the Cinelerra-CV community, choose the -new project name "Lumiera". The basic project infrastructure is up and running, +new project name ``Lumiera''. The basic project infrastructure is up and running, and work on the new codebase is going on continuosely. We can show nothing but -a running test suite for now. +a running test suite for some time to come. +As of _1/2011_:: +the project has created and documented a fairly consistent design, +partially already coded up -- starting from the technical foundations and working up. +The code base is approaching 100k LOC. Roughly half of this is testcode. +The Application can be installed and started to bring up an GTK GUI outline, +but the GUI is very preliminary and not connected to core functionality. +The video processing pipeline is still not complete. + +See the http://issues.lumiera.org/roadmap[Project roadmap] Build Requirements ------------------ -*to be written* +For building Lumiera, you'll need: + + - GNU C/C++ compiler (Version > 4.3) + - Git Version management system + - http://www.boost.org/[Boost libraries] + - http://gmerlin.sourceforge.net/[GAVL library] + - http://lumiera.org/nobug_manual.html[NoBug library] + - GTK\-- + - Cairo and Glade libraries + - X libraries + - http://scons.org[SCons] or Autotools build system + +See the online documentation at http://lumiera.org/Lumiera/NewbiesTutorials +or the local Copy of this page in the file INSTALL + + +Debian Package +-------------- +[verse] +Hermann Vosseler (aka Ichthyo) maintains a *Debian* packaging of the source tree, +which can be pulled from +git://git.lumiera.org/lumiera/debian+ +It can be built by +git-buildpackage+ diff --git a/SConstruct b/SConstruct index 99ee764bb..aa382f2c8 100644 --- a/SConstruct +++ b/SConstruct @@ -30,7 +30,7 @@ #-----------------------------------Configuration TARGDIR = 'target' -VERSION = '0.1+pre.01' +VERSION = '0.pre.01' TOOLDIR = './admin/scons' # SCons plugins SCRIPTDIR = './admin' OPTCACHE = 'optcache' From e73bea379c5add381fc489892c10b4187b9e6a85 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 5 Feb 2011 20:56:51 +0100 Subject: [PATCH 122/140] Adjust some Copyright headers List of years instead of a range is better --- src/backend/thread-wrapper.hpp | 2 +- src/gui/guistart.cpp | 4 +- src/gui/output/gdkdisplayer.hpp | 93 +++++----- src/gui/output/xvdisplayer.hpp | 170 +++++++++--------- src/gui/widgets/video-display-widget.cpp | 113 ++++++------ src/gui/widgets/video-display-widget.hpp | 57 +++--- src/lib/error.hpp | 2 +- src/lumiera/main.cpp | 2 +- src/proc/asset/struct-factory-impl.hpp | 2 +- src/proc/mobject/session/mobjectfactory.cpp | 2 +- src/proc/mobject/session/mobjectfactory.hpp | 2 +- .../session/session-element-tracker-test.cpp | 2 +- .../session/session-structure-test.cpp | 2 +- .../timeline-sequence-handling-test.cpp | 2 +- tests/lib/custom-shared-ptr-test.cpp | 2 +- 15 files changed, 231 insertions(+), 226 deletions(-) diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index c45fd4e75..60413b037 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -2,7 +2,7 @@ THREADWRAPPER.hpp - thin convenience wrapper for starting lumiera threads Copyright (C) Lumiera.org - 2008 - 2010 Hermann Vosseler + 2008, 2010 Hermann Vosseler Christian Thaeter This program is free software; you can redistribute it and/or diff --git a/src/gui/guistart.cpp b/src/gui/guistart.cpp index d8c5513d8..6eadf445c 100644 --- a/src/gui/guistart.cpp +++ b/src/gui/guistart.cpp @@ -3,7 +3,7 @@ Copyright (C) Lumiera.org 2007-2008, Joel Holdsworth - Christian Thaeter + 2009, Christian Thaeter Hermann Vosseler This program is free software; you can redistribute it and/or @@ -199,7 +199,7 @@ extern "C" { /* ================== define an lumieraorg_Gui instance =========== return "Copyright (C) Lumiera.org\n" "2007-2008, Joel Holdsworth \n" - " Christian Thaeter \n" + "2009, Christian Thaeter \n" " Hermann Vosseler "; } ) diff --git a/src/gui/output/gdkdisplayer.hpp b/src/gui/output/gdkdisplayer.hpp index ed7df23b1..8c94efec0 100644 --- a/src/gui/output/gdkdisplayer.hpp +++ b/src/gui/output/gdkdisplayer.hpp @@ -21,6 +21,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /** @file gdkdisplayer.hpp ** This file contains the definition of XvDisplayer, the XVideo ** video output implementation @@ -28,10 +30,10 @@ ** @see displayer.hpp */ -#include "displayer.hpp" +#ifndef GUI_OUTPUT_GDKDISPLAYER_H +#define GUI_OUTPUT_GDKDISPLAYER_H -#ifndef GDKDISPLAYER_HPP -#define GDKDISPLAYER_HPP +#include "displayer.hpp" namespace Gtk { class Widget; @@ -44,46 +46,47 @@ namespace output { * GdkDisplayer is a class which is responsible for rendering a video * image via GDK. */ -class GdkDisplayer : public Displayer -{ -public: - - /** - * Constructor - * @param[in] drawing_area The widget into which the video image will - * be drawn. This value must not be NULL. - * @param[in] width The width of the video image in pixels. This value - * must be greater than zero. - * @param[in] height The height of the video image in pixels. This - * value must be greater than zero. - */ - GdkDisplayer( Gtk::Widget *drawing_area, int width, int height ); - - /** - * Put an image of a given width and height with the expected input - * format (as indicated by the format method). - * @param[in] image The video image array to draw. - */ - void put( const void* image ); - -protected: - - /** - * Indicates if this object can be used to render images on the - * running system. - */ - bool usable(); - -private: - - /** - * The widget that video will be drawn into. - * @remarks This value must be a valid pointer. - */ - Gtk::Widget *drawingArea; -}; - -} // namespace output -} // namespace gui - +class GdkDisplayer + : public Displayer + { + public: + + /** + * Constructor + * @param[in] drawing_area The widget into which the video image will + * be drawn. This value must not be NULL. + * @param[in] width The width of the video image in pixels. This value + * must be greater than zero. + * @param[in] height The height of the video image in pixels. This + * value must be greater than zero. + */ + GdkDisplayer( Gtk::Widget *drawing_area, int width, int height ); + + /** + * Put an image of a given width and height with the expected input + * format (as indicated by the format method). + * @param[in] image The video image array to draw. + */ + void put( const void* image ); + + protected: + + /** + * Indicates if this object can be used to render images on the + * running system. + */ + bool usable(); + + private: + + /** + * The widget that video will be drawn into. + * @remarks This value must be a valid pointer. + */ + Gtk::Widget *drawingArea; + }; + + + +}} // namespace gui::output #endif // GDKDISPLAYER_HPP diff --git a/src/gui/output/xvdisplayer.hpp b/src/gui/output/xvdisplayer.hpp index 362928717..8f8bf388c 100644 --- a/src/gui/output/xvdisplayer.hpp +++ b/src/gui/output/xvdisplayer.hpp @@ -21,6 +21,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /** @file xvdisplayer.hpp ** This file contains the definition of XvDisplayer, the XVideo ** video output implementation @@ -28,6 +30,10 @@ ** @see displayer.hpp */ + +#ifndef GUI_OUTPUT_XVDISPLAYER_H +#define GUI_OUTPUT_XVDISPLAYER_H + #include #include #include @@ -36,8 +42,6 @@ #include "displayer.hpp" -#ifndef XVDISPLAYER_HPP -#define XVDISPLAYER_HPP namespace Gtk { class Widget; @@ -50,88 +54,86 @@ namespace output { * XvDisplayer is a class which is responsible for rendering a video * image via XVideo. */ -class XvDisplayer : public Displayer -{ -public: - /** - * Constructor - * @param drawing_area The widget into which the video image will be - * drawn. This value must not be NULL. - * @param width The width of the video image in pixels. This value - * must be greater than zero. - * @param height The height of the video image in pixels. This value - * must be greater than zero. - */ - XvDisplayer( Gtk::Widget *drawing_area, int width, int height ); +class XvDisplayer + : public Displayer + { + public: + /** + * Constructor + * @param drawing_area The widget into which the video image will be + * drawn. This value must not be NULL. + * @param width The width of the video image in pixels. This value + * must be greater than zero. + * @param height The height of the video image in pixels. This value + * must be greater than zero. + */ + XvDisplayer( Gtk::Widget *drawing_area, int width, int height ); + + + ~XvDisplayer(); + + /** + * Put an image of a given width and height with the expected input + * format (as indicated by the format method). + * @param[in] image The video image array to draw. + */ + void put( const void* image ); + + /** + * Indicates if this object can be used to render images on the + * running system. + */ + bool usable(); + + private: + + /** + * Specifies whether the object is currently attached to an XVideo + * port. + * @remarks This value is false until the constructor has finished + * successfully. + */ + bool gotPort; + + /** + * The current port being used. + * @remarks This value is meaningless unless gotPort is true. + */ + unsigned int grabbedPort; + + /** + * The widget that video will be drawn into. + * @remarks This value must be a valid pointer. + */ + Gtk::Widget *drawingArea; + + /** + * The display that video will be drawn into. + */ + Display *display; + + /** + * The X11 window that video will be drawn into. + */ + Window window; + + /** + * The graphics context which will be used when rendering video. + */ + GC gc; + + /** + * The shared memory image object which video will be written into. + */ + XvImage *xvImage; + + /** + * Info about the shared memory segment. + * @remarks shmInfo.shmaddr is set to NULL, when the SHM is detached. + */ + XShmSegmentInfo shmInfo; + }; - /** - * Destructor - */ - ~XvDisplayer(); - - /** - * Put an image of a given width and height with the expected input - * format (as indicated by the format method). - * @param[in] image The video image array to draw. - */ - void put( const void* image ); - - /** - * Indicates if this object can be used to render images on the - * running system. - */ - bool usable(); - -private: - - /** - * Specifies whether the object is currently attached to an XVideo - * port. - * @remarks This value is false until the constructor has finished - * successfully. - */ - bool gotPort; - /** - * The current port being used. - * @remarks This value is meaninless unless gotPort is true. - */ - unsigned int grabbedPort; - - /** - * The widget that video will be drawn into. - * @remarks This value must be a valid pointer. - */ - Gtk::Widget *drawingArea; - - /** - * The display that video will be drawn into. - */ - Display *display; - - /** - * The X11 window that video will be drawn into. - */ - Window window; - - /** - * The graphics context which will be used when rednering video. - */ - GC gc; - - /** - * The shared memory image object which video will be written into. - */ - XvImage *xvImage; - - /** - * Info about the shared memory segment. - * @remarks shmInfo.shmaddr is set to NULL, when the SHM is detached. - */ - XShmSegmentInfo shmInfo; -}; - -} // namespace output -} // namespace gui - +}} // namespace gui::output #endif // XVDISPLAYER_HPP diff --git a/src/gui/widgets/video-display-widget.cpp b/src/gui/widgets/video-display-widget.cpp index 22531d1f5..7f11c0036 100644 --- a/src/gui/widgets/video-display-widget.cpp +++ b/src/gui/widgets/video-display-widget.cpp @@ -1,5 +1,5 @@ /* - video-display-widget.cpp - Implementation of the video viewer widget + VideoDisplayWidget - Implementation of the video viewer widget Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -20,6 +20,7 @@ * *****************************************************/ + #include "gui/gtk-lumiera.hpp" #include "gui/output/xvdisplayer.hpp" #include "gui/output/gdkdisplayer.hpp" @@ -28,62 +29,58 @@ namespace gui { namespace widgets { - -VideoDisplayWidget::VideoDisplayWidget() : - displayer(NULL) -{ -} - -VideoDisplayWidget::~VideoDisplayWidget() -{ - if(displayer != NULL) - delete displayer; -} - -Displayer* -VideoDisplayWidget::get_displayer() const -{ - return displayer; -} - -void -VideoDisplayWidget::on_realize() -{ - // Call base class: - Gtk::Widget::on_realize(); - - // Set colors - modify_bg(Gtk::STATE_NORMAL, Gdk::Color("black")); - - if(displayer != NULL) - delete displayer; - displayer = createDisplayer(this, 320, 240); - - add_events(Gdk::ALL_EVENTS_MASK); -} - -Displayer* -VideoDisplayWidget::createDisplayer( Gtk::Widget *drawingArea, int width, int height ) -{ - REQUIRE(drawingArea != NULL); - REQUIRE(width > 0 && height > 0); - Displayer *displayer = NULL; + VideoDisplayWidget::VideoDisplayWidget ( ) + : displayer (NULL) + { } - displayer = new XvDisplayer( drawingArea, width, height ); - if ( !displayer->usable() ) - { - delete displayer; - displayer = NULL; - } - - if ( displayer == NULL ) - { - displayer = new GdkDisplayer( drawingArea, width, height ); - } - - return displayer; -} - -} // namespace widgets -} // namespace gui + + VideoDisplayWidget::~VideoDisplayWidget ( ) + { + if (displayer != NULL) delete displayer; + } + + Displayer* + VideoDisplayWidget::get_displayer ( ) const + { + return displayer; + } + + void VideoDisplayWidget::on_realize ( ) + { + // Call base class: + Gtk::Widget::on_realize (); + + // Set colours + modify_bg (Gtk::STATE_NORMAL, Gdk::Color ("black")); + + if (displayer != NULL) delete displayer; + displayer = createDisplayer (this, 320, 240); + + add_events (Gdk::ALL_EVENTS_MASK); + } + + Displayer* + VideoDisplayWidget::createDisplayer (Gtk::Widget *drawingArea, int width, int height) + { + REQUIRE (drawingArea != NULL); + REQUIRE (width > 0 && height > 0); + + Displayer *displayer = NULL; + + displayer = new XvDisplayer (drawingArea, width, height); + if (!displayer->usable ()) + { + delete displayer; + displayer = NULL; + } + + if (displayer == NULL) + { + displayer = new GdkDisplayer (drawingArea, width, height); + } + + return displayer; + } + +}}// namespace gui::widgets diff --git a/src/gui/widgets/video-display-widget.hpp b/src/gui/widgets/video-display-widget.hpp index 8e6e9a5e4..b77cb118e 100644 --- a/src/gui/widgets/video-display-widget.hpp +++ b/src/gui/widgets/video-display-widget.hpp @@ -1,5 +1,5 @@ /* - video-display-widget.hpp - Declaration of the video viewer widget + VIDEO-DISPLAY-WIDGET.hpp - GUI widget for displaying video Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -19,10 +19,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /** @file video-display-widget.hpp ** This file contains the definition of video viewer widget */ + #ifndef VIDEO_DISPLAY_WIDGET_HPP #define VIDEO_DISPLAY_WIDGET_HPP @@ -34,31 +37,31 @@ using namespace gui::output; namespace gui { namespace widgets { - -class VideoDisplayWidget : public Gtk::DrawingArea -{ -public: - VideoDisplayWidget(); - - ~VideoDisplayWidget(); - Displayer* get_displayer() const; - - /* ===== Overrides ===== */ -private: - virtual void on_realize(); - - /* ===== Internals ===== */ -private: - static Displayer* - createDisplayer( Gtk::Widget *drawingArea, int width, int height ); - -private: - - Displayer *displayer; -}; - -} // namespace widgets -} // namespace gui - + class VideoDisplayWidget + : public Gtk::DrawingArea + { + public: + VideoDisplayWidget(); + + ~VideoDisplayWidget(); + + Displayer* get_displayer() const; + + /* ===== Overrides ===== */ + private: + virtual void on_realize(); + + /* ===== Internals ===== */ + private: + static Displayer* + createDisplayer( Gtk::Widget *drawingArea, int width, int height ); + + private: + + Displayer *displayer; + }; + + +}} // namespace gui::widgets #endif // VIDEO_DISPLAY_WIDGET_HPP diff --git a/src/lib/error.hpp b/src/lib/error.hpp index 51fce33e0..e96942ee6 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -2,7 +2,7 @@ ERROR.hpp - Lumiera Exception Interface Copyright (C) Lumiera.org - 2008-2010 Hermann Vosseler + 2008,2010 Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp index a1f73e53e..4eed4da1d 100644 --- a/src/lumiera/main.cpp +++ b/src/lumiera/main.cpp @@ -2,7 +2,7 @@ main.cpp - start the Lumiera Application Copyright (C) Lumiera.org - 2007-2009, Joel Holdsworth + 2007,2011, Joel Holdsworth Christian Thaeter Hermann Vosseler diff --git a/src/proc/asset/struct-factory-impl.hpp b/src/proc/asset/struct-factory-impl.hpp index bfc702d18..78986ea74 100644 --- a/src/proc/asset/struct-factory-impl.hpp +++ b/src/proc/asset/struct-factory-impl.hpp @@ -2,7 +2,7 @@ STRUCT-FACTORY-IMPL.hpp - crating structural assets (impl details) Copyright (C) Lumiera.org - 2008-2010, Hermann Vosseler + 2008,2010, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/proc/mobject/session/mobjectfactory.cpp b/src/proc/mobject/session/mobjectfactory.cpp index f6c6aa807..8c47a3d8e 100644 --- a/src/proc/mobject/session/mobjectfactory.cpp +++ b/src/proc/mobject/session/mobjectfactory.cpp @@ -2,7 +2,7 @@ MObjectFactory - creating concrete MObject subclass instances Copyright (C) Lumiera.org - 2008-2010, Hermann Vosseler + 2008,2010, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/proc/mobject/session/mobjectfactory.hpp b/src/proc/mobject/session/mobjectfactory.hpp index 67e1ada4e..82efd7c0c 100644 --- a/src/proc/mobject/session/mobjectfactory.hpp +++ b/src/proc/mobject/session/mobjectfactory.hpp @@ -2,7 +2,7 @@ MOBJECTFACTORY.hpp - creating concrete MObject subclass instances Copyright (C) Lumiera.org - 2008-2010, Hermann Vosseler + 2008, 2010, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/tests/components/proc/mobject/session/session-element-tracker-test.cpp b/tests/components/proc/mobject/session/session-element-tracker-test.cpp index 207d099a6..d5ba6ad8b 100644 --- a/tests/components/proc/mobject/session/session-element-tracker-test.cpp +++ b/tests/components/proc/mobject/session/session-element-tracker-test.cpp @@ -2,7 +2,7 @@ SessionElementTracker(Test) - check the facility to track and expose selected model elements Copyright (C) Lumiera.org - 2008-2010, Hermann Vosseler + 2008, 2010, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/tests/components/proc/mobject/session/session-structure-test.cpp b/tests/components/proc/mobject/session/session-structure-test.cpp index 458633d5c..6ae27c3ee 100644 --- a/tests/components/proc/mobject/session/session-structure-test.cpp +++ b/tests/components/proc/mobject/session/session-structure-test.cpp @@ -2,7 +2,7 @@ SessionStructure(Test) - verifying basic Session/Model structure Copyright (C) Lumiera.org - 2008-2010, Hermann Vosseler + 2008, 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp b/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp index c250b6019..a1eb84a34 100644 --- a/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp +++ b/tests/components/proc/mobject/session/timeline-sequence-handling-test.cpp @@ -2,7 +2,7 @@ TimelineSequenceHandling(Test) - managing the top level session facade objects Copyright (C) Lumiera.org - 2008-2010, Hermann Vosseler + 2008, 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/tests/lib/custom-shared-ptr-test.cpp b/tests/lib/custom-shared-ptr-test.cpp index c6d9e147d..45bf5da25 100644 --- a/tests/lib/custom-shared-ptr-test.cpp +++ b/tests/lib/custom-shared-ptr-test.cpp @@ -2,7 +2,7 @@ CustomSharedPtr(Test) - ref counting, equality and comparisons Copyright (C) Lumiera.org - 2008-2010, Hermann Vosseler + 2008, 2010, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as From e84ceec9b5ff0465c5f72c47a082d9c850cd6578 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 5 Feb 2011 23:53:37 +0100 Subject: [PATCH 123/140] refactor some lib facilities - Cmdline into namespace lib - test coverage for the SearchPath iterator --- src/common/appstate.cpp | 2 + src/common/bootstrap.cpp | 1 + src/common/bootstrap.hpp | 2 - src/common/option.cpp | 4 +- src/common/option.hpp | 8 +- src/gui/guistart.cpp | 4 +- src/lib/cmdline.cpp | 5 +- src/lib/cmdline.hpp | 8 +- src/lib/error.c | 4 +- src/lib/searchpath.cpp | 94 +++++++++++ src/lib/searchpath.hpp | 147 ++++++++++++++++++ src/lib/test/suite.cpp | 2 +- src/lib/test/testoption.cpp | 41 +++-- src/lib/test/testoption.hpp | 2 +- src/lumiera/main.cpp | 2 +- tests/40components.tests | 15 ++ ...appertest.cpp => cmdline-wrapper-test.cpp} | 22 +-- tests/lib/mainsuite.cpp | 2 +- tests/lib/query/query-utils-test.cpp | 2 +- tests/lib/search-path-splitter-test.cpp | 101 ++++++++++++ tests/lib/subsystem-runner-test.cpp | 2 +- tests/lib/test/testoptiontest.cpp | 13 +- 22 files changed, 422 insertions(+), 61 deletions(-) create mode 100644 src/lib/searchpath.cpp create mode 100644 src/lib/searchpath.hpp rename tests/lib/{test/cmdlinewrappertest.cpp => cmdline-wrapper-test.cpp} (84%) create mode 100644 tests/lib/search-path-splitter-test.cpp diff --git a/src/common/appstate.cpp b/src/common/appstate.cpp index 20d2c6e96..391a550f7 100644 --- a/src/common/appstate.cpp +++ b/src/common/appstate.cpp @@ -34,6 +34,7 @@ extern "C" { #include "common/plugin.h" } +#include "lib/symbol.hpp" #include "lib/util.hpp" #include "include/configfacade.hpp" //////////TODO: temp hack to force configfacade.o to be linked in @@ -59,6 +60,7 @@ namespace lumiera { LifecycleHook schedule_ (ON_BASIC_INIT, &createAppStateInstance); + lib::Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); } diff --git a/src/common/bootstrap.cpp b/src/common/bootstrap.cpp index 35f2d20cc..12eb75e01 100644 --- a/src/common/bootstrap.cpp +++ b/src/common/bootstrap.cpp @@ -6,6 +6,7 @@ +#include "lib/error.hpp" #include "common/bootstrap.hpp" extern "C" { diff --git a/src/common/bootstrap.hpp b/src/common/bootstrap.hpp index 43e43726a..aa2a41f0b 100644 --- a/src/common/bootstrap.hpp +++ b/src/common/bootstrap.hpp @@ -7,8 +7,6 @@ #ifndef COMMON_DUMMY_FUNC_H #define COMMON_DUMMY_FUNC_H -#include "include/nobugcfg.h" - #include diff --git a/src/common/option.cpp b/src/common/option.cpp index d01afc88d..2be4a8d46 100644 --- a/src/common/option.cpp +++ b/src/common/option.cpp @@ -33,7 +33,7 @@ typedef boost::program_options::variables_map VarMap; namespace op = boost::program_options; -using util::VectS; +using lib::VectS; using util::cStr; @@ -50,7 +50,7 @@ namespace lumiera { * \endcode * @todo describe the actual options */ - Option::Option (util::Cmdline& cmdline) + Option::Option (lib::Cmdline& cmdline) : syntax("Lumiera, the non linear video editor. Supported parameters"), parameters() { diff --git a/src/common/option.hpp b/src/common/option.hpp index a6ccc4ab9..012844ea3 100644 --- a/src/common/option.hpp +++ b/src/common/option.hpp @@ -37,13 +37,13 @@ namespace lumiera { using std::string; using std::ostream; - using util::VectS; + using lib::VectS; /** - * Support for selecting and configuring testcases - * via commandline arguments. A preconfigured wrapper + * Frontend for handling the Lumiera application + * commandline arguments. A preconfigured wrapper * around boost::program_options, with the ability * to tolerate unknown options. The commandline * to be parsed is taken wrapped into a Cmdline @@ -54,7 +54,7 @@ namespace lumiera { class Option : private boost::noncopyable { public: - Option (util::Cmdline& cmdline); + Option (lib::Cmdline& cmdline); const string getSessName(); const VectS getScripts(); diff --git a/src/gui/guistart.cpp b/src/gui/guistart.cpp index 6eadf445c..d5f072058 100644 --- a/src/gui/guistart.cpp +++ b/src/gui/guistart.cpp @@ -3,8 +3,8 @@ Copyright (C) Lumiera.org 2007-2008, Joel Holdsworth - 2009, Christian Thaeter - Hermann Vosseler + 2009, Hermann Vosseler + Christian Thaeter This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/src/lib/cmdline.cpp b/src/lib/cmdline.cpp index 8539909ec..bffbd2e8e 100644 --- a/src/lib/cmdline.cpp +++ b/src/lib/cmdline.cpp @@ -40,11 +40,12 @@ using boost::regex; using boost::smatch; using boost::regex_search; +using util::noneg; #include -namespace util { +namespace lib { /** create as a tokenised copy of the current commandline. @@ -88,4 +89,4 @@ namespace util { -} // namespace util +} // namespace lib diff --git a/src/lib/cmdline.hpp b/src/lib/cmdline.hpp index 88d6e2d5a..f65db650b 100644 --- a/src/lib/cmdline.hpp +++ b/src/lib/cmdline.hpp @@ -21,8 +21,8 @@ */ -#ifndef UTIL_CMDLINE_H -#define UTIL_CMDLINE_H +#ifndef LIB_CMDLINE_H +#define LIB_CMDLINE_H #include #include @@ -30,7 +30,7 @@ -namespace util { +namespace lib { using std::string; using std::vector; @@ -66,5 +66,5 @@ namespace util { -} // namespace util +} // namespace lib #endif diff --git a/src/lib/error.c b/src/lib/error.c index d91a27359..322fa2126 100644 --- a/src/lib/error.c +++ b/src/lib/error.c @@ -33,7 +33,7 @@ predefined errors */ LUMIERA_ERROR_DEFINE (ERRNO, "errno"); -LUMIERA_ERROR_DEFINE (EERROR, "could not initialize error system"); +LUMIERA_ERROR_DEFINE (EERROR, "could not initialise error system"); LUMIERA_ERROR_DEFINE (UNKNOWN, "unknown error"); @@ -78,7 +78,7 @@ lumiera_error_get (void) LumieraErrorcontext self = pthread_getspecific (lumiera_error_tls); if (!self) { - /* malloc() and not lumiera_malloc() here because nothing else might be initialized when calling this */ + /* malloc() and not lumiera_malloc() here because nothing else might be initialised when calling this */ self = malloc (sizeof *self); if (!self) LUMIERA_DIE (EERROR); diff --git a/src/lib/searchpath.cpp b/src/lib/searchpath.cpp new file mode 100644 index 000000000..02f9f2e0a --- /dev/null +++ b/src/lib/searchpath.cpp @@ -0,0 +1,94 @@ +/* + Searchpath - helpers for searching directory lists and locating modules + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "lib/error.hpp" +#include "lib/searchpath.hpp" +#include "lib/symbol.hpp" + + +namespace lib { + + + LUMIERA_ERROR_DEFINE (FILE_NOT_DIRECTORY, "path element points at a file instead of a directory"); + + + + namespace { + + Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); + } + + + + regex SearchPathSplitter::EXTRACT_PATHSPEC ("(\\$?ORIGIN/)?([^:]+)"); + + + + + /** @internal helper to figure out the installation directory, + * as given by the absolute path of the currently executing program + * @warning this is Linux specific code */ + string + findExePath() + { + static string buff(lib::STRING_MAX_RELEVANT+1, '\0' ); + if (!buff[0]) + { + ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], lib::STRING_MAX_RELEVANT); + + if (0 > chars_read || chars_read == ssize_t(lib::STRING_MAX_RELEVANT)) + throw error::Fatal ("unable to discover path of running executable"); + + buff.resize(chars_read); + } + return buff; + } + + + + + string + resolveModulePath (string moduleName, string searchPath) + { + fsys::path modulePathName (moduleName); + SearchPathSplitter searchLocation(searchPath); + + while (true) + { + if (fsys::exists (modulePathName)) + { + INFO (config, "found module %s", modulePathName.string().c_str()); + return modulePathName.string(); + } + + // try / continue search path + if (searchLocation.isValid()) + modulePathName = fsys::path() / searchLocation.fetch() / moduleName; + else + throw error::Config ("Module \""+moduleName+"\" not found" + + (searchPath.empty()? ".":" in search path: "+searchPath)); + } } + + + +} // namespace lib diff --git a/src/lib/searchpath.hpp b/src/lib/searchpath.hpp new file mode 100644 index 000000000..eff8b2510 --- /dev/null +++ b/src/lib/searchpath.hpp @@ -0,0 +1,147 @@ +/* + SEARCHPATH.hpp - helpers for searching directory lists and locating modules + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + + +#ifndef COMMON_SEARCHPATH_H +#define COMMON_SEARCHPATH_H + +#include "lib/error.hpp" +#include "lib/bool-checkable.hpp" + +#include +#include +#include +#include +#include + + +namespace lib { + + using std::string; + using boost::regex; + using boost::smatch; + using boost::regex_search; + using boost::sregex_iterator; + + typedef smatch::value_type const& SubMatch; + + namespace error = lumiera::error; + namespace fsys = boost::filesystem; + + LUMIERA_ERROR_DECLARE (FILE_NOT_DIRECTORY); ///< path element points at a file instead of a directory + using error::LUMIERA_ERROR_ITER_EXHAUST; + + + /** retrieve the location of the executable */ + string findExePath(); + + + /** + * Helper: Access a path Specification as a sequence of filesystem Paths. + * This iterator class dissects a ':'-separated path list. The individual + * components may use the symbol \c $ORIGIN to denote the directory holding + * the current executable. After resolving this symbol, a valid absolute or + * relative filesystem path should result, which must not denote an existing + * file (directory is OK). + * @note #fetch picks the current component and advances the iteration. + */ + class SearchPathSplitter + : public BoolCheckable + { + string pathSpec_; + sregex_iterator pos_, + end_; + + static regex EXTRACT_PATHSPEC; + + public: + SearchPathSplitter (string const& searchPath) + : pathSpec_(searchPath) + , pos_(pathSpec_.begin(),pathSpec_.end(), EXTRACT_PATHSPEC) + , end_() + { } + + bool + isValid() const + { + return pos_ != end_; + } + + string + fetch () + { + if (!isValid()) + throw error::Logic ("Search path exhausted." + ,LUMIERA_ERROR_ITER_EXHAUST); + + string currentPathElement = resolveRelative(); + ++pos_; + return currentPathElement; + } + + private: + /** maybe resolve a path spec given relative to + * the current Executable location ($ORIGIN) */ + string + resolveRelative () + { + if (containsORIGINToken()) + return asAbsolutePath(); + else + return getFullPath(); + } + + SubMatch found(int group=0) { return (*pos_)[group]; } + + bool containsORIGINToken() { return found(1).matched; } + string getRelativePath() { return found(2); } + string getFullPath() { return found(); } + + string + asAbsolutePath() + { + fsys::path exePathName (findExePath()); + fsys::path modPathName (exePathName.remove_leaf() / getRelativePath()); + + if (fsys::exists(modPathName) && !fsys::is_directory (modPathName)) + throw error::Invalid ("Error in search path: component \""+modPathName.string()+"\" is not a directory" + ,LUMIERA_ERROR_FILE_NOT_DIRECTORY); + + return modPathName.directory_string(); + } + }; + + + + /** helper to establish the location to search for loadable modules. + * This is a simple demonstration of the basic technique used in the + * real application source to establish a plugin search path, based + * on the actual executable position plus compiled in and configured + * relative and absolute path specifications. + */ + string resolveModulePath (string moduleName, string searchPath = ""); + + + +} // namespace lib +#endif diff --git a/src/lib/test/suite.cpp b/src/lib/test/suite.cpp index b109b755d..dd8e35bfe 100644 --- a/src/lib/test/suite.cpp +++ b/src/lib/test/suite.cpp @@ -232,7 +232,7 @@ namespace test { void Suite::describe () { - util::Cmdline noCmdline(""); + lib::Cmdline noCmdline(""); PTestMap tests = testcases.getGroup(groupID_); ASSERT (tests); diff --git a/src/lib/test/testoption.cpp b/src/lib/test/testoption.cpp index 3a84ee4f8..9e0598d2b 100644 --- a/src/lib/test/testoption.cpp +++ b/src/lib/test/testoption.cpp @@ -33,10 +33,9 @@ typedef boost::program_options::variables_map VarMap; namespace op = boost::program_options; -using util::VectS; +using lib::VectS; -namespace test - { +namespace test { /** set up an options parser to use the current commandline. @@ -47,7 +46,7 @@ namespace test * --describe * \endcode */ - TestOption::TestOption (util::Cmdline& cmdline) + TestOption::TestOption (lib::Cmdline& cmdline) : syntax("Run a collection of test cases. Supported parameters"), parameters() { @@ -89,37 +88,37 @@ namespace test */ const string TestOption::getTestgroup () - { - ASSERT (parameters.count ("group")); - return parameters["group"].as(); - } + { + ASSERT (parameters.count ("group")); + return parameters["group"].as(); + } /** @return ID of a single test to run, empty string if not specified */ const string TestOption::getTestID () - { - if (parameters.count ("id") && - parameters["id"].as().size() > 0) - return parameters["id"].as()[0]; - else - return string (); - } + { + if (parameters.count ("id") && + parameters["id"].as().size() > 0) + return parameters["id"].as()[0]; + else + return string (); + } /** @return \c true if --describe switch was given */ bool TestOption::getDescribe () - { - return parameters["describe"].as(); - } + { + return parameters["describe"].as(); + } ostream& operator<< (ostream& os, const TestOption& to) - { - return os << to.syntax; - } + { + return os << to.syntax; + } diff --git a/src/lib/test/testoption.hpp b/src/lib/test/testoption.hpp index efa9ea2cd..4d41de8e6 100644 --- a/src/lib/test/testoption.hpp +++ b/src/lib/test/testoption.hpp @@ -53,7 +53,7 @@ namespace test { class TestOption : private boost::noncopyable { public: - TestOption (util::Cmdline& cmdline); + TestOption (lib::Cmdline& cmdline); const string getTestgroup (); const string getTestID (); bool getDescribe (); diff --git a/src/lumiera/main.cpp b/src/lumiera/main.cpp index 4eed4da1d..14d039b7d 100644 --- a/src/lumiera/main.cpp +++ b/src/lumiera/main.cpp @@ -34,7 +34,7 @@ #include "proc/facade.hpp" #include "gui/guifacade.hpp" -using util::Cmdline; +using lib::Cmdline; using lumiera::Subsys; using lumiera::AppState; using lumiera::ON_GLOBAL_INIT; diff --git a/tests/40components.tests b/tests/40components.tests index b22c6025d..d72a6a8f8 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -590,6 +590,21 @@ return: 0 END +TEST "Search path walking" SearchPathSplitter_test < @@ -27,21 +27,26 @@ #include #include +#include #include +using util::for_each; +using std::string; using std::cout; +using std::endl; -namespace util { -namespace test { +namespace lib { +namespace test{ + using boost::lambda::_1; using boost::lambda::var; - /** @test for util::Cmdline, wrapping various example cmdlines */ + /** @test for lib::Cmdline, wrapping various example cmdlines */ class CmdlineWrapper_test : public Test { void @@ -61,12 +66,12 @@ namespace test { void testLine (const string cmdline) { - cout << "wrapping cmdline:" << cmdline << "..." << "\n"; + cout << "wrapping cmdline:" << cmdline << "..." << endl; int i=0; Cmdline theCmdline (cmdline); for_each(theCmdline, (cout << var(i)++ << "|" << _1 << "|\n")); - cout << "-->" << theCmdline << "\n"; + cout << "-->" << theCmdline << endl; // consistency checks std::ostringstream output; @@ -88,12 +93,11 @@ namespace test { { const char* fakeArg[3] = {"CMD", "one ", "two"}; Cmdline theCmdline(3, fakeArg); - cout << "Standard Cmdlineformat:" << theCmdline << "\n"; + cout << "Standard Cmdlineformat:" << theCmdline << endl; } }; LAUNCHER (CmdlineWrapper_test, "unit common"); -}} // namespace util::test - +}} // namespace lib::test diff --git a/tests/lib/mainsuite.cpp b/tests/lib/mainsuite.cpp index 882ee78d4..4493fc9b6 100644 --- a/tests/lib/mainsuite.cpp +++ b/tests/lib/mainsuite.cpp @@ -39,7 +39,7 @@ using lumiera::ON_GLOBAL_SHUTDOWN; */ int main (int argc, const char* argv[]) { - util::Cmdline args (argc,argv); + lib::Cmdline args (argc,argv); test::TestOption optparser (args); test::Suite suite (optparser.getTestgroup()); LifecycleHook::trigger (ON_GLOBAL_INIT); diff --git a/tests/lib/query/query-utils-test.cpp b/tests/lib/query/query-utils-test.cpp index 70a9f59ec..b0af6b950 100644 --- a/tests/lib/query/query-utils-test.cpp +++ b/tests/lib/query/query-utils-test.cpp @@ -33,7 +33,7 @@ #include using lumiera::Query; -using util::Cmdline; +using lib::Cmdline; using util::isnil; using util::contains; using util::for_each; diff --git a/tests/lib/search-path-splitter-test.cpp b/tests/lib/search-path-splitter-test.cpp new file mode 100644 index 000000000..e4e824090 --- /dev/null +++ b/tests/lib/search-path-splitter-test.cpp @@ -0,0 +1,101 @@ +/* + SearchPathSplitter(Test) - iterating a search path specification + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" + +#include "lib/searchpath.hpp" +#include "common/appstate.hpp" + +#include + +using std::cout; +using std::endl; + + + +namespace lib { +namespace test { + + + /****************************************************** + * @test verify splitting a search path specification + * and retrieving the components by iteration. + * Embedded \c $ORIGIN tokens get resolved + * to the absolute path of this executable. + */ + class SearchPathSplitter_test : public Test + { + void + run (Arg) + { + walkSimplePaths (); + resolveEmbeddedOriginToken (); + } + + + void + walkSimplePaths () + { + walk (""); + walk (":"); + walk ("a:"); + walk (":a"); + walk ("a:b"); + walk (":a:b:c:"); + walk (" d : e f"); + walk ("/usr/bin:/usr/lib"); + + SearchPathSplitter sp(""); + VERIFY_ERROR (ITER_EXHAUST, sp.fetch() ); + } + + void + walk (string spec) + { + SearchPathSplitter path(spec); + while (path) + cout << "➢➢" << path.fetch() << endl; + } + + + + void + resolveEmbeddedOriginToken () + { + fsys::path exePath (findExePath()); + string expected = (exePath.remove_leaf() / "modules").string(); + + SearchPathSplitter sp("xyz:$ORIGIN/modules:abc"); + CHECK ("xyz" == sp.fetch()); + CHECK (sp.fetch() == expected); + CHECK ("abc" == sp.fetch()); + CHECK (!sp.isValid()); + } + }; + + + LAUNCHER (SearchPathSplitter_test, "unit common"); + + +}} // namespace lib::test diff --git a/tests/lib/subsystem-runner-test.cpp b/tests/lib/subsystem-runner-test.cpp index 8eebc7d26..6db7ebf99 100644 --- a/tests/lib/subsystem-runner-test.cpp +++ b/tests/lib/subsystem-runner-test.cpp @@ -69,7 +69,7 @@ namespace test { const uint DELAY_FOR_FLOUNDERING_THRAD_ms = 20; /** dummy options just to be ignored */ - util::Cmdline dummyArgs (""); + lib::Cmdline dummyArgs (""); lumiera::Option dummyOpt (dummyArgs); /** marker for simulated failure exceptions */ diff --git a/tests/lib/test/testoptiontest.cpp b/tests/lib/test/testoptiontest.cpp index 2fd3761d4..8bb517604 100644 --- a/tests/lib/test/testoptiontest.cpp +++ b/tests/lib/test/testoptiontest.cpp @@ -26,12 +26,11 @@ #include "lib/test/testoption.hpp" #include "lib/util.hpp" -using util::Cmdline; +using lib::Cmdline; using util::isnil; using std::endl; -namespace test - { +namespace test { /**************************************************************** * invokes the TestOption parser for various example commandlines @@ -76,10 +75,10 @@ namespace test void groupFilter2() { doIt (" --group TestGroupID SingleTestID "); } void additionalCmd() { doIt (" --group TestGroupID SingleTestID spam eggs"); } void additionalCmd2() { doIt ("\t\tSingleTestID spam --group TestGroupID \t --eggs"); } - + }; - LAUNCHER (TestOption_test, "function common"); - + LAUNCHER (TestOption_test, "function common"); + + } // namespace test - From 11d709b85ef5d65c9b70da51da6427db501d2ff5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 6 Feb 2011 02:12:00 +0100 Subject: [PATCH 124/140] incorporate basic setup.ini into the AppState object --- data/config/setup.ini | 14 +- src/common/appstate.cpp | 11 +- src/common/appstate.hpp | 7 +- src/common/basic-setup.cpp | 96 +++++++++ src/common/basic-setup.hpp | 88 ++++++++ src/common/bootstrap.cpp | 269 ------------------------ src/common/bootstrap.hpp | 37 ---- src/common/option.hpp | 3 +- src/lib/searchpath.cpp | 17 +- src/tool/try.cpp | 17 +- tests/lib/search-path-splitter-test.cpp | 1 - 11 files changed, 216 insertions(+), 344 deletions(-) create mode 100644 src/common/basic-setup.cpp create mode 100644 src/common/basic-setup.hpp delete mode 100644 src/common/bootstrap.cpp delete mode 100644 src/common/bootstrap.hpp diff --git a/data/config/setup.ini b/data/config/setup.ini index e4a00f4e2..c1da6425b 100644 --- a/data/config/setup.ini +++ b/data/config/setup.ini @@ -4,17 +4,9 @@ # (file located relative to the Lumiera executable) # -[BuildsystemDemo] -# This is dummy/demonstration executable -# built from the "scons" git tree. It serves to -# document and evolve the SCons buildsystem in isolation -# and for tests of the packaging and build process. +[Lumiera] +# Setup of the Lumiera video editor main application. +# $ORIGIN denotes the directory of the executable. # gui = gtk_gui.lum modulepath = $ORIGIN/modules - -[Lumiera] -# Lumiera video editor main application -# -# Nothing configurable as of 1/2011 -# diff --git a/src/common/appstate.cpp b/src/common/appstate.cpp index 391a550f7..c47dea849 100644 --- a/src/common/appstate.cpp +++ b/src/common/appstate.cpp @@ -41,6 +41,9 @@ extern "C" { using util::cStr; +#define LOCATION_OF_BOOTSTRAP_INI "$ORIGIN/config/setup.ini" + + namespace lumiera { @@ -60,7 +63,6 @@ namespace lumiera { LifecycleHook schedule_ (ON_BASIC_INIT, &createAppStateInstance); - lib::Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); } @@ -77,7 +79,8 @@ namespace lumiera { * client codes POV it just behaves like intended). */ AppState::AppState() - : subsystems_(0) + : setup_(LOCATION_OF_BOOTSTRAP_INI) + , subsystems_(0) , emergency_(false) , core_up_ (false) { } @@ -102,8 +105,8 @@ namespace lumiera { #define _THROW_IF \ - if (lumiera_error_peek()) \ - throw error::Fatal (lumiera_error()); + maybeThrow ("internal failure while initialising the "\ + "Lumiera application framework"); diff --git a/src/common/appstate.hpp b/src/common/appstate.hpp index 6472b5102..0f2a4ee14 100644 --- a/src/common/appstate.hpp +++ b/src/common/appstate.hpp @@ -21,7 +21,7 @@ */ /** @file appstate.hpp - ** Registering and managing some application-global services. + ** Registering and managing primary application-global services. ** This can be considered the "main" object of the Lumiera application ** Besides encapsulating the logic for starting up the fundamental parts ** of the application, there is a mechanism for registering \em subsystems @@ -30,6 +30,7 @@ ** callbacks) and provides the top-level catch-all error handling. ** ** @see LifecycleHook + ** @see BasicSetup ** @see Subsys ** @see main.cpp ** @see logging.h @@ -42,6 +43,7 @@ #include "lib/symbol.hpp" #include "common/option.hpp" #include "common/subsys.hpp" +#include "common/basic-setup.hpp" #include #include @@ -60,6 +62,7 @@ namespace lumiera { /** + * The Lumiera Application state and basic initialisation. * Singleton to hold global flags directing the overall application behaviour, * for triggering lifecycle events and performing early initialisation tasks. * AppState services are available already from static initialisation code. @@ -123,6 +126,8 @@ namespace lumiera { private: typedef scoped_ptr PSub; + BasicSetup setup_; + PSub subsystems_; bool emergency_; diff --git a/src/common/basic-setup.cpp b/src/common/basic-setup.cpp new file mode 100644 index 000000000..1492695c3 --- /dev/null +++ b/src/common/basic-setup.cpp @@ -0,0 +1,96 @@ +/* + BasicSetup - elementary self-configuration of the application + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 "common/basic-setup.hpp" +#include "lib/searchpath.hpp" +#include "lib/error.hpp" +#include "lib/util.hpp" + +extern "C" { +#include +} +#include +#include +#include + + +namespace lumiera { + + using std::string; + using std::ifstream; + + namespace fsys = boost::filesystem; + namespace opt = boost::program_options; + + namespace { // details of the bootstrap process... + + + // Helper to locate a module using a search path spec + using lib::resolveModulePath; + + /** use the general mechanism for resolving a search path + * to get the absolute path of the \c setup.ini */ + string + resolve (fsys::path iniSpec) + { + string file = iniSpec.leaf(); + string searchpath = iniSpec.branch_path().string(); + return resolveModulePath (file, searchpath); + } + + }//(End) implementation details + + + + /** + * Creating the BasicSetup object performs the + * initial self-configuration of the Lumiera Application. + * For this, the \c setup.ini file is located relative to the + * current application executable, read in and parsed into a + * map of setup variables. + */ + BasicSetup::BasicSetup (string bootstrapIni) + : syntax("Lumiera installation and platform configuration") + , settings() + { + syntax.add_options() + ("BuildsystemDemo.gui", opt::value(), + "name of the Lumiera GUI plugin to load") + ("BuildsystemDemo.modulepath", opt::value(), + "search path for loadable modules. " + "May us $ORIGIN to refer to the EXE location") + ; + + ifstream configIn (resolve(bootstrapIni).c_str()); + + + opt::parsed_options parsed = + opt::parse_config_file (configIn, syntax); + + opt::store (parsed, settings); + opt::notify(settings); + } + + + +} // namespace lumiera diff --git a/src/common/basic-setup.hpp b/src/common/basic-setup.hpp new file mode 100644 index 000000000..ec1759e3d --- /dev/null +++ b/src/common/basic-setup.hpp @@ -0,0 +1,88 @@ +/* + BASIC-SETUP.hpp - elementary self-configuration of the application + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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. + +*/ + + +#ifndef COMMON_BASIC_SETUP_H +#define COMMON_BASIC_SETUP_H + +#include "lib/error.hpp" + +#include +#include +#include + + +namespace lumiera { + + using std::string; + + namespace opt = boost::program_options; + + + /** + * Represents the elementary self-configuration + * of a running Lumiera application instance. + * This basic setup is tied to the location of the + * Lumiera executable; from there the initial configuration + * locates a \c setup.ini to read in the fundamental settings. + * This is even prerequisite for loading any extension modules + * or reading in extended application configuration; usually + * this bootstrap process happens at or before the start of + * the \c main() function. Any failure leads to immediate + * termination of the application. + * + * \par WIP 2011 -- preliminary configuration solution + * The full-blown Configuration subsystem is just drafted + * and way from being usable. Thus we'll use this basic + * configuration as a replacement for the time being. + * + * @see configfacade.hpp + * @see AppState + */ + class BasicSetup + : boost::noncopyable + { + opt::options_description syntax; + opt::variables_map settings; + + public: + BasicSetup (string bootstrapIni); + + string + operator[] (string const& key) const + { + return settings[key].as(); + } + + opt::variable_value const& + get (string const& key) + { + return settings[key]; + } + + + private: + }; + + +} // namespace lumiera +#endif diff --git a/src/common/bootstrap.cpp b/src/common/bootstrap.cpp deleted file mode 100644 index 12eb75e01..000000000 --- a/src/common/bootstrap.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* - dummy-func.cpp - placeholder with dummy functions to demonstrate building/loading shared modules - -* *************************************************************************************************/ - - - - -#include "lib/error.hpp" -#include "common/bootstrap.hpp" - -extern "C" { -#include -} -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace lumiera { - - using std::string; - using std::ifstream; - using boost::regex; - using boost::smatch; - using boost::regex_search; - using boost::sregex_iterator; - - typedef smatch::value_type const& SubMatch; - - - - namespace fsys = boost::filesystem; - namespace opt = boost::program_options; - - - namespace { // Implementation helpers - - const size_t STRING_MAX_RELEVANT = 1000; - - const char * const BOOTSTRAP_INI = "$ORIGIN/config/setup.ini"; - const char * const GET_PATH_TO_EXECUTABLE = "/proc/self/exe"; - - regex EXTRACT_PATHSPEC ("(\\$?ORIGIN/)?([^:]+)"); - - - /** the real application would throw a custom exception... */ - void - dieHard (string msg) - { - NOBUG_ERROR (NOBUG_ON, "Fatal Error: %s ", msg.c_str()); - abort(); - } - - - /** figure out the absolute path - * of the currently running executable - */ - string - catchMyself () - { - static string buff(STRING_MAX_RELEVANT+1, '\0' ); - if (!buff[0]) - { - ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT); - - if (0 > chars_read || chars_read == ssize_t(STRING_MAX_RELEVANT)) - dieHard ("unable to discover path of running executable"); - - buff.resize(chars_read); - } - return buff; - } - - - - /** - * Helper: Access a path Specification as a sequence of filesystem Paths. - * This iterator class dissects a ':'-separated path list. The individual - * components may use the symbol \c $ORIGIN to denote the directory holding - * the current executable. After resolving this symbol, a valid absolute or - * relative filesystem path should result, which must not denote an existing - * file (directory is OK). - * @note #fetch picks the current component and advances the iteration. - */ - class SearchPathSplitter - : boost::noncopyable - { - sregex_iterator pos_, - end_; - - public: - SearchPathSplitter (string const& searchPath) - : pos_(searchPath.begin(),searchPath.end(), EXTRACT_PATHSPEC) - , end_() - { } - - bool - isValid() const - { - return pos_ != end_; - } - - string - fetch () - { - if (!isValid()) - dieHard ("Search path exhausted."); - - string currentPathElement = resolveRelative(); - ++pos_; - return currentPathElement; - } - - private: - /** maybe resolve a path spec given relative to - * the current Executable location ($ORIGIN) */ - string - resolveRelative () - { - if (containsORIGINToken()) - return asAbsolutePath(); - else - return getFullPath(); - } - - SubMatch found(int group=0) { return (*pos_)[group]; } - - bool containsORIGINToken() { return found(1).matched; } - string getRelativePath() { return found(2); } - string getFullPath() { return found(); } - - string - asAbsolutePath() - { - fsys::path exePathName (catchMyself()); - fsys::path modPathName (exePathName.remove_leaf() / getRelativePath()); - - if (fsys::exists(modPathName) && !fsys::is_directory (modPathName)) - dieHard ("Error in search path: component \""+modPathName.string()+"\" is not a directory"); - - return modPathName.directory_string(); - } - }; - - - /** helper to establish the location to search for loadable modules. - * This is a simple demonstration of the basic technique used in the - * real application source to establish a plugin search path, based - * on the actual executable position plus compiled in and configured - * relative and absolute path specifications. - */ - string - resolveModulePath (string moduleName, string searchPath = "") - { - fsys::path modulePathName (moduleName); - SearchPathSplitter searchLocation(searchPath); - - while (true) - { - if (fsys::exists (modulePathName)) - { - INFO (config, "found module %s", modulePathName.string().c_str()); - return modulePathName.string(); - } - - // try / continue search path - if (searchLocation.isValid()) - modulePathName = fsys::path() / searchLocation.fetch() / moduleName; - else - dieHard ("Module \""+moduleName+"\" not found" - + (searchPath.empty()? ".":" in search path: "+searchPath)); - } } - - - - /** - * Encapsulate an INI-style configuration file. - * The acceptable settings are defined in the ctor. - * Implementation based on boost::program_options - */ - class Config - : boost::noncopyable - { - opt::options_description syntax; - opt::variables_map settings; - - public: - Config (string bootstrapIni) - : syntax("Lumiera installation and platform configuration") - , settings() - { - syntax.add_options() - ("BuildsystemDemo.gui", opt::value(), - "name of the Lumiera GUI plugin to load") - ("BuildsystemDemo.modulepath", opt::value(), - "search path for loadable modules. " - "May us $ORIGIN to refer to the EXE location") - ; - - ifstream configIn (resolve(bootstrapIni).c_str()); - - - opt::parsed_options parsed = - opt::parse_config_file (configIn, syntax); - - opt::store (parsed, settings); - opt::notify(settings); - } - - string - operator[] (const string key) const - { - return settings[key].as(); - } - - private: - string - resolve (fsys::path iniSpec) - { - string file = iniSpec.leaf(); - string searchpath = iniSpec.branch_path().string(); - return resolveModulePath (file, searchpath); - } - }; - - }//(End) implementation helpers - - - - - - - - void - loadDummyGui() - { - Config appConfig(BOOTSTRAP_INI); - string guiModule = appConfig["BuildsystemDemo.gui"]; - string moduleSearch = appConfig["BuildsystemDemo.modulepath"]; - string moduleLocation = resolveModulePath (guiModule, moduleSearch); - - void* handle = dlopen (moduleLocation.c_str(), RTLD_LAZY|RTLD_LOCAL); - if (handle) - { - typedef void (*VoidFunc)(void); - - VoidFunc entryPoint = (VoidFunc) dlsym (handle, "start_dummy_gui"); - - if (!entryPoint) - dieHard ("unable to resolve the entry point symbol after loading the GUI module."); - - else - (*entryPoint) (); // activate loaded module - } - else - dieHard ("unable to load "+moduleLocation); - } - - - - -} // namespace lumiera diff --git a/src/common/bootstrap.hpp b/src/common/bootstrap.hpp deleted file mode 100644 index aa2a41f0b..000000000 --- a/src/common/bootstrap.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - dummy-func.hpp - placeholder with dummy functions to demonstrate building shared modules - -* ******************************************************************************************/ - - -#ifndef COMMON_DUMMY_FUNC_H -#define COMMON_DUMMY_FUNC_H - -#include - - -namespace lumiera { - - using std::string; - - /** this is a function located in the liblumieracore.so, - * which attempts to load the "pseudo-gui" as shared module - * and invoke the gui-main. The sole purpose of this function - * is to demonstrate that the SCons build system is working. - * - * \par requirements - * While this isn't the actual implementation used in Lumiera, - * we try to mimic or demonstrate the techniques used to resolve - * the actual module to be loaded. So there are some requirements - * - \c $ORIGIN/config/setup.ini exists and defines... - * - a section [BuildsystemDemo], which holds - * - a setting gui = gtk_gui.lum - * - a module search path, typically: modulepath = $ORIGIN/modules - * Here the token \c $ORIGIN is automatically resolved to the directory - * holding the current executable, by reading the symlink \c /proc/self/exe - */ - void loadDummyGui(); - - -} // namespace lumiera -#endif diff --git a/src/common/option.hpp b/src/common/option.hpp index 012844ea3..1d706e5d5 100644 --- a/src/common/option.hpp +++ b/src/common/option.hpp @@ -51,7 +51,8 @@ namespace lumiera { * vector will contain only the remaining * unrecognised parts. */ - class Option : private boost::noncopyable + class Option + : boost::noncopyable { public: Option (lib::Cmdline& cmdline); diff --git a/src/lib/searchpath.cpp b/src/lib/searchpath.cpp index 02f9f2e0a..809cc4889 100644 --- a/src/lib/searchpath.cpp +++ b/src/lib/searchpath.cpp @@ -26,25 +26,22 @@ #include "lib/symbol.hpp" +/** how to retrieve the absolute path of the currently running executable + * on a Linux system: read the link provided by the kernel through /proc + */ +#define GET_PATH_TO_EXECUTABLE "/proc/self/exe" + + + namespace lib { - LUMIERA_ERROR_DEFINE (FILE_NOT_DIRECTORY, "path element points at a file instead of a directory"); - - namespace { - - Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); - } - - regex SearchPathSplitter::EXTRACT_PATHSPEC ("(\\$?ORIGIN/)?([^:]+)"); - - /** @internal helper to figure out the installation directory, * as given by the absolute path of the currently executing program * @warning this is Linux specific code */ diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 01a99925b..bdfca7100 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -30,21 +30,19 @@ extern "C" { #include } -//#include "lib/error.hpp" -//#include "lib/symbol.hpp" +#include "lib/error.hpp" +#include "lib/symbol.hpp" using std::string; using std::cout; using std::endl; -//using lib::Literal; -//using lib::STRING_MAX_RELEVANT; -const size_t STRING_MAX_RELEVANT = 1000; +using lib::Literal; +using lib::STRING_MAX_RELEVANT; -//namespace error = lumiera::error; +namespace error = lumiera::error; -//Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); -const char * const GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); +Literal GET_PATH_TO_EXECUTABLE ("/proc/self/exe"); string catchMyself () @@ -53,8 +51,7 @@ catchMyself () ssize_t chars_read = readlink (GET_PATH_TO_EXECUTABLE, &buff[0], STRING_MAX_RELEVANT); if (0 > chars_read || chars_read == ssize_t(STRING_MAX_RELEVANT)) -// throw error::Fatal ("unable to discover path of running executable") - throw string("unable to discover path of running executable"); + throw error::Fatal ("unable to discover path of running executable"); buff.resize(chars_read); return buff; diff --git a/tests/lib/search-path-splitter-test.cpp b/tests/lib/search-path-splitter-test.cpp index e4e824090..bd58190e3 100644 --- a/tests/lib/search-path-splitter-test.cpp +++ b/tests/lib/search-path-splitter-test.cpp @@ -25,7 +25,6 @@ #include "lib/test/test-helper.hpp" #include "lib/searchpath.hpp" -#include "common/appstate.hpp" #include From 9f3c1ecf00cf787c7aa1690099c039d0cd594dde Mon Sep 17 00:00:00 2001 From: Stefan Kangas Date: Mon, 27 Dec 2010 08:09:42 +0100 Subject: [PATCH 125/140] spelling fixes --- doc/DIR_INFO | 2 +- doc/design/gui/GuiDiscussion/index.txt | 2 +- doc/design/gui/index.txt | 4 +++- doc/design/index.txt | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/DIR_INFO b/doc/DIR_INFO index 9d0722d69..ddb558ed3 100644 --- a/doc/DIR_INFO +++ b/doc/DIR_INFO @@ -1,2 +1,2 @@ documentation -source for documentation and on some cases generated documentation in its own subdirs +source for documentation and in some cases generated documentation in its own subdirs diff --git a/doc/design/gui/GuiDiscussion/index.txt b/doc/design/gui/GuiDiscussion/index.txt index aa0bad218..d5f97da23 100644 --- a/doc/design/gui/GuiDiscussion/index.txt +++ b/doc/design/gui/GuiDiscussion/index.txt @@ -10,7 +10,7 @@ GUI concepts and -features and proposals. * link:Proposal.RichardSpindler.html[Richard Spindler] * link:Workspaces.html[about Workspace organsiation] * link:scrolling.html[about scrolling..] - * link:MenuAndShortcuts.html[Ideas for menues and shortcuts] + * link:MenuAndShortcuts.html[Ideas for menus and shortcuts] - link:TimelineDiscussion.html[Conclusions by Joel Holdsworth] [icon="warning.png"] diff --git a/doc/design/gui/index.txt b/doc/design/gui/index.txt index 482679c78..33090d858 100644 --- a/doc/design/gui/index.txt +++ b/doc/design/gui/index.txt @@ -3,7 +3,9 @@ Design Documents: GUI In the early stages of the project, there was a lot of debate regarding GUI concepts and -features and proposals. -link:GuiDiscussion/index.html[here] is a collection of documents from these discussions. + +link:GuiDiscussion/index.html[Here] is a collection of documents from these early +discussions. In our discussions, *Workflow* denotes all considerations how certain tasks can be achieved within the application in the most suitable and stringent fashion. diff --git a/doc/design/index.txt b/doc/design/index.txt index a78d59ac8..189851bd5 100644 --- a/doc/design/index.txt +++ b/doc/design/index.txt @@ -1,6 +1,6 @@ == Lumiera Design Documents -The goal of Lumiera is to provide a professionnal tool for video editing on GNU/Linux systems. +The goal of Lumiera is to provide a professional tool for video editing on GNU/Linux systems. The vision of the development team defines a modern design for the core of a Non-Linear Editing software. One of the key aspect of Lumiera is the strong separation between the user interface and the processing core. Lumiera as a software will come along with a GTK GUI but that does not make this exclusive, any other GUI could be written as well as scripts to drive the core. @@ -20,7 +20,7 @@ They can be categorized into three main categories plus extra components : ==== link:gui/index.html[Graphical User Interface] User interfaces are basically handled like plugins, consequently it is possible to interface with Lumiera through scripts. It is also possible to create specialized GUIs. -The interface is the closest component to the user, it is purely visual. There the user manipulates, organizes, loads, configures all sorts of datas, especially MObjects (media objects) and Assets. These elements are contained within a structure called the Session. +The interface is the closest component to the user, it is purely visual. There the user manipulates, organizes, loads, configures all sorts of data, especially MObjects (media objects) and Assets. These elements are contained within a structure called the Session. ==== The Processing Layer From 87475fa3c14b98b792259f25483954101d1ee76c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 6 Feb 2011 02:39:34 +0100 Subject: [PATCH 126/140] Fix usage of field named "new" in configitem.h This prevents including config.h into C++ code --- src/common/config.h | 6 +----- src/common/configentry.c | 2 +- src/common/configfacade.cpp | 15 ++------------- src/common/configitem.c | 4 ++-- src/common/configitem.h | 2 +- src/include/configfacade.hpp | 16 ++++++++-------- 6 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/common/config.h b/src/common/config.h index 4fa462f25..be8b47171 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -94,11 +94,8 @@ typedef lumiera_config* LumieraConfig; LUMIERA_CONFIG_TYPE(bool, int) -// * does only initialize the variables, so that they get valid values, but does not allocate them as they will be allocated before as they are singleton. -// * lumiera_config_init (const char* searchpath) searchpath is a builtin-default, can be changed via configure and can be appended and overridden by using a flag, e.g. {{{ --config-path-append="" }}} or {{{ --config-path="" }}} - /** - * Initialize the configuration subsystem. + * Initialise the configuration subsystem. * @param path search path for config files. * Must be called only once */ @@ -106,7 +103,6 @@ int lumiera_config_init (const char* path); -// * frees all space allocated by the ConfigLoader. /** * Destroys the configuration subsystem. diff --git a/src/common/configentry.c b/src/common/configentry.c index 495690c04..ef02f5149 100644 --- a/src/common/configentry.c +++ b/src/common/configentry.c @@ -59,7 +59,7 @@ lumiera_configentry_destroy (LumieraConfigitem self) struct lumiera_configitem_vtable lumiera_configentry_funcs = { - .new = lumiera_configentry_new, + .newitem = lumiera_configentry_new, .destroy = lumiera_configentry_destroy }; diff --git a/src/common/configfacade.cpp b/src/common/configfacade.cpp index 08aa04903..bb91d84a8 100644 --- a/src/common/configfacade.cpp +++ b/src/common/configfacade.cpp @@ -25,19 +25,8 @@ #include "include/lifecycle.h" #include "include/configfacade.hpp" -extern "C" { // TODO: can't include "lumiera/config.h" from C++ code, because it uses an identifier "new" - - /** Initialise the configuration subsystem. - * Must be called exactly once prior to any use - * @param path search path for config files. - */ - int lumiera_config_init (const char* path); - - /** Destroys the configuration subsystem. - * Subsequent calls are no-op. */ - void - lumiera_config_destroy (); - +extern "C" { + #include "common/config.h" } #ifndef LUMIERA_CONFIG_PATH diff --git a/src/common/configitem.c b/src/common/configitem.c index e2a22c98c..1a07ee820 100644 --- a/src/common/configitem.c +++ b/src/common/configitem.c @@ -109,8 +109,8 @@ lumiera_configitem_new (const char* line) lumiera_configitem_parse (&tmp, line); - LumieraConfigitem self = tmp.vtable && tmp.vtable->new - ? tmp.vtable->new (&tmp) + LumieraConfigitem self = tmp.vtable && tmp.vtable->newitem + ? tmp.vtable->newitem (&tmp) : lumiera_configitem_move (lumiera_malloc (sizeof (*self)), &tmp); return self; diff --git a/src/common/configitem.h b/src/common/configitem.h index 3c3823422..49ae8b62d 100644 --- a/src/common/configitem.h +++ b/src/common/configitem.h @@ -90,7 +90,7 @@ struct lumiera_configitem_vtable; struct lumiera_configitem_vtable { - LumieraConfigitem (*new)(LumieraConfigitem); + LumieraConfigitem (*newitem)(LumieraConfigitem); LumieraConfigitem (*destroy)(LumieraConfigitem); }; diff --git a/src/include/configfacade.hpp b/src/include/configfacade.hpp index 90a2528b2..850cc74d8 100644 --- a/src/include/configfacade.hpp +++ b/src/include/configfacade.hpp @@ -48,23 +48,23 @@ namespace lumiera { /********************************************************************* * C++ wrapper for convenient access to the Lumiera config system. + * + * @warning Config system not fully implemented yet. Thus for now + * this facade is wired with the setup.ini and will just + * fetch the values from there. */ - struct Config + class Config { - + public: static const string get (string const& key); - static lib::Singleton instance; + private: Config(); - ~Config(); - + ~Config(); friend class lib::singleton::StaticCreate; - - //////////////////TODO: define startup/shutdown and loading of the config interface - }; From 88678209bc51a6cfebd342fd91941ae419e5d443 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 6 Feb 2011 05:06:16 +0100 Subject: [PATCH 127/140] use setup.ini to retrieve the modulepath and configpath connect config-facade with the new BasicSetup implementation to fetch values from setup.ini, instead of the (not implemented) Config-system. Hook this new lookup mechanism into the plugin loader to retrieve the search path from there --- data/config/setup.ini | 2 + src/common/appstate.cpp | 12 ++- src/common/appstate.hpp | 4 + src/common/basic-setup.cpp | 16 ++-- src/common/basic-setup.hpp | 18 ++++- src/common/configfacade.cpp | 80 ++++++++++++++++--- src/common/plugin.c | 34 ++++---- .../{configfacade.hpp => config-facade.h} | 29 ++++++- src/lib/lifecycle.cpp | 1 - tests/31plugin.tests | 6 +- tests/40components.tests | 2 +- ...{appconfigtest.cpp => app-config-test.cpp} | 49 ++++++------ 12 files changed, 183 insertions(+), 70 deletions(-) rename src/include/{configfacade.hpp => config-facade.h} (74%) rename tests/lib/{appconfigtest.cpp => app-config-test.cpp} (64%) diff --git a/data/config/setup.ini b/data/config/setup.ini index c1da6425b..60c92ed0d 100644 --- a/data/config/setup.ini +++ b/data/config/setup.ini @@ -10,3 +10,5 @@ # gui = gtk_gui.lum modulepath = $ORIGIN/modules +configpath = /usr/share/lumiera/config:~/.lumiera +version = 0.pre.01 diff --git a/src/common/appstate.cpp b/src/common/appstate.cpp index c47dea849..c0036fdf9 100644 --- a/src/common/appstate.cpp +++ b/src/common/appstate.cpp @@ -36,9 +36,10 @@ extern "C" { #include "lib/symbol.hpp" #include "lib/util.hpp" -#include "include/configfacade.hpp" //////////TODO: temp hack to force configfacade.o to be linked in + using util::cStr; +using lib::Literal; #define LOCATION_OF_BOOTSTRAP_INI "$ORIGIN/config/setup.ini" @@ -98,6 +99,13 @@ namespace lumiera { + string + AppState::fetchSetupValue (Literal key) + { + return setup_.get(key).as(); + } + + @@ -191,8 +199,6 @@ namespace lumiera { AppState::abort (lumiera::Error& problem) { - INFO (common, "Address of Config Facade = %p", &lumiera::Config::instance()); //////////TODO: a temp hack to force configfacade.cpp to be linked into lumiera exe. - ERROR (common, "Aborting Lumiera after unhandled error: %s", cStr(problem.what())); log_and_clear_unexpected_errorstate(); diff --git a/src/common/appstate.hpp b/src/common/appstate.hpp index 0f2a4ee14..8d44f09cd 100644 --- a/src/common/appstate.hpp +++ b/src/common/appstate.hpp @@ -90,6 +90,10 @@ namespace lumiera { void init (lumiera::Option& options); + /** access basic application setup values (from \c setup.ini) */ + string fetchSetupValue (lib::Literal key); + + /** building on the state determined by #evaluate, decide if the given Subsys * needs to be pulled up and, if necessary, register the Subsys and its * prerequisites to be maintained throughout the application's lifetime. */ diff --git a/src/common/basic-setup.cpp b/src/common/basic-setup.cpp index 1492695c3..5abeee2c0 100644 --- a/src/common/basic-setup.cpp +++ b/src/common/basic-setup.cpp @@ -74,11 +74,17 @@ namespace lumiera { , settings() { syntax.add_options() - ("BuildsystemDemo.gui", opt::value(), - "name of the Lumiera GUI plugin to load") - ("BuildsystemDemo.modulepath", opt::value(), - "search path for loadable modules. " - "May us $ORIGIN to refer to the EXE location") + ("Lumiera.gui", opt::value(), + "name of the Lumiera GUI plugin to load") + ("Lumiera.modulepath", opt::value(), + "search path for loadable modules. " + "May us $ORIGIN to refer to the EXE location") + ("Lumiera.configpath", opt::value(), + "search path for extended configuration. " + "Extended Config system not yet implemented " + "Ignored as of 2/2011") + ("Lumiera.version", opt::value(), + "Application version string") ; ifstream configIn (resolve(bootstrapIni).c_str()); diff --git a/src/common/basic-setup.hpp b/src/common/basic-setup.hpp index ec1759e3d..d35f25d38 100644 --- a/src/common/basic-setup.hpp +++ b/src/common/basic-setup.hpp @@ -25,6 +25,8 @@ #define COMMON_BASIC_SETUP_H #include "lib/error.hpp" +#include "lib/symbol.hpp" +#include "lib/util.hpp" #include #include @@ -68,19 +70,27 @@ namespace lumiera { BasicSetup (string bootstrapIni); string - operator[] (string const& key) const + operator[] (lib::Literal key) const { - return settings[key].as(); + return get (key).as(); } opt::variable_value const& - get (string const& key) + get (lib::Literal key) const { - return settings[key]; + string keyID (key); + __ensure_hasKey(keyID); + return settings[keyID]; } private: + void + __ensure_hasKey (string const& key) const + { + if (!util::contains (settings, key)) + throw error::Logic ("Key \""+key+"\" not found in setup.ini"); + } }; diff --git a/src/common/configfacade.cpp b/src/common/configfacade.cpp index bb91d84a8..f17d9163d 100644 --- a/src/common/configfacade.cpp +++ b/src/common/configfacade.cpp @@ -23,20 +23,39 @@ #include "include/logging.h" #include "include/lifecycle.h" -#include "include/configfacade.hpp" +#include "include/config-facade.h" +#include "common/appstate.hpp" +#include "lib/searchpath.hpp" +#include "lib/util.hpp" extern "C" { #include "common/config.h" } -#ifndef LUMIERA_CONFIG_PATH -#error LUMIERA_CONFIG_PATH not defined -#endif + +/** key to fetch the search path for extended configuration. + * Will corresponding value is defined in the basic setup.ini + * and will be fed to the (planned) full-blown config system + * after the basic application bootstrap was successful. + */ +#define KEY_CONFIG_PATH "Lumiera.configpath" + +/** Similarly, this key is used to fetch the configured default + * plugin/module search path from the basic setup.ini + * This patch is used by the plugin-loader to discover + * lumiera plugins and extensions. + */ +#define KEY_PLUGIN_PATH "Lumiera.modulepath" + namespace lumiera { + using util::cStr; + using util::isnil; + using lib::Literal; + namespace { @@ -47,7 +66,7 @@ namespace lumiera { Config::instance(); } - LifecycleHook trigger__ (ON_BASIC_INIT, &pull_up_ConfigSystem); + LifecycleHook trigger__ (ON_BASIC_INIT, &pull_up_ConfigSystem); } @@ -59,28 +78,63 @@ namespace lumiera { Config::Config () { - lumiera_config_init (LUMIERA_CONFIG_PATH); + string extendedConfigSearchPath = AppState::instance().fetchSetupValue (KEY_CONFIG_PATH); + lumiera_config_init (cStr(extendedConfigSearchPath)); INFO (config, "Config system ready."); - - TODO ("wire Config facade to config interface"); } Config::~Config() { lumiera_config_destroy(); - TRACE (common, "config system closed."); + TRACE (config, "config system closed."); } - const string - Config::get (string const& key) + /** @note because the full-blown Config system isn't implemented yet + * we retrieve the contents of setup.ini as a preliminary solution + */ + string + Config::get (lib::Literal key) { - UNIMPLEMENTED ("config facade access to config value"); - return string("warwohlnix"); + string value = AppState::instance().fetchSetupValue (key); + if (isnil (value)) + throw error::Config ("Configuration value for key=\""+key+"\" is missing"); + + return value; } } // namespace lumiera + + +extern "C" { /* ==== implementation C interface for accessing setup.ini ======= */ + + + using std::string; + using lumiera::Config; + using lib::SearchPathSplitter; + using util::isnil; + using util::cStr; + + + + const char* + lumiera_get_plugin_path_default () + { + static string pathSpec; + if (isnil (pathSpec)) + { + pathSpec += "plugin.path="; // syntax expected by lumiera_config_setdefault + + // fetch plugin search path from setup.ini and expand any $ORIGIN token + SearchPathSplitter pathElement(Config::get (KEY_PLUGIN_PATH)); + while (pathElement) + pathSpec += pathElement.fetch() +":"; + } + + return cStr(pathSpec); + } +} diff --git a/src/common/plugin.c b/src/common/plugin.c index 2638af5d1..373e405e7 100644 --- a/src/common/plugin.c +++ b/src/common/plugin.c @@ -19,6 +19,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/** @file plugin.c + ** Plugin loader implementation. + */ + + + #include "include/logging.h" #include "lib/safeclib.h" #include "lib/tmpbuf.h" @@ -26,6 +33,7 @@ #include "lib/recmutex.h" #include "lib/error.h" +#include "include/config-facade.h" #include "common/interfaceregistry.h" #include "common/config.h" #include "common/plugin.h" @@ -34,23 +42,15 @@ #include -#ifndef LUMIERA_PLUGIN_PATH -#error please define the plugin search path as -DLUMIERA_PLUGIN_PATH, e.g. as $INSTALL_PREFIX/lib/lumiera -#endif -/** - * @file - * Plugin loader. - */ -/* just some declarations */ extern PSplay lumiera_pluginregistry; static char* init_exts_globs (void); -/* TODO default plugin path should be set by the build system */ + /* errors */ -LUMIERA_ERROR_DEFINE(PLUGIN_INIT, "Initialization error"); +LUMIERA_ERROR_DEFINE(PLUGIN_INIT, "Initialisation error"); LUMIERA_ERROR_DEFINE(PLUGIN_OPEN, "Could not open plugin"); LUMIERA_ERROR_DEFINE(PLUGIN_WTF, "Not a Lumiera plugin"); LUMIERA_ERROR_DEFINE(PLUGIN_REGISTER, "Could not register plugin"); @@ -61,7 +61,7 @@ LUMIERA_ERROR_DEFINE(PLUGIN_VERSION, "Plugin Version unsupported"); /** * Supported (and planned) plugin types and their file extensions * This maps filename extensions to implementations (of the respective _load_NAME and _unload_NAME functions) - * So far we only support platform dynamic libraries, later we may add plugins implemented in lua + * So far we only support platform dynamic libraries, later we may add plugins implemented in Lua * and c source modules which get compiled on the fly. */ #define LUMIERA_PLUGIN_TYPES \ @@ -115,7 +115,8 @@ struct lumiera_plugin_struct /* time when the refcounter dropped to 0 last time */ time_t last; - /* when loading plugins en masse we do not want to fail completely if one doesnt cooperate, instead we record local errors here */ + /** bulk loading plugins must not fail entirely, just because one + * plugin doesn't comply. Thus we're recording local errors here */ lumiera_err error; /* the 'plugin' interface itself */ @@ -198,9 +199,12 @@ lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), REQUIRE (callback_load); REQUIRE (callback_register); - lumiera_config_setdefault ("plugin.path ="LUMIERA_PLUGIN_PATH); + // Note: because the full-blown Config system isn't implemented yet, + // as a temporary solution we fetch this basic configuration + // from the setup.ini used to bootstrap the application + lumiera_config_setdefault (lumiera_get_plugin_path_default()); - /* construct glob trail {.so,.c,.foo} ... */ + /* construct glob trail {.so,.lum,.lua} ... */ static char* exts_globs = NULL; if (!exts_globs) exts_globs = init_exts_globs (); @@ -375,7 +379,7 @@ static char* init_exts_globs (void) ++itr; } exts_globs[exts_sz-2] = '}'; - TRACE (pluginloader_dbg, "initialized extension glob to '%s'", exts_globs); + TRACE (pluginloader_dbg, "initialised extension glob to '%s'", exts_globs); return exts_globs; } diff --git a/src/include/configfacade.hpp b/src/include/config-facade.h similarity index 74% rename from src/include/configfacade.hpp rename to src/include/config-facade.h index 850cc74d8..564c49533 100644 --- a/src/include/configfacade.hpp +++ b/src/include/config-facade.h @@ -20,11 +20,14 @@ */ -/** @file configfacade.hpp +/** @file configfacade.h ** The lumiera::Config wrapper class addresses two issues. ** First, it registers startup and shutdown hooks to bring up the config system ** as early as possible. Later, on application main initialisation, the global ** config interface is opened and wrapped for convenient access from C++ code. + ** + ** @todo there ought to be an external Interface for the Config subsystem. + ** But the full-blown Config system isn't implemented yet anyway ** ** @see config.h ** @see lumiera::AppState @@ -36,7 +39,13 @@ #define INTERFACE_CONFIGFACADE_H + + + +#ifdef __cplusplus /* ============== C++ Interface ================= */ + #include "lib/singleton.hpp" +#include "lib/symbol.hpp" #include @@ -56,7 +65,7 @@ namespace lumiera { class Config { public: - static const string get (string const& key); + static string get (lib::Literal key); static lib::Singleton instance; @@ -70,4 +79,20 @@ namespace lumiera { } // namespace lumiera + + + +#else /* =========================== C Interface ====================== */ + + +/** retrieve the default plugin search path from the + * basic applications setup.ini + * @return a fully expanded string suitable to be fed + * to the lumiera_config_setdefault function + * @see lumiera_plugin_discover + */ +const char* lumiera_get_plugin_path_default (); + + +#endif #endif diff --git a/src/lib/lifecycle.cpp b/src/lib/lifecycle.cpp index 2e23081ec..1894ae2fb 100644 --- a/src/lib/lifecycle.cpp +++ b/src/lib/lifecycle.cpp @@ -98,4 +98,3 @@ extern "C" { /* ==== implementation C interface for lifecycle hooks ======= */ } } - diff --git a/tests/31plugin.tests b/tests/31plugin.tests index 6181c224b..e81b1c5a9 100644 --- a/tests/31plugin.tests +++ b/tests/31plugin.tests @@ -6,11 +6,11 @@ out: found plugin: \(null\) return: 0 END -export LUMIERA_PLUGIN_PATH=.libs +export LUMIERA_PLUGIN_PATH=modules export LUMIERA_CONFIG_PATH=./ TEST "discovering plugins" plugin_discover < Date: Sun, 6 Feb 2011 15:12:13 +0100 Subject: [PATCH 128/140] Fix build: the LUMIERA_PLUGIN didn't get through to the object compilation --- SConstruct | 4 ++-- admin/scons/Buildhelper.py | 5 +++-- admin/scons/LumieraEnvironment.py | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/SConstruct b/SConstruct index aa382f2c8..717609d76 100644 --- a/SConstruct +++ b/SConstruct @@ -90,7 +90,7 @@ def setupBasicEnvironment(localDefinitions): env.Append ( CCCOM=' -std=gnu99') env.Append ( SHCCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honour CFLAGS, only CCFLAGS env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root - , CPPDEFINES=['-DLUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines + , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines , CCFLAGS='-Wall -Wextra ' , CFLAGS='-std=gnu99' ) @@ -367,7 +367,7 @@ def defineBuildTargets(env, artifacts): envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm']) envGtk.Append(LIBS=core) - objgui = srcSubtree(envGtk,'src/gui') + objgui = srcSubtree(envGtk,'src/gui', appendCPP='LUMIERA_PLUGIN') guimodule = envGtk.LumieraPlugin('gtk_gui', objgui, install=True) artifacts['gui'] = ( guimodule + [env.GuiResource(f) for f in env.Glob('src/gui/*.rc')] diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index aed9c07da..69fb3849d 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -49,11 +49,12 @@ def isHelpRequest(): -def srcSubtree(env,tree,isShared=True,builder=None, **args): +def srcSubtree(env,tree,isShared=True,builder=None,appendCPP=None, **args): """ convenience wrapper: scans the given subtree, which is relative to the current SConscript, find all source files and declare them as Static or SharedObjects for compilation """ + if appendCPP: env.Append(CPPDEFINES=appendCPP) root = env.subst(tree) # expand Construction Vars if not builder: if isShared: @@ -160,7 +161,7 @@ def createPlugins(env, dir, **kw): @return: a list of build nodes defining a plugin for each of these source trees. """ return [env.LumieraPlugin( getDirname(tree) - , srcSubtree(env, tree) + , srcSubtree(env, tree, appendCPP='LUMIERA_PLUGIN') , **kw ) for tree in findSrcTrees(dir) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 82a89b6ae..28b1eb48f 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -185,6 +185,8 @@ class WrappedStandardExeBuilder(SCons.Util.Proxy): def __init__(self, originalBuilder): SCons.Util.Proxy.__init__ (self, originalBuilder) + def __nonzero__(self): return True + def __call__(self, env, target=None, source=None, **kw): """ when the builder gets invoked from the SConscript... create a clone environment for specific configuration From 29e67e828da88090bca911e517b314875bf13784 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 6 Feb 2011 19:57:26 +0100 Subject: [PATCH 129/140] remove NoBug resourcetracker and switch back to plain pthreads due to desing shortcomings, the resourcetracker produces lots of false positives and inhibits any further diagnostics regarding GUI startup. --- src/lib/sync.cpp | 72 ++++++++++++++++ src/lib/sync.hpp | 217 ++++++++++++++++------------------------------- 2 files changed, 147 insertions(+), 142 deletions(-) create mode 100644 src/lib/sync.cpp diff --git a/src/lib/sync.cpp b/src/lib/sync.cpp new file mode 100644 index 000000000..902652de5 --- /dev/null +++ b/src/lib/sync.cpp @@ -0,0 +1,72 @@ +/* + Sync - generic helper for object based locking and synchronisation + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + 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 sync.cpp + ** This compilation unit holds the static attribute struct + ** for initialising pthread's recursive mutex. + ** + */ + + +#include "lib/sync.hpp" + + +namespace lib { +namespace sync { + + + namespace { // private pthread attributes + + pthread_mutexattr_t attribute_; + pthread_once_t is_init_ = PTHREAD_ONCE_INIT; + + void + initAttribute() + { + pthread_mutexattr_init (&attribute_); + pthread_mutexattr_settype (&attribute_, PTHREAD_MUTEX_RECURSIVE); + } + + + inline pthread_mutexattr_t* + recursive_flag() + { + pthread_once (&is_init_, initAttribute); + return &attribute_; + } + } + + + + /** + * @internal creating a recursive mutex. + * Defined here in a separate compilation unit, + * so it can refer to a single mutex attribute flag. + */ + Wrapped_RecursiveMutex::Wrapped_RecursiveMutex() + { + pthread_mutex_init (&mutex_, recursive_flag()); + } + + + +}}// lib::sync diff --git a/src/lib/sync.hpp b/src/lib/sync.hpp index c6ceafc40..650c54bc6 100644 --- a/src/lib/sync.hpp +++ b/src/lib/sync.hpp @@ -23,10 +23,11 @@ /** @file sync.hpp ** Object Monitor based synchronisation. - ** Actually, everything is implemented by delegating the raw locking/sync calls - ** to the Lumiera backend. The purpose is to support and automate the most common - ** use cases in a way which fits better with scoping and encapsulation; especially - ** we build upon the monitor object pattern. + ** The actual locking, signalling and waiting is implemented by delegating to the + ** raw pthreads locking/sync calls. Rather, the purpose of the Sync baseclass is + ** to support locking based on the object monitor pattern. This pattern + ** describes a way of dealing with synchronisation known to play well with + ** scoping, encapsulation and responsibility for a single purpose. ** ** A class becomes \em lockable by inheriting from lib::Sync with the appropriate ** parametrisation. This causes any instance to inherit a monitor member (object), @@ -68,21 +69,15 @@ #include "lib/error.hpp" #include "lib/util.hpp" -#include "lib/diagnostic-context.hpp" extern "C" { -#include "lib/mutex.h" -#include "lib/recmutex.h" -#include "lib/condition.h" -#include "lib/reccondition.h" +#include "lib/lockerror.h" } -#include #include #include #include -using boost::noncopyable; namespace lib { @@ -91,177 +86,116 @@ namespace lib { namespace sync { - namespace { // implementation shortcuts... - - inline DiagnosticContext& - _accessContext() - { - return DiagnosticContext::access(); - } - } + /* ========== adaptation layer for accessing backend/system level code ============== */ - /* ========== adaptation layer for accessing the backend code ============== */ - - struct Wrapped_LumieraExcMutex + struct Wrapped_ExclusiveMutex { - lumiera_mutex self_; + pthread_mutex_t mutex_; protected: - Wrapped_LumieraExcMutex() - { //TODO: currently passing no lineno/function info, put planning to use the DiagnosticContext to provide something in place of that later... - lumiera_mutex_init (&self_, "Obj.Monitor ExclMutex", &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); - } - ~Wrapped_LumieraExcMutex() + Wrapped_ExclusiveMutex() { - lumiera_mutex_destroy (&self_, &NOBUG_FLAG(sync), NOBUG_CONTEXT); + pthread_mutex_init (&mutex_, NULL); } + ~Wrapped_ExclusiveMutex() + { + if (pthread_mutex_destroy (&mutex_)) + ERROR (sync, "Failure destroying mutex."); + } // shouldn't happen in a correct program - bool + void lock() { - return !!lumiera_mutex_lock (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } + if (pthread_mutex_lock (&mutex_)) + throw lumiera::error::Fatal ("Mutex acquire failed"); + } // shouldn't happen in a correct program void unlock() { - lumiera_mutex_unlock (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } + if (pthread_mutex_unlock (&mutex_)) + ERROR (sync, "Failure unlocking mutex."); + } // shouldn't happen in a correct program }; - struct Wrapped_LumieraRecMutex + + struct Wrapped_RecursiveMutex { - lumiera_recmutex self_; + pthread_mutex_t mutex_; protected: - Wrapped_LumieraRecMutex() + Wrapped_RecursiveMutex(); + ~Wrapped_RecursiveMutex() { - lumiera_recmutex_init (&self_, "Obj.Monitor RecMutex", &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); - } - ~Wrapped_LumieraRecMutex() - { - lumiera_recmutex_destroy (&self_, &NOBUG_FLAG(sync), NOBUG_CONTEXT); - } + if (pthread_mutex_destroy (&mutex_)) + ERROR (sync, "Failure destroying (rec)mutex."); + } // shouldn't happen in a correct program - bool + void lock() { - return !!lumiera_recmutex_lock (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } + if (pthread_mutex_lock (&mutex_)) + throw lumiera::error::Fatal ("(rec)Mutex acquire failed"); + } // shouldn't happen in a correct program void unlock() { - lumiera_recmutex_unlock (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } + if (pthread_mutex_unlock (&mutex_)) + ERROR (sync, "Failure unlocking (rec)mutex."); + } // shouldn't happen in a correct program }; - struct Wrapped_LumieraExcCond + template + struct Wrapped_Condition + : MTX { - lumiera_condition self_; + pthread_cond_t cond_; protected: - Wrapped_LumieraExcCond() + Wrapped_Condition() { - lumiera_condition_init (&self_, "Obj.Monitor ExclCondition", &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); + pthread_cond_init (&cond_, NULL); } - ~Wrapped_LumieraExcCond() + ~Wrapped_Condition() { - lumiera_condition_destroy (&self_, &NOBUG_FLAG(sync), NOBUG_CONTEXT); - } - - bool - lock() - { - return !!lumiera_condition_lock (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } - - void - unlock() - { - lumiera_condition_unlock (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } + if (pthread_cond_destroy (&cond_)) + ERROR (sync, "Failure destroying condition variable."); + } // shouldn't happen in a correct program bool wait() { - return !!lumiera_condition_wait (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); + int err; + do { err = pthread_cond_wait (&this->cond_, &this->mutex_); + } while(err == EINTR); + + if (err) lumiera_lockerror_set (err, &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); + return !err; } bool timedwait (const struct timespec* timeout) { - return !!lumiera_condition_timedwait (&self_, timeout, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); + int err; + do { err = pthread_cond_timedwait (&this->cond_, &this->mutex_, timeout); + } while(err == EINTR); + + if (err) lumiera_lockerror_set (err, &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); + return !err; } - void - signal() - { - lumiera_condition_signal (&self_, &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); - } - void - broadcast() - { - lumiera_condition_broadcast (&self_, &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); - } + void signal() { pthread_cond_signal (&cond_); } + void broadcast() { pthread_cond_broadcast (&cond_); } }; - struct Wrapped_LumieraRecCond - { - lumiera_reccondition self_; - - protected: - Wrapped_LumieraRecCond() - { - lumiera_reccondition_init (&self_, "Obj.Monitor RecCondition", &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); - } - ~Wrapped_LumieraRecCond() - { - lumiera_reccondition_destroy (&self_, &NOBUG_FLAG(sync), NOBUG_CONTEXT); - } - - bool - lock() - { - return !!lumiera_reccondition_lock (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } - - void - unlock () - { - lumiera_reccondition_unlock (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } - - bool - wait() - { - return !!lumiera_reccondition_wait (&self_, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } - - bool - timedwait (const struct timespec* timeout) - { - return !!lumiera_reccondition_timedwait (&self_, timeout, &NOBUG_FLAG(sync), _accessContext(), NOBUG_CONTEXT_NOFUNC); - } - - void - signal() - { - lumiera_reccondition_signal (&self_, &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); - } - - void - broadcast() - { - lumiera_reccondition_broadcast (&self_, &NOBUG_FLAG(sync), NOBUG_CONTEXT_NOFUNC); - } - }; + @@ -296,20 +230,20 @@ namespace lib { class Timeout; - template + template class Condition - : public Mutex + : public Mutex > { - typedef Mutex _PM; + typedef Wrapped_Condition Cond; public: void signal (bool wakeAll=false) { if (wakeAll) - CDX::broadcast (); + Cond::broadcast (); else - CDX::signal (); + Cond::signal (); } @@ -320,12 +254,12 @@ namespace lib { bool ok = true; while (ok && !predicate()) if (waitEndTime) - ok = CDX::timedwait (&waitEndTime); + ok = Cond::timedwait (&waitEndTime); else - ok = CDX::wait (); + ok = Cond::wait (); if (!ok && lumiera_error_expect(LUMIERA_ERROR_LOCK_TIMEOUT)) return false; - lumiera::throwOnError(); // any other error thows + lumiera::throwOnError(); // any other error throws return true; } @@ -435,10 +369,10 @@ namespace lib { bool isTimedWait() {return (timeout_);} }; - typedef Mutex NonrecursiveLock_NoWait; - typedef Mutex RecursiveLock_NoWait; - typedef Condition NonrecursiveLock_Waitable; - typedef Condition RecursiveLock_Waitable; + typedef Mutex NonrecursiveLock_NoWait; + typedef Mutex RecursiveLock_NoWait; + typedef Condition NonrecursiveLock_Waitable; + typedef Condition RecursiveLock_Waitable; } // namespace sync (helpers and building blocks) @@ -488,7 +422,6 @@ namespace lib { public: class Lock - : DiagnosticContext { Monitor& mon_; From 421a81b25b5fe12ed03870da3f5aff4aee60546a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 6 Feb 2011 21:23:34 +0100 Subject: [PATCH 130/140] Gui: WindowManager and GtkLumiera refactoring - split off the tricky basic GTKmm includes - reduce the clutter in the gtk-lumiera.header - make GtkLumiera a singleton, placed into static memory - remove all static functions from WindowManager --- src/gui/gtk-base.hpp | 104 +++++++++++ src/gui/gtk-lumiera.cpp | 64 +++---- src/gui/gtk-lumiera.hpp | 171 +++++------------- src/gui/guistart.cpp | 3 +- src/gui/model/parent-track.hpp | 8 +- src/gui/panels/timeline-panel.hpp | 7 + src/gui/widgets/timeline/timeline-body.hpp | 2 + .../timeline/timeline-layout-helper.hpp | 2 + src/gui/widgets/timeline/timeline-track.hpp | 17 +- src/gui/window-manager.cpp | 9 +- src/gui/window-manager.hpp | 57 +++--- src/gui/workspace/actions.cpp | 2 +- 12 files changed, 252 insertions(+), 194 deletions(-) create mode 100644 src/gui/gtk-base.hpp diff --git a/src/gui/gtk-base.hpp b/src/gui/gtk-base.hpp new file mode 100644 index 000000000..12c0d5638 --- /dev/null +++ b/src/gui/gtk-base.hpp @@ -0,0 +1,104 @@ +/* + GTK-BASE.hpp - GTK includes and basic definitions + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + 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 gtk-base.hpp + ** A set of basic GTK includes. + ** There are some tricky point to consider when including the + ** basic GTKmm headers. Especially, GTK tries to shadow the ERROR macro + ** from Microsoft Windows. Unfortunately this breaks the ERROR macro from NoBug; + ** thus we need to include NoBug \em after GTK + ** + ** Besides, this header defines the basic NLS. Most parts of the GUI rely either + ** directly on this header, or through the inclusion of gtk-lumiera.hpp + ** + ** @see GtkLumiera + */ + + +#ifndef GUI_GTK_BASE_H +#define GUI_GTK_BASE_H + +//--------------------tricky special Include sequence +#include +#include +#include +//--------------------tricky special Include sequence + +#include "lib/error.hpp" +#include "lib/util.hpp" +#include "lib/lumitime.hpp" + + +#ifdef ENABLE_NLS +# include +# define _(String) gettext (String) +# define gettext_noop(String) String +# define N_(String) gettext_noop (String) +#else +# define _(String) (String) +# define N_(String) String +# define textdomain(Domain) +# define bindtextdomain(Package, Directory) +#endif + + + + +/* ======= Namespace Definitions ======= */ + +/** Lumiera GTK GUI implementation root. */ +namespace gui { + + /** Dialog box classes. */ + namespace dialogs {} + + /** The Lumiera GTK-GUI uses a thin proxy layer data model + * on top of the actual "high-level-model", which lives in the + * Proc-Layer below. GUI operations interact with these proxy model + * entities, which in turn forward the calls to the actual objects + * in the Proc-Layer, through the Command system (which enables UNDO). + * + * @todo: as of 1/2011 this connection between the GUI proxy model and + * the Proc-Layer model needs to be set up. Currently, the GUI model + * entities are just created standalone and thus dysfunctional. + */ + namespace model {} + + /** The namespace of all video output implementations. */ + namespace output {} + + /** Docking panel classes. */ + namespace panels {} + + /** Lumiera custom widgets. */ + namespace widgets {} + + /** The workspace window and it's helper classes. */ + namespace workspace {} + + /** GUI helpers, utility functions and classes. */ + namespace util {} + +}// namespace gui +#endif diff --git a/src/gui/gtk-lumiera.cpp b/src/gui/gtk-lumiera.cpp index 3ec81680f..a67d419d1 100644 --- a/src/gui/gtk-lumiera.cpp +++ b/src/gui/gtk-lumiera.cpp @@ -1,5 +1,5 @@ /* - gtk-lumiera.cpp - The entry point for the GTK GUI application + GtkLumiera - The Lumiera GUI Application Object Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -20,24 +20,14 @@ * *****************************************************/ -#include -#include -#ifdef ENABLE_NLS -# include -#endif +#include "gui/gtk-lumiera.hpp" +#include "gui/window-manager.hpp" +#include "gui/workspace/workspace-window.hpp" +#include "gui/controller/controller.hpp" +#include "gui/model/project.hpp" +#include "lib/singleton.hpp" -#include "gtk-lumiera.hpp" -#include "window-manager.hpp" -#include "workspace/workspace-window.hpp" -#include "model/project.hpp" -#include "controller/controller.hpp" - -extern "C" { -#include "common/interface.h" -} - -NOBUG_CPP_DEFINE_FLAG(gui); using namespace Gtk; using namespace Glib; @@ -47,36 +37,47 @@ using namespace gui::model; using namespace gui::controller; using namespace std; -GtkLumiera the_application; - - - - namespace gui { +namespace { + /** storage for the Main Application object */ + lib::Singleton theApplicationInstance; +} + + + + +GtkLumiera& +GtkLumiera::application() +{ + return theApplicationInstance(); +} + + + void -GtkLumiera::main(int argc, char *argv[]) +GtkLumiera::main (int argc, char *argv[]) { Glib::thread_init(); Main kit(argc, argv); - Glib::set_application_name(get_app_title()); + Glib::set_application_name (get_app_title()); Project project; Controller controller(project); - windowManager.init(); - windowManager.set_theme("lumiera_ui.rc"); - windowManager.new_window(project, controller); + windowManagerInstance_.init(); + windowManagerInstance_.set_theme ("lumiera_ui.rc"); + windowManagerInstance_.new_window (project, controller); kit.run(); } WindowManager& -GtkLumiera::get_window_manager() +GtkLumiera::windowManager() { - return windowManager; + return windowManagerInstance_; } Glib::ustring @@ -125,10 +126,5 @@ GtkLumiera::get_app_authors() return list; } -GtkLumiera& -application() -{ - return the_application; -} } // namespace gui diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index de7c509bf..226e1acf3 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -1,5 +1,5 @@ /* - gtk-lumiera.hpp - Application wide global definitions + GTK-LUMIERA.hpp - The Lumiera GUI Application Object Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -25,137 +25,58 @@ ** @see gtk-lumiera.cpp */ -#ifndef GTK_LUMIERA_HPP -#define GTK_LUMIERA_HPP +#ifndef GUI_GTK_LUMIERA_H +#define GUI_GTK_LUMIERA_H -#include -#include -#include // need to include this after gtkmm.h, because types.h from GTK tries to shaddow the ERROR macro from windows, which kills NoBug's ERROR macro + +#include "gui/gtk-base.hpp" +#include "gui/window-manager.hpp" + +#include #include -#include -#include -#include -#include -#include -#include -#include "lib/util.hpp" -#include "lib/lumitime.hpp" -#include "window-manager.hpp" -extern "C" { -#include -} - -//NOBUG_DECLARE_FLAG(gui); - -#ifdef ENABLE_NLS -# include -# define _(String) gettext (String) -# define gettext_noop(String) String -# define N_(String) gettext_noop (String) -#else -# define _(String) (String) -# define N_(String) String -# define textdomain(Domain) -# define bindtextdomain(Package, Directory) -#endif - -/** - * Lumiera GTK GUI implementation root. - */ namespace gui { - -/* ===== The Application Class ===== */ + + +/* ====== The Application Class ====== */ /** * The main application class. */ -class GtkLumiera : private boost::noncopyable -{ -public: - void main(int argc, char *argv[]); - - WindowManager& get_window_manager(); - - static Glib::ustring get_home_data_path(); - - /** - * Returns the name of the application - */ - static const Glib::ustring get_app_title(); - - /** - * Returns the version number of the application - */ - static const Glib::ustring get_app_version(); - - /** - * Returns the copyright of the application - */ - static const Glib::ustring get_app_copyright(); - - /** - * Returns the website of the application - */ - static const Glib::ustring get_app_website(); - - /** - * Returns tn alphabetical list of the application's authors - */ - static const std::vector get_app_authors(); - -protected: - /** - * The application window manager object - */ - WindowManager windowManager; -}; - -/** - * Returns a reference to the global application object - */ -GtkLumiera& application(); - -/* ===== Namespace Definitions ===== */ - -/** - * The namespace of all dialog box classes. - */ -namespace dialogs {} - -/** - * The namespace of data model classes. - */ -namespace model {} - -/** - * The namespace of all video output implementations. - */ -namespace output {} - -/** - * The namespace of all docking panel classes. - */ -namespace panels {} - -/** - * The namespace of all Lumiera custom widgets. - */ -namespace widgets {} - -/** - * The namespace of the workspace window, and it's helper classes. - */ -namespace workspace {} - -/** - * The namespace of utility functions and classes. - */ -namespace util {} - -} // namespace gui - -#endif // GTK_LUMIERA_HPP - +class GtkLumiera + : boost::noncopyable + { + /** Central application window manager instance */ + WindowManager windowManagerInstance_; + + + public: + /** access the the global application object */ + static GtkLumiera& application(); + + + + void main(int argc, char *argv[]); + + WindowManager& windowManager(); + + static Glib::ustring get_home_data_path(); + + + /** the name of the application */ + static const Glib::ustring get_app_title(); + + static const Glib::ustring get_app_version(); + + static const Glib::ustring get_app_copyright(); + + static const Glib::ustring get_app_website(); + + /** alphabetical list of the application's authors */ + static const std::vector get_app_authors(); + + }; +}// namespace gui +#endif diff --git a/src/gui/guistart.cpp b/src/gui/guistart.cpp index d5f072058..b5b5ee6e9 100644 --- a/src/gui/guistart.cpp +++ b/src/gui/guistart.cpp @@ -110,7 +110,8 @@ namespace gui { int argc =0; char *argv[] = {}; // dummy command line for GTK - gui::application().main(argc, argv); // execute the GTK Event Loop + // execute the GTK Event Loop____________ + GtkLumiera::application().main(argc, argv); if (!lumiera_error_peek()) return; // all went well, normal shutdown diff --git a/src/gui/model/parent-track.hpp b/src/gui/model/parent-track.hpp index 9112c86f7..78451972e 100644 --- a/src/gui/model/parent-track.hpp +++ b/src/gui/model/parent-track.hpp @@ -25,11 +25,15 @@ ** are also track parents. This class wraps proc layer data */ +#ifndef PARENT_TRACK_HPP +#define PARENT_TRACK_HPP + #include "track.hpp" #include "lib/observable-list.hpp" -#ifndef PARENT_TRACK_HPP -#define PARENT_TRACK_HPP +#include +#include + namespace gui { namespace model { diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index aef78f9e3..198368fae 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -19,10 +19,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /** @file timeline-panel.hpp ** This file contains the definition of the timeline panel */ + #ifndef TIMELINE_PANEL_HPP #define TIMELINE_PANEL_HPP @@ -30,8 +33,12 @@ #include "gui/widgets/timecode-widget.hpp" #include "gui/widgets/timeline-widget.hpp" +#include +#include + using namespace gui::widgets; + namespace gui { namespace model { diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 6a4bb0b28..72dc374b2 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -29,6 +29,8 @@ #include "gui/gtk-lumiera.hpp" #include "timeline-tool.hpp" +#include + namespace gui { namespace model { diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index dbd588a34..bc0f1c39d 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -30,6 +30,8 @@ #include "gui/gtk-lumiera.hpp" #include "lib/tree.hpp" +#include + namespace gui { namespace model { diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 21c5be6da..a45bf5ab4 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -19,21 +19,28 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /** @file widgets/timeline/timeline-track.hpp ** This file contains the definition of timeline track object */ + +#ifndef TIMELINE_TRACK_HPP +#define TIMELINE_TRACK_HPP + + #include "gui/gtk-lumiera.hpp" #include "gui/model/track.hpp" #include "gui/widgets/menu-button.hpp" #include "gui/widgets/mini-button.hpp" #include "gui/widgets/button-bar.hpp" -#include "timeline-clip.hpp" -#include "timeline-header-container.hpp" -#include "timeline-header-widget.hpp" +#include "gui/widgets/timeline/timeline-clip.hpp" +#include "gui/widgets/timeline/timeline-header-container.hpp" +#include "gui/widgets/timeline/timeline-header-widget.hpp" + +#include -#ifndef TIMELINE_TRACK_HPP -#define TIMELINE_TRACK_HPP namespace gui { namespace widgets { diff --git a/src/gui/window-manager.cpp b/src/gui/window-manager.cpp index 44c550215..461c9c01c 100644 --- a/src/gui/window-manager.cpp +++ b/src/gui/window-manager.cpp @@ -1,5 +1,5 @@ /* - window-manager.cpp - Defines the global UI Manager class + WindowManager - Global UI Manager class Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -20,9 +20,10 @@ * *****************************************************/ -#include "window-manager.hpp" -#include "include/logging.h" -#include "workspace/workspace-window.hpp" + +#include "gui/window-manager.hpp" +#include "gui/gtk-lumiera.hpp" +#include "gui/workspace/workspace-window.hpp" using namespace Gtk; using namespace Glib; diff --git a/src/gui/window-manager.hpp b/src/gui/window-manager.hpp index 4f23ac6e6..090babdc2 100644 --- a/src/gui/window-manager.hpp +++ b/src/gui/window-manager.hpp @@ -1,5 +1,5 @@ /* - window-manager.hpp - Defines the global UI Manager class + WINDOW-MANAGER.hpp - Global UI Manager class Copyright (C) Lumiera.org 2008, Joel Holdsworth @@ -19,20 +19,31 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /** @file window-manager.hpp - ** This file contains the defintion of global UI Manager class. - ** @see window-manager.cpp + ** Manager for all application windows and resources. + ** This file defines the global UI Manager class. The central WindowManager + ** instance is owned by the GtkLumiera object and initialised in GTK-main. + ** The WindowManager has the ability to create new windows integrated with + ** the application framework, to provide Icons and other resources and + ** to set and access a general UI theme. + ** ** @see gtk-lumiera.hpp */ -#include - -#include "gtk-lumiera.hpp" -//#include "workspace/workspace-window.hpp" #ifndef WINDOW_MANAGER_HPP #define WINDOW_MANAGER_HPP +#include "gui/gtk-base.hpp" + +#include +#include +#include + + + namespace gui { namespace model { @@ -50,11 +61,13 @@ namespace workspace { /** * The centralised manager of all lumiera-gui's windows. */ -class WindowManager : private boost::noncopyable +class WindowManager + : boost::noncopyable { public: /** - * Initializes the window manager object + * Initialise the window manager on application start. + * Register the icon configuration and sizes. */ void init(); @@ -104,17 +117,16 @@ private: * otherwise. */ void update_close_window_in_menus(); - - /** - * Registers the custom icon sizes. - */ - static void register_app_icon_sizes(); + + + /** Registers the custom icon sizes. */ + void register_app_icon_sizes(); /** * Registers application stock items: icons and * labels associated with IDs */ - static void register_stock_items(); + void register_stock_items(); /** * Adds an icon (in different sizes) to the icon factory. @@ -125,7 +137,7 @@ private: * @return Returns true if the icon was successfully loaded, returns * false otherwise. */ - static bool add_stock_icon_set( + bool add_stock_icon_set( const Glib::RefPtr& factory, const Glib::ustring& icon_name, const Glib::ustring& id, @@ -141,7 +153,7 @@ private: * wildcarded. * @return Returns true if the icon was loaded successfully. */ - static bool add_stock_icon(Gtk::IconSet &icon_set, + bool add_stock_icon(Gtk::IconSet &icon_set, const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard); /** @@ -153,7 +165,7 @@ private: * wildcarded. * @return Returns true if the icon was loaded successfully. */ - static bool add_theme_icon_source(Gtk::IconSet &icon_set, + bool add_theme_icon_source(Gtk::IconSet &icon_set, const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard); /** @@ -166,7 +178,7 @@ private: * wildcarded. * @return Returns true if the icon was loaded successfully. */ - static bool add_non_theme_icon_source(Gtk::IconSet &icon_set, + bool add_non_theme_icon_source(Gtk::IconSet &icon_set, const Glib::ustring& base_dir, const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard); @@ -179,7 +191,7 @@ private: * wildcarded. * @return Returns true if the icon was loaded successfully. */ - static bool add_stock_icon_from_path(Glib::ustring path, + bool add_stock_icon_from_path(Glib::ustring path, Gtk::IconSet &icon_set, Gtk::IconSize size, bool wildcard); private: @@ -203,6 +215,7 @@ public: static Gtk::IconSize MenuIconSize; }; -} // namespace gui -#endif // WINDOW_MANAGER_HPP + +}// namespace gui +#endif diff --git a/src/gui/workspace/actions.cpp b/src/gui/workspace/actions.cpp index 4260b40ad..d448c41a4 100644 --- a/src/gui/workspace/actions.cpp +++ b/src/gui/workspace/actions.cpp @@ -334,7 +334,7 @@ Actions::on_menu_track_add() void Actions::on_menu_window_new_window() { - application().get_window_manager().new_window( + GtkLumiera::application().windowManager().new_window( workspaceWindow.get_project(), workspaceWindow.get_controller()); } From 87c70d0b1f10ee23a4241c0f4a85cb4a9a471900 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 7 Feb 2011 00:54:44 +0100 Subject: [PATCH 131/140] Gui: some typedefs for Glib::ustring ...helps to write way shorter signatures --- src/gui/dialogs/name-chooser.cpp | 6 +++--- src/gui/dialogs/name-chooser.hpp | 6 +++--- src/gui/gtk-base.hpp | 4 ++++ src/gui/gtk-lumiera.hpp | 12 +++++------- src/gui/model/project.cpp | 2 +- src/gui/model/project.hpp | 2 +- src/gui/panels/timeline-panel.hpp | 2 +- src/gui/widgets/menu-button.cpp | 2 +- src/gui/widgets/menu-button.hpp | 6 +++--- src/gui/workspace/actions.cpp | 16 ++++++++-------- 10 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/gui/dialogs/name-chooser.cpp b/src/gui/dialogs/name-chooser.cpp index 634f2bd07..4c8a297e1 100644 --- a/src/gui/dialogs/name-chooser.cpp +++ b/src/gui/dialogs/name-chooser.cpp @@ -30,8 +30,8 @@ using namespace Glib; namespace gui { namespace dialogs { -NameChooser::NameChooser(Window &parent, const Glib::ustring title, - const Glib::ustring default_name) : +NameChooser::NameChooser(Window &parent, cuString title, + cuString default_name) : Dialog::Dialog(title, parent, true), caption(_("Name:")) { @@ -60,7 +60,7 @@ NameChooser::NameChooser(Window &parent, const Glib::ustring title, show_all_children(); } -const Glib::ustring NameChooser::get_name() const +cuString NameChooser::get_name() const { return name.get_text(); } diff --git a/src/gui/dialogs/name-chooser.hpp b/src/gui/dialogs/name-chooser.hpp index 8b020c51f..84634591f 100644 --- a/src/gui/dialogs/name-chooser.hpp +++ b/src/gui/dialogs/name-chooser.hpp @@ -46,15 +46,15 @@ public: * @param default_name The name that will be shown by default in the * edit box of the dialog. */ - NameChooser(Gtk::Window &parent, const Glib::ustring title, - const Glib::ustring default_name); + NameChooser(Gtk::Window &parent, cuString title, + cuString default_name); /** * Gets the current name of the chosen in the dialog. * @return Returns the name currently typed into the edit box of the * dialog. */ - const Glib::ustring get_name() const; + cuString get_name() const; private: Gtk::HBox hBox; diff --git a/src/gui/gtk-base.hpp b/src/gui/gtk-base.hpp index 12c0d5638..37f342cc6 100644 --- a/src/gui/gtk-base.hpp +++ b/src/gui/gtk-base.hpp @@ -69,6 +69,10 @@ /** Lumiera GTK GUI implementation root. */ namespace gui { + + typedef Glib::ustring uString; + typedef const uString cuString; + /** Dialog box classes. */ namespace dialogs {} diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index 226e1acf3..9d403ccfa 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -61,20 +61,18 @@ class GtkLumiera WindowManager& windowManager(); - static Glib::ustring get_home_data_path(); - /** the name of the application */ - static const Glib::ustring get_app_title(); + static cuString getAppTitle(); - static const Glib::ustring get_app_version(); + static cuString getAppVersion(); - static const Glib::ustring get_app_copyright(); + static cuString getCopyright(); - static const Glib::ustring get_app_website(); + static cuString getLumieraWebsite(); /** alphabetical list of the application's authors */ - static const std::vector get_app_authors(); + static const std::vector getLumieraAuthors(); }; diff --git a/src/gui/model/project.cpp b/src/gui/model/project.cpp index 92dda0758..3b37020cd 100644 --- a/src/gui/model/project.cpp +++ b/src/gui/model/project.cpp @@ -50,7 +50,7 @@ Project::get_sequences() } void -Project::add_new_sequence(Glib::ustring name) +Project::add_new_sequence(uString name) { shared_ptr sequence(new Sequence()); sequence->set_name(name); diff --git a/src/gui/model/project.hpp b/src/gui/model/project.hpp index 42fb8d611..e6e6bbc9e 100644 --- a/src/gui/model/project.hpp +++ b/src/gui/model/project.hpp @@ -43,7 +43,7 @@ public: lumiera::observable_list< boost::shared_ptr >& get_sequences(); - void add_new_sequence(Glib::ustring name); + void add_new_sequence(uString name); private: diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index 198368fae..435599759 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -152,7 +152,7 @@ private: /** * The column to use as the label for the combo box widget items. */ - Gtk::TreeModelColumn< Glib::ustring > nameColumn; + Gtk::TreeModelColumn< uString > nameColumn; }; private: diff --git a/src/gui/widgets/menu-button.cpp b/src/gui/widgets/menu-button.cpp index a5e7629b6..d7756a17c 100644 --- a/src/gui/widgets/menu-button.cpp +++ b/src/gui/widgets/menu-button.cpp @@ -57,7 +57,7 @@ MenuButton::MenuButton(const StockID& stock_id) : setup_button(); } -MenuButton::MenuButton(const Glib::ustring& label, bool mnemonic) : +MenuButton::MenuButton(cuString& label, bool mnemonic) : ToggleButton(), caption(label, mnemonic), arrow(arrowType, shadowType) diff --git a/src/gui/widgets/menu-button.hpp b/src/gui/widgets/menu-button.hpp index 422a93adb..25d343a4a 100644 --- a/src/gui/widgets/menu-button.hpp +++ b/src/gui/widgets/menu-button.hpp @@ -26,7 +26,7 @@ #ifndef MENU_BUTTON_HPP #define MENU_BUTTON_HPP -#include +#include "gui/gtk-base.hpp" namespace gui { namespace widgets { @@ -43,7 +43,7 @@ public: * @remarks With an empty button, you can Gtk::Button::add() a widget * such as a Gtk::Pixmap or Gtk::Box. If you just wish to add a * Gtk::Label, you may want to use the - * Gtk::MenuButton(const Glib::ustring& label) ctor directly instead. + * Gtk::MenuButton(cuString& label) ctor directly instead. */ MenuButton(); @@ -61,7 +61,7 @@ public: * able to add a widget in this button since it already has a * Gtk::Label in it */ - MenuButton(const Glib::ustring& label, bool mnemonic=false); + MenuButton(cuString& label, bool mnemonic=false); /** * Gets the menu which will be displayed when the button is clicked diff --git a/src/gui/workspace/actions.cpp b/src/gui/workspace/actions.cpp index d448c41a4..90bc89967 100644 --- a/src/gui/workspace/actions.cpp +++ b/src/gui/workspace/actions.cpp @@ -136,7 +136,7 @@ Actions::populate_main_actions(Glib::RefPtr uiManager) uiManager->insert_action_group(actionGroup); //----- Create the UI layout -----// - Glib::ustring ui_info = + uString ui_info = "" " " " " @@ -216,7 +216,7 @@ Actions::populate_show_panel_actions(Glib::RefPtr uiManager) for(int i = 0; i < count; i++) { const gchar *stock_id = PanelManager::get_panel_stock_id(i); - const ustring name = ustring::compose("Panel%1", i); + cuString name = ustring::compose("Panel%1", i); actionGroup->add(Action::create(name, StockID(stock_id)), bind(mem_fun(*this, &Actions::on_menu_show_panel), i)); } @@ -225,7 +225,7 @@ Actions::populate_show_panel_actions(Glib::RefPtr uiManager) for(int i = 0; i < count; i++) { - const ustring name = ustring::compose("Panel%1", i); + cuString name = ustring::compose("Panel%1", i); uiManager->add_ui(uiManager->new_merge_id(), "/MenuBar/WindowMenu/WindowShowPanel", name, name); } @@ -334,7 +334,7 @@ Actions::on_menu_track_add() void Actions::on_menu_window_new_window() { - GtkLumiera::application().windowManager().new_window( + GtkLumiera::application().windowManager().newWindow ( workspaceWindow.get_project(), workspaceWindow.get_controller()); } @@ -361,11 +361,11 @@ Actions::on_menu_help_about() AboutDialog dialog; //dialog.set_program_name(AppTitle); - dialog.set_version(GtkLumiera::get_app_version()); + dialog.set_version(GtkLumiera::getAppVersion()); //dialog.set_version(AppState::get("version")); - dialog.set_copyright(GtkLumiera::get_app_copyright()); - dialog.set_website(GtkLumiera::get_app_website()); - dialog.set_authors(GtkLumiera::get_app_authors()); + dialog.set_copyright(GtkLumiera::getCopyright()); + dialog.set_website(GtkLumiera::getLumieraWebsite()); + dialog.set_authors(GtkLumiera::getLumieraAuthors()); dialog.set_transient_for(workspaceWindow); From 65d28b40184075b46f8e9a1cd3af7b8e14e0d6ea Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 7 Feb 2011 00:54:16 +0100 Subject: [PATCH 132/140] Gui: rework resource loading to make the application fully relocatable --- SConstruct | 2 +- data/config/setup.ini | 8 + src/common/basic-setup.cpp | 16 +- src/common/configfacade.cpp | 2 +- src/gui/gtk-lumiera.cpp | 103 +++++++------ src/gui/gtk-lumiera.hpp | 28 +++- src/gui/window-manager.cpp | 186 +++++++++++++----------- src/gui/window-manager.hpp | 91 ++++++------ src/gui/workspace/actions.cpp | 15 +- src/gui/workspace/workspace-window.cpp | 2 +- src/lib/cmdline.cpp | 8 +- src/lib/searchpath.cpp | 2 +- src/lib/searchpath.hpp | 15 +- src/lib/time.cpp | 3 +- tests/lib/search-path-splitter-test.cpp | 10 +- 15 files changed, 281 insertions(+), 210 deletions(-) diff --git a/SConstruct b/SConstruct index 717609d76..26674fc25 100644 --- a/SConstruct +++ b/SConstruct @@ -42,7 +42,7 @@ srcConf = 'data/config' buildExe = '#$TARGDIR' buildLib = '#$TARGDIR/modules' buildPlug = '#$TARGDIR/modules' -buildIcon = '#$TARGDIR/icons' +buildIcon = '#$TARGDIR/gui/icons' buildUIRes = '#$TARGDIR/' buildConf = '#$TARGDIR/config' installExe = '#$DESTDIR/lib/lumiera' diff --git a/data/config/setup.ini b/data/config/setup.ini index 60c92ed0d..9ea9af1a3 100644 --- a/data/config/setup.ini +++ b/data/config/setup.ini @@ -11,4 +11,12 @@ gui = gtk_gui.lum modulepath = $ORIGIN/modules configpath = /usr/share/lumiera/config:~/.lumiera +title = Lumiera version = 0.pre.01 +website = http://www.lumiera.org +authors = Joel Holdsworth|Christian Thäter|Hermann Voßeler|[Other Authors Here] + +[Gui] +stylesheet = lumiera_ui.rc +iconpath = $ORIGIN/../../share/lumiera/icons:$ORIGIN/gui/icons:~/.lumiera/icons +resourcepath = $ORIGIN/../../share/lumiera/gui:$ORIGIN/gui diff --git a/src/common/basic-setup.cpp b/src/common/basic-setup.cpp index 5abeee2c0..030d76623 100644 --- a/src/common/basic-setup.cpp +++ b/src/common/basic-setup.cpp @@ -83,15 +83,27 @@ namespace lumiera { "search path for extended configuration. " "Extended Config system not yet implemented " "Ignored as of 2/2011") + ("Lumiera.title", opt::value(), + "title of the Lumiera Application, e.g. for windows") ("Lumiera.version", opt::value(), "Application version string") + ("Lumiera.website", opt::value(), + "URL of the Lumiera website") + ("Lumiera.authors", opt::value(), + "names of Lumiera authors, for 'about' dialog. Separated by '|'") + + ("Gui.stylesheet", opt::value(), + "name of the GTK stylesheet to use. Will be searched in resource path") + ("Gui.iconpath", opt::value(), + "search path for icons") + ("Gui.resourcepath", opt::value(), + "general search path for UI resources") ; ifstream configIn (resolve(bootstrapIni).c_str()); - opt::parsed_options parsed = - opt::parse_config_file (configIn, syntax); + opt::parsed_options parsed = opt::parse_config_file (configIn, syntax); opt::store (parsed, settings); opt::notify(settings); diff --git a/src/common/configfacade.cpp b/src/common/configfacade.cpp index f17d9163d..3509c435e 100644 --- a/src/common/configfacade.cpp +++ b/src/common/configfacade.cpp @@ -132,7 +132,7 @@ extern "C" { /* ==== implementation C interface for accessing setup.ini ======= // fetch plugin search path from setup.ini and expand any $ORIGIN token SearchPathSplitter pathElement(Config::get (KEY_PLUGIN_PATH)); while (pathElement) - pathSpec += pathElement.fetch() +":"; + pathSpec += pathElement.next() +":"; } return cStr(pathSpec); diff --git a/src/gui/gtk-lumiera.cpp b/src/gui/gtk-lumiera.cpp index a67d419d1..c44e737fd 100644 --- a/src/gui/gtk-lumiera.cpp +++ b/src/gui/gtk-lumiera.cpp @@ -27,21 +27,45 @@ #include "gui/controller/controller.hpp" #include "gui/model/project.hpp" #include "lib/singleton.hpp" +#include "lib/symbol.hpp" +#include "include/config-facade.h" -using namespace Gtk; -using namespace Glib; -using namespace gui; -using namespace gui::workspace; -using namespace gui::model; -using namespace gui::controller; -using namespace std; +#include +#include +#include namespace gui { +using namespace Gtk; +using namespace Glib; +using namespace gui::model; +using namespace gui::workspace; +using namespace gui::controller; + +using boost::algorithm::is_any_of; +using boost::algorithm::split; + +using lumiera::Config; +using lib::Literal; + +typedef std::vector UVector; + + + namespace { + /** storage for the Main Application object */ lib::Singleton theApplicationInstance; + + Literal KEY_TITLE = "Lumiera.title"; + Literal KEY_VERSION = "Lumiera.version"; + Literal KEY_WEBSITE = "Lumiera.website"; + Literal KEY_AUTHORS = "Lumiera.authors"; + + Literal KEY_STYLESHEET = "Gui.stylesheet"; + Literal KEY_UIRES_PATH = "Gui.resourcepath"; + Literal KEY_ICON_PATH = "Gui.iconpath"; } @@ -62,68 +86,63 @@ GtkLumiera::main (int argc, char *argv[]) Main kit(argc, argv); - Glib::set_application_name (get_app_title()); + Glib::set_application_name (getAppTitle()); Project project; Controller controller(project); - windowManagerInstance_.init(); - windowManagerInstance_.set_theme ("lumiera_ui.rc"); - windowManagerInstance_.new_window (project, controller); - - kit.run(); + windowManagerInstance_.init (Config::get (KEY_ICON_PATH), Config::get (KEY_UIRES_PATH)); + windowManagerInstance_.setTheme (Config::get (KEY_STYLESHEET)); + + + windowManagerInstance_.newWindow (project, controller); + kit.run(); // GTK event loop } + WindowManager& GtkLumiera::windowManager() { return windowManagerInstance_; } -Glib::ustring -GtkLumiera::get_home_data_path() + +cuString +GtkLumiera::getAppTitle() { - const ustring app_name("lumiera"); - const ustring path(Glib::get_home_dir()); - return ustring::compose("%1/.%2", path, app_name); + return Config::get (KEY_TITLE); } -const Glib::ustring -GtkLumiera::get_app_title() + +cuString +GtkLumiera::getAppVersion() { - return "Lumiera"; + return Config::get (KEY_VERSION); } -const Glib::ustring -GtkLumiera::get_app_version() + +cuString +GtkLumiera::getCopyright() { - return "0.pre.01"; + return _("© 2012 The Lumiera Team"); } -const Glib::ustring GtkLumiera::get_app_copyright() + +cuString +GtkLumiera::getLumieraWebsite() { - return _("© 2008 The Lumiera Team"); + return Config::get (KEY_WEBSITE); } -const Glib::ustring GtkLumiera::get_app_website() -{ - return "http://www.lumiera.org"; -} -const std::vector -GtkLumiera::get_app_authors() +const UVector +GtkLumiera::getLumieraAuthors() { - const gchar* app_authors[] = { - "Joel Holdsworth", - "Christian Thaeter", - "Hermann Vosseler", - "[Other Authors Here]"}; + string authors = Config::get (KEY_AUTHORS); + UVector authorsList; - const int count = sizeof(app_authors) / sizeof(gchar*); - std::vector list(count); - for(int i = 0; i < count; i++) - list[i] = app_authors[i]; - return list; + split (authorsList, authors, is_any_of (",|")); + return authorsList; } diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index 9d403ccfa..65ee00e9c 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -19,10 +19,32 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /** @file gtk-lumiera.hpp - ** This file contains application wide global definitions - ** user actions. - ** @see gtk-lumiera.cpp + ** The main application object. + ** Invoking the GtkLumiera::main() function brings up the GUI; this + ** function will block in the GTK event thread until the Application gets + ** closed by user interaction or by triggering a shutdown via the GuiNotificationFacade. + ** GtkLumiera is a singleton and owns the central WindowManager instance used for + ** opening all windows and registering and loading icons and resources. + ** + ** \par configuration and resource search + ** The GUI object retrieves the necessary configuration values from lumiera::Config, + ** the config facade in the application core. Currently as of 2/2011 these values are + ** loaded from setup.ini, because the full-blown config system is not yet implemented. + ** Amongst others, this configuration defines a search path for icons and a + ** separate search path for resources. These path specs may use the token \c $ORIGIN + ** to refer to the installation directory of the currently executing program. + ** This allows for a relocatable Lumiera installation bundle. + ** + ** @see guistart.cpp the plugin to pull up this GUI + ** @see gui::GuiFacade access point for starting the GUI + ** @see gui::GuiNotification interface for communication with the gui from the lower layers + ** @see lumiera::Config + ** @see lumiera::BasicSetup definition of the acceptable configuration values + ** @see lumiera::AppState general Lumiera application main + ** */ #ifndef GUI_GTK_LUMIERA_H diff --git a/src/gui/window-manager.cpp b/src/gui/window-manager.cpp index 461c9c01c..ea8322deb 100644 --- a/src/gui/window-manager.cpp +++ b/src/gui/window-manager.cpp @@ -24,6 +24,12 @@ #include "gui/window-manager.hpp" #include "gui/gtk-lumiera.hpp" #include "gui/workspace/workspace-window.hpp" +#include "lib/searchpath.hpp" +#include "lib/util.hpp" + +#include + +using util::cStr; using namespace Gtk; using namespace Glib; @@ -31,21 +37,37 @@ using namespace boost; using namespace std; using namespace gui::workspace; +namespace fsys = boost::filesystem; + + namespace gui { IconSize WindowManager::GiantIconSize = ICON_SIZE_INVALID; IconSize WindowManager::MenuIconSize = ICON_SIZE_INVALID; + void -WindowManager::init() +WindowManager::init (string const& iconPath, string const& resourcePath) { + this->iconSearchPath_ = iconPath; + this->resourceSerachPath_ = resourcePath; + register_app_icon_sizes(); register_stock_items(); } + void -WindowManager::new_window(gui::model::Project &source_project, - gui::controller::Controller &source_controller) +WindowManager::setTheme (string const& stylesheetName) +{ + gtk_rc_parse (cStr(lib::resolveModulePath (stylesheetName, resourceSerachPath_))); + gtk_rc_reset_styles (gtk_settings_get_default()); +} + + +void +WindowManager::newWindow (gui::model::Project& source_project, + gui::controller::Controller &source_controller) { shared_ptr window( new WorkspaceWindow(source_project, source_controller)); @@ -61,25 +83,9 @@ WindowManager::new_window(gui::model::Project &source_project, update_close_window_in_menus(); } -bool -WindowManager::set_theme(Glib::ustring path) -{ - if(access(path.c_str(), R_OK)) - { - // gdk defines 'ERROR' need to prefix it with 'NOBUG_' here - NOBUG_ERROR(gui, "WindowManger: Unable to load rc file \"%s\"", - path.c_str()); - return false; - } - - gtk_rc_parse(path.c_str()); - gtk_rc_reset_styles (gtk_settings_get_default()); - - return true; -} bool -WindowManager::on_window_closed(GdkEventAny* event) +WindowManager::on_window_closed (GdkEventAny* event) { REQUIRE(event); REQUIRE(event->window); @@ -87,7 +93,7 @@ WindowManager::on_window_closed(GdkEventAny* event) list< shared_ptr >::iterator iterator = windowList.begin(); - while(iterator != windowList.end()) + while (iterator != windowList.end()) { shared_ptr workspace_window(*iterator); REQUIRE(workspace_window); @@ -103,7 +109,7 @@ WindowManager::on_window_closed(GdkEventAny* event) iterator++; } - if(windowList.empty()) + if (windowList.empty()) { // All windows have been closed - we should exit Main *main = Main::instance(); @@ -117,6 +123,7 @@ WindowManager::on_window_closed(GdkEventAny* event) return false; } + void WindowManager::update_close_window_in_menus() { @@ -135,10 +142,11 @@ WindowManager::update_close_window_in_menus() } } + Cairo::RefPtr -WindowManager::read_style_colour_property ( - Gtk::Widget &widget, const gchar *property_name, - guint16 red, guint16 green, guint16 blue) +WindowManager::read_style_colour_property (Gtk::Widget& widget, + const gchar *property_name, + guint16 red, guint16 green, guint16 blue) { REQUIRE (property_name); @@ -164,6 +172,7 @@ WindowManager::read_style_colour_property ( return pattern; } + void WindowManager::register_app_icon_sizes() { @@ -173,34 +182,35 @@ WindowManager::register_app_icon_sizes() MenuIconSize = IconSize::register_new ("menu", 16, 16); } + void WindowManager::register_stock_items() { Glib::RefPtr factory = IconFactory::create(); - add_stock_icon_set(factory, "panel-assets", "panel_assets", _("_Assets")); - add_stock_icon_set(factory, "panel-timeline", "panel_timeline", _("_Timeline")); - add_stock_icon_set(factory, "panel-viewer", "panel_viewer", _("_Viewer")); + add_stock_icon_set(factory, "panel-assets", "panel_assets", _("_Assets")); + add_stock_icon_set(factory, "panel-timeline", "panel_timeline",_("_Timeline")); + add_stock_icon_set(factory, "panel-viewer", "panel_viewer", _("_Viewer")); - add_stock_icon_set(factory, "window-new", "new_window", _("New _Window")); + add_stock_icon_set(factory, "window-new", "new_window", _("New _Window")); - add_stock_icon_set(factory, "tool-arrow", "tool_arrow", _("_Arrow")); - add_stock_icon_set(factory, "tool-i-beam", "tool_i_beam", _("_I-Beam")); + add_stock_icon_set(factory, "tool-arrow", "tool_arrow", _("_Arrow")); + add_stock_icon_set(factory, "tool-i-beam", "tool_i_beam", _("_I-Beam")); - add_stock_icon_set(factory, "track-disabled", "track_disabled", _("Track Disabled")); - add_stock_icon_set(factory, "track-enabled", "track_enabled", _("Track Enabled")); - add_stock_icon_set(factory, "track-locked", "track_locked", _("Track Locked")); - add_stock_icon_set(factory, "track-unlocked", "track_unlocked", _("Track Unlocked")); + add_stock_icon_set(factory, "track-disabled", "track_disabled",_("Track Disabled")); + add_stock_icon_set(factory, "track-enabled", "track_enabled", _("Track Enabled")); + add_stock_icon_set(factory, "track-locked", "track_locked", _("Track Locked")); + add_stock_icon_set(factory, "track-unlocked", "track_unlocked",_("Track Unlocked")); factory->add_default(); //Add factory to list of factories. } + bool -WindowManager::add_stock_icon_set( - const Glib::RefPtr& factory, - const Glib::ustring& icon_name, - const Glib::ustring& id, - const Glib::ustring& label) +WindowManager::add_stock_icon_set (Glib::RefPtr const& factory, + cuString& icon_name, + cuString& id, + cuString& label) { Gtk::IconSet icon_set; @@ -221,7 +231,7 @@ WindowManager::add_stock_icon_set( if(no_icons) { // No icons were loaded - ERROR(gui, "Unable to load icon \"%s\"", icon_name.c_str()); + ERROR (gui, "Unable to load icon '%s'", cStr(icon_name)); return false; } @@ -232,31 +242,36 @@ WindowManager::add_stock_icon_set( return true; } + bool -WindowManager::add_stock_icon(Gtk::IconSet &icon_set, - const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard) +WindowManager::add_stock_icon (Gtk::IconSet &icon_set, + cuString& icon_name, + Gtk::IconSize size, + bool wildcard) { // Try the icon theme if(add_theme_icon_source(icon_set, icon_name, size, wildcard)) return true; - - // Try the ~/.lumiera/icons folder - if(add_non_theme_icon_source(icon_set, ustring::compose("%1/%2", - GtkLumiera::get_home_data_path(), ustring("icons")), - icon_name, size, wildcard)) - return true; - // Try the local directory - if(add_non_theme_icon_source( - icon_set, get_current_dir(), icon_name, size, wildcard)) - return true; - - return false; + // Try to resolve the icon via the configured search path + lib::SearchPathSplitter iconLocations (iconSearchPath_); + while (iconLocations) + if (add_non_theme_icon_source (icon_set + ,iconLocations.next() + ,icon_name + ,size + ,wildcard)) + return true; + + return false; // icon not found } + bool -WindowManager::add_theme_icon_source(Gtk::IconSet &icon_set, - const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard) +WindowManager::add_theme_icon_source (Gtk::IconSet &icon_set, + cuString& icon_name, + Gtk::IconSize size, + bool wildcard) { // Get the size int width = 0, height = 0; @@ -269,22 +284,19 @@ WindowManager::add_theme_icon_source(Gtk::IconSet &icon_set, REQUIRE(theme); TODO ("find out how IconInfo could be made const. For example, GTKmm 2.10.10 is missing the const on operator bool() in iconinfo.h"); - IconInfo info = theme->lookup_icon(icon_name, width, - (IconLookupFlags)0); - if(info) - { - const ustring path(info.get_filename()); - if(add_stock_icon_from_path(path, icon_set, size, wildcard)) - return true; - } - - return false; + IconInfo info = theme->lookup_icon(icon_name, width, (IconLookupFlags)0); + + if (!info) return false; // unable to resolve Icon + + cuString path(info.get_filename()); + return add_stock_icon_from_path(path, icon_set, size, wildcard); } + bool -WindowManager::add_non_theme_icon_source(Gtk::IconSet &icon_set, - const Glib::ustring& base_dir, const Glib::ustring& icon_name, - Gtk::IconSize size, bool wildcard) +WindowManager::add_non_theme_icon_source (Gtk::IconSet &icon_set, + cuString& base_dir, cuString& icon_name, + Gtk::IconSize size, bool wildcard) { // Get the size int width = 0, height = 0; @@ -293,33 +305,37 @@ WindowManager::add_non_theme_icon_source(Gtk::IconSet &icon_set, REQUIRE(width > 0); // Try to load the icon - const ustring path(ustring::compose("%1/%2x%3/%4.png", + cuString path(ustring::compose("%1/%2x%3/%4.png", base_dir, width, height, icon_name)); return add_stock_icon_from_path(path, icon_set, size, wildcard); } + bool -WindowManager::add_stock_icon_from_path(Glib::ustring path, - Gtk::IconSet &icon_set, Gtk::IconSize size, bool wildcard) +WindowManager::add_stock_icon_from_path (string path, + Gtk::IconSet &icon_set, + Gtk::IconSize size, + bool wildcard) { - Gtk::IconSource source; + if (!fsys::exists (path)) return false; - try - { - // This throws an exception if the file is not found: + try { + Gtk::IconSource source; source.set_pixbuf(Gdk::Pixbuf::create_from_file(path)); + source.set_size_wildcarded(wildcard); + source.set_size(size); + + icon_set.add_source(source); + + return true; } - catch(const Glib::Exception& ex) + + catch(Glib::Exception const& ex) { + WARN (gui, "Failure when accessing icon '%s'. Problem: %s", cStr(path), cStr(ex.what())); return false; } - - source.set_size(size); - source.set_size_wildcarded(wildcard); - - icon_set.add_source(source); - - return true; } + } // namespace gui diff --git a/src/gui/window-manager.hpp b/src/gui/window-manager.hpp index 090babdc2..2dbd91e93 100644 --- a/src/gui/window-manager.hpp +++ b/src/gui/window-manager.hpp @@ -41,51 +41,55 @@ #include #include #include - +#include namespace gui { + +using std::string; -namespace model { - class Project; -} // model -namespace controller { - class Controller; -} // model +namespace model { class Project; } +namespace controller { class Controller; } +namespace workspace { class WorkspaceWindow;} -namespace workspace { - class WorkspaceWindow; -} /** - * The centralised manager of all lumiera-gui's windows. + * The centralised manager of all the windows, + * icons and resources within Lumiera's GUI. */ class WindowManager : boost::noncopyable { + string iconSearchPath_; + string resourceSerachPath_; + + public: /** * Initialise the window manager on application start. - * Register the icon configuration and sizes. + * Register the icon configuration and sizes and lookup + * all the icons -- either from the default theme of via + * the given Lumiera icon search paths (see \c setup.ini ). + * @see lumiera::Config */ - void init(); + void init (string const& iconPath, string const& resourcePath); /** - * Creates a new window connected to a specified project and - * controller + * Creates a new window connected to a specified project and controller * @param source_project The project to connect the window to. * @param source_controller The controller to connect the window to. */ - void new_window(gui::model::Project &source_project, - gui::controller::Controller &source_controller); + void newWindow (gui::model::Project&, gui::controller::Controller&); /** - * Sets the theme of the lumiera-gui's. - * @param path This string must specify a path where a GTK stylesheet - * will be found. + * Sets the theme to use for the Lumiera GUI. + * @param stylesheetName GTK stylesheet to load from the resourceSearchPath_ + * @throw error::Config if this stylesheet can't be resolved on the searchpath + * @see #init + * @see lumiera::Config */ - bool set_theme(Glib::ustring path); + void setTheme (string const& stylesheetName); /** * A utility function which reads a colour style from the GTK Style. @@ -102,9 +106,7 @@ public: private: - /** - * An event handler for when a window has been closed. - */ + /** Event handler for when a window has been closed */ bool on_window_closed(GdkEventAny* event); private: @@ -134,14 +136,14 @@ private: * @param icon_name The file name of the icon to add. * @param id The id name of the icon. * @param label The user readable icon name for this icon. - * @return Returns true if the icon was successfully loaded, returns - * false otherwise. + * @return \c true if the icon was successfully loaded, + * returns \c false otherwise. */ bool add_stock_icon_set( const Glib::RefPtr& factory, - const Glib::ustring& icon_name, - const Glib::ustring& id, - const Glib::ustring& label); + cuString& icon_name, + cuString& id, + cuString& label); /** * Loads an icon, searching standard icon locations, @@ -149,24 +151,22 @@ private: * @param icon_set The icon set to add the icon to. * @param icon_name The file name of the icon to load. * @param size The size of the icon to load. - * @param wildcard This value is set to true if this icon is - * wildcarded. - * @return Returns true if the icon was loaded successfully. + * @param wildcard \c true if this icon is to be wildcarded. + * @return \c true if the icon was loaded successfully. */ bool add_stock_icon(Gtk::IconSet &icon_set, - const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard); + cuString& icon_name, Gtk::IconSize size, bool wildcard); /** * Loads an icon from a the icon theme * @param icon_set The icon set to add the icon to. * @param icon_name The name of the icon to load. * @param size The size of the icon to load. - * @param wildcard This value is set to true if this icon is - * wildcarded. - * @return Returns true if the icon was loaded successfully. + * @param wildcard \c true if this icon is to be wildcarded. + * @return \c true if the icon was loaded successfully. */ bool add_theme_icon_source(Gtk::IconSet &icon_set, - const Glib::ustring& icon_name, Gtk::IconSize size, bool wildcard); + cuString& icon_name, Gtk::IconSize size, bool wildcard); /** * Loads an icon from a non theme set. @@ -174,12 +174,11 @@ private: * @param base_dir The root icons directory to load from. * @param icon_name The file name of the icon to load. * @param size The size of the icon to load. - * @param wildcard This value is set to true if this icon is - * wildcarded. - * @return Returns true if the icon was loaded successfully. + * @param wildcard \c true if this icon is to be wildcarded. + * @return \c true if the icon was loaded successfully. */ bool add_non_theme_icon_source(Gtk::IconSet &icon_set, - const Glib::ustring& base_dir, const Glib::ustring& icon_name, + cuString& base_dir, cuString& icon_name, Gtk::IconSize size, bool wildcard); /** @@ -187,13 +186,13 @@ private: * @param path The path to load from. * @param icon_set The icon set to add the icon to. * @param size The size of the icon to load. - * @param wildcard This value is set to true if this icon is - * wildcarded. - * @return Returns true if the icon was loaded successfully. + * @param wildcard \c true if this icon is to be wildcarded. + * @return \c true if the icon was loaded successfully. */ - bool add_stock_icon_from_path(Glib::ustring path, + bool add_stock_icon_from_path(string path, Gtk::IconSet &icon_set, Gtk::IconSize size, bool wildcard); - + + private: std::list< boost::shared_ptr > windowList; diff --git a/src/gui/workspace/actions.cpp b/src/gui/workspace/actions.cpp index 90bc89967..fffdd7f71 100644 --- a/src/gui/workspace/actions.cpp +++ b/src/gui/workspace/actions.cpp @@ -58,13 +58,13 @@ Actions::populate_main_actions(Glib::RefPtr uiManager) // File menu actionGroup->add(Action::create("FileMenu", _("_File"))); - actionGroup->add(Action::create("FileNewProject", Stock::NEW, _("_New Project...")), + actionGroup->add(Action::create("FileNewProject", Stock::NEW, _("_New Project...")), mem_fun(*this, &Actions::on_menu_file_new_project)); - actionGroup->add(Action::create("FileOpenProject", Stock::OPEN, _("_Open Project...")), + actionGroup->add(Action::create("FileOpenProject", Stock::OPEN, _("_Open Project...")), mem_fun(*this, &Actions::on_menu_file_open_project)); - actionGroup->add(Action::create("FileSaveProject", Stock::SAVE, _("_Save Project")), + actionGroup->add(Action::create("FileSaveProject", Stock::SAVE, _("_Save Project")), mem_fun(*this, &Actions::on_menu_others)); - actionGroup->add(Action::create("FileSaveProjectAs", Stock::SAVE_AS, _("_Save Project As...")), + actionGroup->add(Action::create("FileSaveProjectAs",Stock::SAVE_AS, _("_Save Project As...")), mem_fun(*this, &Actions::on_menu_others)); actionGroup->add(Action::create("FileRender", _("_Render...")), AccelKey("R"), @@ -78,11 +78,11 @@ Actions::populate_main_actions(Glib::RefPtr uiManager) mem_fun(*this, &Actions::on_menu_others)); actionGroup->add(Action::create("EditRedo", Stock::REDO), mem_fun(*this, &Actions::on_menu_others)); - actionGroup->add(Action::create("EditCut", Stock::CUT), + actionGroup->add(Action::create("EditCut", Stock::CUT), mem_fun(*this, &Actions::on_menu_others)); actionGroup->add(Action::create("EditCopy", Stock::COPY), mem_fun(*this, &Actions::on_menu_others)); - actionGroup->add(Action::create("EditPaste", Stock::PASTE), + actionGroup->add(Action::create("EditPaste",Stock::PASTE), mem_fun(*this, &Actions::on_menu_others)); actionGroup->add(Action::create("EditPreferences", Stock::PREFERENCES), mem_fun(*this, &Actions::on_menu_edit_preferences)); @@ -360,9 +360,8 @@ Actions::on_menu_help_about() // Configure the about dialog AboutDialog dialog; - //dialog.set_program_name(AppTitle); + dialog.set_program_name(GtkLumiera::getAppTitle()); dialog.set_version(GtkLumiera::getAppVersion()); - //dialog.set_version(AppState::get("version")); dialog.set_copyright(GtkLumiera::getCopyright()); dialog.set_website(GtkLumiera::getLumieraWebsite()); dialog.set_authors(GtkLumiera::getLumieraAuthors()); diff --git a/src/gui/workspace/workspace-window.cpp b/src/gui/workspace/workspace-window.cpp index b8ef1ea56..015daa4c5 100644 --- a/src/gui/workspace/workspace-window.cpp +++ b/src/gui/workspace/workspace-window.cpp @@ -84,7 +84,7 @@ WorkspaceWindow::create_ui() //set_default_direction (TEXT_DIR_RTL); //----- Configure the Window -----// - set_title(GtkLumiera::get_app_title()); + set_title(GtkLumiera::getAppTitle()); set_default_size(1024, 768); //----- Set up the UI Manager -----// diff --git a/src/lib/cmdline.cpp b/src/lib/cmdline.cpp index bffbd2e8e..249121e60 100644 --- a/src/lib/cmdline.cpp +++ b/src/lib/cmdline.cpp @@ -27,18 +27,12 @@ #include "include/logging.h" #include -#include #include -#include - -using boost::algorithm::split; -using boost::algorithm::join; -using boost::algorithm::is_any_of; -using boost::algorithm::token_compress_on; using boost::regex; using boost::smatch; using boost::regex_search; +using boost::algorithm::join; using util::noneg; diff --git a/src/lib/searchpath.cpp b/src/lib/searchpath.cpp index 809cc4889..393c16b66 100644 --- a/src/lib/searchpath.cpp +++ b/src/lib/searchpath.cpp @@ -80,7 +80,7 @@ namespace lib { // try / continue search path if (searchLocation.isValid()) - modulePathName = fsys::path() / searchLocation.fetch() / moduleName; + modulePathName = fsys::path() / searchLocation.next() / moduleName; else throw error::Config ("Module \""+moduleName+"\" not found" + (searchPath.empty()? ".":" in search path: "+searchPath)); diff --git a/src/lib/searchpath.hpp b/src/lib/searchpath.hpp index eff8b2510..64b420903 100644 --- a/src/lib/searchpath.hpp +++ b/src/lib/searchpath.hpp @@ -27,7 +27,6 @@ #include "lib/error.hpp" #include "lib/bool-checkable.hpp" -#include #include #include #include @@ -88,7 +87,7 @@ namespace lib { } string - fetch () + next () { if (!isValid()) throw error::Logic ("Search path exhausted." @@ -133,11 +132,13 @@ namespace lib { - /** helper to establish the location to search for loadable modules. - * This is a simple demonstration of the basic technique used in the - * real application source to establish a plugin search path, based - * on the actual executable position plus compiled in and configured - * relative and absolute path specifications. + /** helper to establish the location to search for loadable modules, + * configuration files, icons and further resources. After first trying + * the moduleName directly, the given search path is walked using the + * SearchPathSplitter, until encountering an existing file with the + * given name. + * @return the absolute pathname of the module file found + * @throws error::Config when the resolution fails */ string resolveModulePath (string moduleName, string searchPath = ""); diff --git a/src/lib/time.cpp b/src/lib/time.cpp index c9dc31a4b..a7efb8009 100644 --- a/src/lib/time.cpp +++ b/src/lib/time.cpp @@ -2,7 +2,8 @@ Time - Utilities for handling time Copyright (C) Lumiera.org - 2011, Christian Thaeter + 2008, Christian Thaeter + 2011, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/tests/lib/search-path-splitter-test.cpp b/tests/lib/search-path-splitter-test.cpp index bd58190e3..b9d1b1396 100644 --- a/tests/lib/search-path-splitter-test.cpp +++ b/tests/lib/search-path-splitter-test.cpp @@ -66,7 +66,7 @@ namespace test { walk ("/usr/bin:/usr/lib"); SearchPathSplitter sp(""); - VERIFY_ERROR (ITER_EXHAUST, sp.fetch() ); + VERIFY_ERROR (ITER_EXHAUST, sp.next() ); } void @@ -74,7 +74,7 @@ namespace test { { SearchPathSplitter path(spec); while (path) - cout << "➢➢" << path.fetch() << endl; + cout << "➢➢" << path.next() << endl; } @@ -86,9 +86,9 @@ namespace test { string expected = (exePath.remove_leaf() / "modules").string(); SearchPathSplitter sp("xyz:$ORIGIN/modules:abc"); - CHECK ("xyz" == sp.fetch()); - CHECK (sp.fetch() == expected); - CHECK ("abc" == sp.fetch()); + CHECK ("xyz" == sp.next()); + CHECK (sp.next() == expected); + CHECK ("abc" == sp.next()); CHECK (!sp.isValid()); } }; From 80461c9b7630f06faaf35e9cf86a72d485c95759 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 7 Feb 2011 11:35:44 +0100 Subject: [PATCH 133/140] SCons: try to force installation of all generated icons Not sure if this works; problem is that the icons generated from SVG are just dumped into the target folder, but we miss to generate the respective installation targets --- SConstruct | 1 + admin/scons/LumieraEnvironment.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 26674fc25..6f78fbdbd 100644 --- a/SConstruct +++ b/SConstruct @@ -410,6 +410,7 @@ def defineInstallTargets(env, artifacts): env.SymLink('$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') # env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) + env.Alias('install', artifacts['gui']) env.Alias('install', '$DESTDIR') ##################################################################### diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 28b1eb48f..21b1f9ecb 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -138,7 +138,17 @@ def register_LumieraResourceBuilder(env): source = str(source[0]) targetdir = env.path.buildIcon targetfiles = renderer.getTargetNames(source) # parse SVG - return ([targetdir+name for name in targetfiles], source) + + # additionally create an installation task for each Icon to be generated + installLocation = env.path.installIcon + generateTargets = [] + for icon in targetfiles: + icon = targetdir+icon + subdir = getDirname(str(icon)) + env.Install (installLocation+subdir, icon) + generateTargets.append(icon) + + return (generateTargets, source) def IconResource(env, source): """Copy icon pixmap to corresponding icon dir. """ From 30bc42a3f3660717a9895b08e021511d530959c5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 7 Feb 2011 11:36:06 +0100 Subject: [PATCH 134/140] document icon rendering and fix a broken 16x16 icon --- admin/render_icon.py | 25 +++++++++++++++++++++++++ icons/svg/tool-i-beam.svg | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/admin/render_icon.py b/admin/render_icon.py index e3f76c129..63bacbf37 100755 --- a/admin/render_icon.py +++ b/admin/render_icon.py @@ -31,6 +31,31 @@ inkscapePath = "/usr/bin/inkscape" rsvgPath = "./rsvg-convert" artworkLayerPrefix = "artwork:" +# +# 2/2011 some notes by Ichthyo +# The purpose of this python script is +# - to parse a SVG +# - to invoke Inkscape to render this SVG into a raster image (icon) +# +# For the actual call into Incscape we rely on an executable 'rsvg-convert', +# which is built during the Lumiera build process. +# +# Judging from the code and the actual SVGs, this seems to work as follows: +# The SVG contains a design to be rendered into raster images of various sizes. +# These sizes are determined by special rectangles, which act as bounding box and +# are placed on a special 'plate' layer, which is a child layer of the main +# 'artwork:' layer. The grid of the SVG is setup such as to result in pixel sizes +# suitable for icon generation. The actual size of the generated icons are then +# parsed from the height and width attributes of the mentioned bounding box +# rectangles. +# +# The parser seems to be rather simplistic; the sizes and positions need to be +# integral numbers. In one instance we had a float number in the y coordinate, +# which resulted in an invalid, zero sized output icon +# +# + + def createDirectory( name ): try: if os.path.isfile(name): diff --git a/icons/svg/tool-i-beam.svg b/icons/svg/tool-i-beam.svg index caad4c175..51551054c 100644 --- a/icons/svg/tool-i-beam.svg +++ b/icons/svg/tool-i-beam.svg @@ -158,7 +158,7 @@ width="16" height="16" x="124" - y="76.009514" /> + y="76" /> Date: Sun, 13 Feb 2011 23:11:16 +0100 Subject: [PATCH 135/140] small fixes, comments --- admin/scons/Buildhelper.py | 2 +- tests/library/test-time.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 69fb3849d..e5d111885 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -207,7 +207,7 @@ def checkCommandOption(env, optID, val=None, cmdName=None): class Record(dict): - """ a set of properties with map style access. + """ a set of properties with record style access. Record is a dictionary, but the elements can be accessed conveniently as if they where object fields """ diff --git a/tests/library/test-time.c b/tests/library/test-time.c index 5143ad994..13137991b 100644 --- a/tests/library/test-time.c +++ b/tests/library/test-time.c @@ -132,7 +132,9 @@ TEST (ntsc_drop_frame) CHECK (lumiera_time_ntsc_drop_frames (t) == 15); CHECK (lumiera_time_frame_count (t, NTSC_DROP_FRAME_FPS) == 423191); - // Make sure we get back the same times. These tests are perhaps overly exhaustive. + // Cover the whole value range; + // Manually construct a drop-frame timecode + // Make sure our library function returns the same times. int min; int sec; int frame; From 35380e7873701b077c8ef4268248108e1e4139a2 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 13 Feb 2011 23:12:36 +0100 Subject: [PATCH 136/140] correct expected values for timehandling tests regarding frames: we don't round, we truncate towards the next lower integer --- tests/15time.tests | 4 ++-- tests/library/test-time.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/15time.tests b/tests/15time.tests index 2dcedbea9..fbdd631a9 100644 --- a/tests/15time.tests +++ b/tests/15time.tests @@ -1,8 +1,8 @@ TESTING "Time conversion" ./test-time TEST "basic functionality" basic < Date: Sun, 13 Feb 2011 23:45:11 +0100 Subject: [PATCH 137/140] fix the plugin loading test to suit the new dir layout the 'missing path' test still works, but rather accidentally: the plugin search path retrieved from resolving $ORIGIN is an absolute path, while this test uses relative paths; thus the querried plugin won't be found. In the second test, we set an environment override, using the relative path (which is now 'modules'). Thus the discovery works. --- data/config/setup.ini | 2 +- tests/31plugin.tests | 3 ++- tests/common/test-interfaces.c | 13 +++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/data/config/setup.ini b/data/config/setup.ini index 9ea9af1a3..b9723bc0f 100644 --- a/data/config/setup.ini +++ b/data/config/setup.ini @@ -10,7 +10,7 @@ # gui = gtk_gui.lum modulepath = $ORIGIN/modules -configpath = /usr/share/lumiera/config:~/.lumiera +configpath = $ORIGIN/../../share/lumiera/config:~/.lumiera title = Lumiera version = 0.pre.01 website = http://www.lumiera.org diff --git a/tests/31plugin.tests b/tests/31plugin.tests index e81b1c5a9..ace85086a 100644 --- a/tests/31plugin.tests +++ b/tests/31plugin.tests @@ -15,7 +15,8 @@ return: 0 END TEST "plugin unloading" plugin_unload < Date: Mon, 14 Feb 2011 23:51:11 +0100 Subject: [PATCH 138/140] Fix: extract empty path relative to $ORIGIN --- src/lib/searchpath.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/searchpath.cpp b/src/lib/searchpath.cpp index 393c16b66..d328a388b 100644 --- a/src/lib/searchpath.cpp +++ b/src/lib/searchpath.cpp @@ -39,7 +39,7 @@ namespace lib { - regex SearchPathSplitter::EXTRACT_PATHSPEC ("(\\$?ORIGIN/)?([^:]+)"); + regex SearchPathSplitter::EXTRACT_PATHSPEC ("(\\$?ORIGIN/?)?([^:]*)"); /** @internal helper to figure out the installation directory, From aef929b3d98a6b414d51f12f08938d82a1367caf Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 14 Feb 2011 23:52:36 +0100 Subject: [PATCH 139/140] better install the setup.ini direcly into $ORIGIN seems to be the most obvious location to install it --- SConstruct | 2 +- admin/scons/LumieraEnvironment.py | 23 ++++++++++++++++++++--- data/config/setup.ini | 2 +- src/common/appstate.cpp | 4 +--- src/common/basic-setup.hpp | 5 +++++ 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/SConstruct b/SConstruct index 6f78fbdbd..6419b5cb7 100644 --- a/SConstruct +++ b/SConstruct @@ -348,7 +348,7 @@ def defineBuildTargets(env, artifacts): artifacts['corelib'] = core artifacts['support'] = lLib artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'], LIBS=core, install=True) - + env.ConfigData(env.path.srcConf+'setup.ini') + + env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') ) diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index 21b1f9ecb..c0ce77375 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -165,10 +165,27 @@ def register_LumieraResourceBuilder(env): env.Install (toInstall, source) return env.Install(toBuild, source) - def ConfigData(env, source): + def ConfigData(env, source, targetDir=None): + """ install (copy) configuration- and metadata. + target dir is either the install location configured (in SConstruct), + or an explicitly given absolute or relative path segment, which might refer + to the location of the executable through the $ORIGIN token + """ subdir = getDirname(str(source), env.path.srcConf) # removes source location path prefix - toBuild = env.path.buildConf+subdir - toInstall = env.path.installConf+subdir + if targetDir: + if path.isabs(targetDir): + toBuild = toInstall = path.join(targetDir,subdir) + else: + if targetDir.startswith('$ORIGIN'): + targetDir = targetDir[len('$ORIGIN'):] + toBuild = path.join(env.path.buildExe, targetDir, subdir) + toInstall = path.join(env.path.installExe, targetDir, subdir) + else: + toBuild = path.join(env.path.buildConf, targetDir, subdir) + toInstall = path.join(env.path.installConf, targetDir, subdir) + else: + toBuild = path.join(env.path.buildConf,subdir) + toInstall = path.join(env.path.installConf,subdir) env.Install (toInstall, source) return env.Install(toBuild, source) diff --git a/data/config/setup.ini b/data/config/setup.ini index b9723bc0f..43f4b9317 100644 --- a/data/config/setup.ini +++ b/data/config/setup.ini @@ -10,7 +10,7 @@ # gui = gtk_gui.lum modulepath = $ORIGIN/modules -configpath = $ORIGIN/../../share/lumiera/config:~/.lumiera +configpath = $ORIGIN/../../share/lumiera/config:$ORIGIN/config:~/.lumiera # unused currently (2/2011) title = Lumiera version = 0.pre.01 website = http://www.lumiera.org diff --git a/src/common/appstate.cpp b/src/common/appstate.cpp index c0036fdf9..fca9c31b2 100644 --- a/src/common/appstate.cpp +++ b/src/common/appstate.cpp @@ -42,8 +42,6 @@ using util::cStr; using lib::Literal; -#define LOCATION_OF_BOOTSTRAP_INI "$ORIGIN/config/setup.ini" - namespace lumiera { @@ -80,7 +78,7 @@ namespace lumiera { * client codes POV it just behaves like intended). */ AppState::AppState() - : setup_(LOCATION_OF_BOOTSTRAP_INI) + : setup_(LUMIERA_LOCATION_OF_BOOTSTRAP_INI) , subsystems_(0) , emergency_(false) , core_up_ (false) diff --git a/src/common/basic-setup.hpp b/src/common/basic-setup.hpp index d35f25d38..1385dd270 100644 --- a/src/common/basic-setup.hpp +++ b/src/common/basic-setup.hpp @@ -33,6 +33,11 @@ #include +/** "bootstrapIni" : the basic setup configuration to load */ +#define LUMIERA_LOCATION_OF_BOOTSTRAP_INI "$ORIGIN/setup.ini" + + + namespace lumiera { using std::string; From dca5b8171016a1433cf8783fd8327c19834e0bdd Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 15 Feb 2011 00:16:25 +0100 Subject: [PATCH 140/140] Fix: problems whith bare $ORIGIN without relative dir actually the iterator was written quite contrieved; now doing the token replacement as a separate first step -- makes matters way simpler --- src/lib/searchpath.cpp | 20 ++++++++++++++++-- src/lib/searchpath.hpp | 47 ++++++++---------------------------------- 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/src/lib/searchpath.cpp b/src/lib/searchpath.cpp index d328a388b..4e4411ec3 100644 --- a/src/lib/searchpath.cpp +++ b/src/lib/searchpath.cpp @@ -39,7 +39,7 @@ namespace lib { - regex SearchPathSplitter::EXTRACT_PATHSPEC ("(\\$?ORIGIN/?)?([^:]*)"); + const regex SearchPathSplitter::EXTRACT_PATHSPEC ("[^:]+"); /** @internal helper to figure out the installation directory, @@ -62,6 +62,22 @@ namespace lib { } + /** @internal helper to replace all $ORIGIN prefixes in a given string + * by the directory holding the current executable + * @note also picks ORIGIN, $ORIGIN/, ORIGIN/ + */ + string + replaceTokens (string const& src) + { + static const regex PICK_ORIGIN_TOKEN ("\\$?ORIGIN/?"); + static const string expandedOriginDir + = fsys::path (findExePath()).remove_leaf().directory_string(); + + return boost::regex_replace(src, PICK_ORIGIN_TOKEN, expandedOriginDir); + } + + + string @@ -85,7 +101,7 @@ namespace lib { throw error::Config ("Module \""+moduleName+"\" not found" + (searchPath.empty()? ".":" in search path: "+searchPath)); } } - + } // namespace lib diff --git a/src/lib/searchpath.hpp b/src/lib/searchpath.hpp index 64b420903..66b985a5d 100644 --- a/src/lib/searchpath.hpp +++ b/src/lib/searchpath.hpp @@ -53,15 +53,17 @@ namespace lib { /** retrieve the location of the executable */ string findExePath(); + /** replace $ORIGIN tokens in the given string + * @return copy with expansions applied */ + string replaceTokens (string const& src); + /** * Helper: Access a path Specification as a sequence of filesystem Paths. * This iterator class dissects a ':'-separated path list. The individual * components may use the symbol \c $ORIGIN to denote the directory holding - * the current executable. After resolving this symbol, a valid absolute or - * relative filesystem path should result, which must not denote an existing - * file (directory is OK). - * @note #fetch picks the current component and advances the iteration. + * the current executable. + * @note #next picks the current component and advances the iteration. */ class SearchPathSplitter : public BoolCheckablestr(); ++pos_; return currentPathElement; } - - private: - /** maybe resolve a path spec given relative to - * the current Executable location ($ORIGIN) */ - string - resolveRelative () - { - if (containsORIGINToken()) - return asAbsolutePath(); - else - return getFullPath(); - } - - SubMatch found(int group=0) { return (*pos_)[group]; } - - bool containsORIGINToken() { return found(1).matched; } - string getRelativePath() { return found(2); } - string getFullPath() { return found(); } - - string - asAbsolutePath() - { - fsys::path exePathName (findExePath()); - fsys::path modPathName (exePathName.remove_leaf() / getRelativePath()); - - if (fsys::exists(modPathName) && !fsys::is_directory (modPathName)) - throw error::Invalid ("Error in search path: component \""+modPathName.string()+"\" is not a directory" - ,LUMIERA_ERROR_FILE_NOT_DIRECTORY); - - return modPathName.directory_string(); - } };