diff --git a/.gitignore b/.gitignore index e2f73fc4d..5c6dc182c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ *.gch ,valgrind.log* *.pyc +/.sconf_temp +/.settings optcache Makefile.in build/* diff --git a/README b/README index 0e0f938aa..7f7519ea9 100644 --- a/README +++ b/README @@ -66,8 +66,8 @@ 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+ +Hermann Vosseler (aka Ichthyo) maintains a *Debian* packaging of the source tree + +- the package definition can be pulled from +git://git.lumiera.org/lumiera/debian+ +- the package can be built by +git-buildpackage+ diff --git a/SConstruct b/SConstruct index 250936bc7..d2468b374 100644 --- a/SConstruct +++ b/SConstruct @@ -1,6 +1,6 @@ # -*- python -*- ## -## SConstruct - SCons based build-sytem for Lumiera +## SConstruct - SCons based build-system for Lumiera ## # Copyright (C) Lumiera.org @@ -23,422 +23,62 @@ # NOTE: scons -h for help. +# This script /defines/ the components and how they fit together. +# SCons will derive dependencies and the necessary build steps. # 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.pre.01' -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/gui/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' -installUIRes = '#$DESTDIR/share/lumiera/' -installConf = '#$DESTDIR/lib/lumiera/config' - -localDefinitions = locals() -#-----------------------------------Configuration - - - -import os +# SCons plugins and extension modules +#------------------------------------------------ import sys +sys.path.append('./admin/scons') +#------------------------------------------------ -sys.path.append(TOOLDIR) -sys.path.append(SCRIPTDIR) + +import Setup +import Options +import Platform from Buildhelper import * from LumieraEnvironment import * -##################################################################### - -def setupBasicEnvironment(localDefinitions): - """ define cmdline options, build type decisions - """ - EnsurePythonVersion(2,4) - EnsureSConsVersion(1,0) - - Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 - - vars = defineCmdlineVariables() - env = LumieraEnvironment(variables=vars - ,toolpath = [TOOLDIR] - ,pathConfig = extract_localPathDefs(localDefinitions) # e.g. buildExe -> env.path.buildExe - ,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( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root - , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines - , CCFLAGS='-Wall -Wextra ' - , CFLAGS='-std=gnu99' - ) - handleNoBugSwitches(env) - - env.Append(CPPDEFINES = '_GNU_SOURCE') - appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') -# appendCppDefine(env,'OPENGL','USE_OPENGL') - appendVal(env,'ARCHFLAGS','CCFLAGS') # for both C and C++ - appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') - appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') - - # setup search path for Lumiera plugins - 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/:.\\"') - - prepareOptionsHelp(vars,env) - vars.Save(OPTCACHE, env) - return env - -def appendCppDefine(env,var,cppVar, elseVal=''): - if env[var]: - env.Append(CPPDEFINES = env.subst(cppVar) ) - elif elseVal: - env.Append(CPPDEFINES = env.subst(elseVal)) - -def appendVal(env,var,targetVar,val=None): - if env[var]: - env.Append( **{targetVar: env.subst(val) or env[var]}) - - -def handleNoBugSwitches(env): - """ set the build level for NoBug. - Release builds imply no DEBUG - whereas ALPHA and BETA require DEBUG - """ - level = env['BUILDLEVEL'] - if level in ['ALPHA', 'BETA']: - if not env['DEBUG']: - print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!' - env.Replace( DEBUG = 1 ) - env.Append(CPPDEFINES = 'EBUG_'+level) - elif level == 'RELEASE': - env.Replace( DEBUG = 0 ) - -def handleVerboseMessages(env): - """ toggle verbose build output """ - if not env['VERBOSE']: - # SetOption('silent', True) - env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" - env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" - env['LINKCOMSTR'] = " Linking --> $TARGET" - env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" - - - - -def defineCmdlineVariables(): - """ several toggles and configuration variables can be set on the commandline, - current settings will be persisted in a options cache file. - you may define custom variable settings in a separate file. - Commandline will override both. - """ - vars = Variables([OPTCACHE, CUSTOPTFILE]) - vars.AddVariables( - ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') - ,('CC', 'Set the C compiler to use.', 'gcc') - ,('CXX', 'Set the C++ compiler to use.', 'g++') - ,PathVariable('CCACHE', 'Integrate with CCache', '', PathVariable.PathAccept) - ,PathVariable('DISTCC', 'Invoke C/C++ compiler commands through DistCC', '', PathVariable.PathAccept) - ,EnumVariable('BUILDLEVEL', 'NoBug build level for debugging', 'ALPHA', allowed_values=('ALPHA', 'BETA', 'RELEASE')) - ,BoolVariable('DEBUG', 'Build with debugging information and no optimisations', False) - ,BoolVariable('OPTIMIZE', 'Build with strong optimisation (-O3)', False) - ,BoolVariable('VALGRIND', 'Run Testsuite under valgrind control', True) - ,BoolVariable('VERBOSE', 'Print full build commands', False) - ,('TESTSUITES', 'Run only Testsuites matching the given pattern', '') -# ,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('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) - ) - - return vars - - - -def prepareOptionsHelp(vars,env): - prelude = """ -USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] - Build and optionally install Lumiera. - Without specifying any target, just the (re)build target will run. - Add -c to the commandline to clean up anything a given target would produce - -Special Targets: - build : just compile and link - 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 - -Configuration Options: -""" - Help(prelude + vars.GenerateHelpText(env)) - - - - -def configurePlatform(env): - """ locate required libs. - setup platform specific options. - Abort build in case of failure. - """ - conf = env.Configure() - # run all configuration checks in the given env - - # Perform checks for prerequisites -------------------------------------------- - problems = [] - if not conf.TryAction('pkg-config --version > $TARGET')[0]: - problems.append('We need pkg-config for including library configurations, exiting.') - - if not conf.CheckLibWithHeader('m', 'math.h','C'): - problems.append('Did not find math.h / libm.') - - if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): - problems.append('Functions for runtime dynamic loading not available.') - - if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): - problems.append('Did not find the pthread lib or pthread.h.') - else: - conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') - conf.env.Append(CCFLAGS = ' -pthread') - - if conf.CheckCHeader('execinfo.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') - - if conf.CheckCHeader('valgrind/valgrind.h'): - conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') - else: - print 'Valgrind not found. The use of Valgrind is optional; building without.' - - 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') - - if not conf.CheckCXXHeader('tr1/memory'): - 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-libraries.') - else: - 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).') - - - if conf.CheckLib(symbol='clock_gettime'): - print 'Using function clock_gettime() as defined in the C-lib...' - else: - if not conf.CheckLib(symbol='clock_gettime', library='rt'): - problems.append('No library known to provide the clock_gettime() function.') - - if not conf.CheckPkgConfig('gavl', 1.0): - problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') - else: - conf.env.mergeConf('gavl') - - if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): - problems.append('Unable to configure GTK--') - - if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): - 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--') - - verGDL = '2.27.1' - if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): - print 'No sufficiently recent (>=%s) version of GDL found. Maybe use custom package gdl-lum?' % verGDL - if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): - problems.append('GNOME Docking Library not found. We either need a sufficiently recent GDL ' - 'version (>=%s), or the custom package "gdl-lum" from Lumiera.org.' % verGDL) - - if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): - problems.append('Need rsvg Library for rendering icons.') - - if not conf.CheckCHeader(['X11/Xutil.h', 'X11/Xlib.h'],'<>'): - problems.append('Xlib.h and Xutil.h required. Please install libx11-dev.') - - if not conf.CheckPkgConfig('xv') : problems.append('Need libXv...') - if not conf.CheckPkgConfig('xext'): problems.append('Need libXext.') - - - # report missing dependencies - if problems: - print "*** unable to build due to the following problems:" - for isue in problems: - print " * %s" % isue - print - print "build aborted." - Exit(1) - - print "** Gathered Library Info: %s" % conf.env.libInfo.keys() - - - # create new env containing the finished configuration - return conf.Finish() - - - -def defineSetupTargets(env, artifacts): - """ build operations and targets to be done /before/ compiling. - things like creating a source tarball or preparing a version header. - """ - pass ## currently none - - - -def defineBuildTargets(env, artifacts): - """ define the source file/dirs comprising each artifact to be built. - setup sub-environments with special build options if necessary. - We use a custom function to declare a whole tree of srcfiles. - """ - - # use PCH to speed up building // disabled for now due to strange failures -# env['GCH'] = ( env.PrecompiledHeader('src/pre.hpp') -# + env.PrecompiledHeader('src/pre_a.hpp') -# ) - - - - 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['config'] = ( env.ConfigData(env.path.srcConf+'setup.ini', targetDir='$ORIGIN') - + env.ConfigData(env.path.srcConf+'dummy_lumiera.ini') - ) - artifacts['lumiera'] = ( env.Program('lumiera', ['src/lumiera/main.cpp'] + core, install=True) - + artifacts['config'] - ) - - # building Lumiera Plugins - 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.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] - ) - - # the Lumiera GTK GUI - envGtk = env.Clone() - envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm']) - envGtk.Append(LIBS=core) - - 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')] - + artifacts['icons'] - ) - - # call subdir SConscript(s) for independent components - SConscript(dirs=['src/tool'], exports='env artifacts core') - SConscript(dirs=['tests'], exports='env artifacts core') - - - -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['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' ]) - - doxydoc = artifacts['doxydoc'] = env.Doxygen('doc/devel/Doxyfile') - env.Alias ('doc', doxydoc) - env.Clean ('doc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt']) - - env.Alias ('all', build+artifacts['testsuite']+doxydoc) - env.Default('build') - # SCons default target - - -def defineInstallTargets(env, artifacts): - """ 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 - """ - 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') ##################################################################### +env = Setup.defineBuildEnvironment() # dirs & compiler flags +env = Platform.configure(env) # library dependencies + + + +### === MAIN BUILD === ############################################## + +# call subdir SConscript(s) to define the actual build targets... +SConscript(dirs=['data','src','src/tool','research','tests','doc'], exports='env') -### === MAIN === #################################################### +# 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' ]) -env = setupBasicEnvironment(localDefinitions) -if not (isCleanupOperation(env) or isHelpRequest()): - env = configurePlatform(env) - -artifacts = {} -# the various things we build. -# Each entry actually is a SCons-Node list. -# Passing these entries to other builders defines dependencies. -# 'lumiera' : the App -# 'gui' : the GTK UI (plugin) -# 'plugins' : plugin shared lib -# 'tools' : small tool applications (e.g mpegtoc) -defineSetupTargets(env, artifacts) -defineBuildTargets(env, artifacts) -definePostBuildTargets(env, artifacts) -defineInstallTargets(env, artifacts) +### === Alias Targets === ########################################### +# pick up the targets defined by the sub SConscripts +Import('lumiera plugins tools gui testsuite doxydoc') + +build = env.Alias('build', lumiera + plugins + tools +gui) +env.Default('build') +# SCons default target + + +env.Alias ('all', build + testsuite + doxydoc) +env.Alias ('doc', doxydoc) + +env.Alias('install', gui) +env.Alias('install', '$DESTDIR') + +##################################################################### diff --git a/admin/render_icon.py b/admin/render_icon.py deleted file mode 100755 index 63bacbf37..000000000 --- a/admin/render_icon.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/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:" - -# -# 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): - os.remove(name) - if not os.path.exists(name): - os.mkdir(name) - except: - print 'WARNING: createDirectory("%s") failed. Permission problems?' % 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 not (in_path and out_dir): - print "Missing arguments in_path and out_dir." - sys.exit(1) - - if os.path.isfile(out_dir): - print "Unable to use '%s' as output directory, because it\'s a file." % out_dir - sys.exit(1) - if not os.path.isdir(out_dir): - print "Output directory '%s' not found." % out_dir - sys.exit(1) - - # 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/rfc.sh b/admin/rfc.sh index ce0e4d0ea..43fc36132 100755 --- a/admin/rfc.sh +++ b/admin/rfc.sh @@ -293,7 +293,7 @@ function add_comment() { local name="$1" local nl=$'\n' - local comment="//edit comment$nl $(date +%c) $(git config --get user.name) <$(git config --get user.email)>$nl" + local comment="//edit comment$nl$nl$(git config --get user.name):: '$(date +%c)' ~<$(git config --get user.email)>~$nl" ed "$name" 2>/dev/null < +# +# 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 os +import sys +import getopt +import shutil +from xml.dom import minidom + + +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 Cairo based SVG rendering 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): + os.remove (name) + if not os.path.exists (name): + os.mkdir (name) + except: + print 'WARNING: createDirectory("%s") failed. Permission problems?' % 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 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], width, height), + "--output=" + os.path.join(out_dir, "%gx%g/%s.png" % (width, height, 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 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 not (in_path and out_dir): + print "Missing arguments in_path and out_dir." + sys.exit(1) + + if os.path.isfile(out_dir): + print "Unable to use '%s' as output directory, because it\'s a file." % out_dir + sys.exit(1) + if not os.path.isdir(out_dir): + print "Output directory '%s' not found." % out_dir + sys.exit(1) + + # 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) + + + + +if __name__=="__main__": + main(sys.argv[1:]) + diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py index c0ce77375..8d2d8db43 100644 --- a/admin/scons/LumieraEnvironment.py +++ b/admin/scons/LumieraEnvironment.py @@ -22,11 +22,10 @@ ##################################################################### -import os from os import path -import SCons import SCons.SConf +from SCons.Action import Action from SCons.Environment import Environment from Buildhelper import * @@ -38,9 +37,15 @@ class LumieraEnvironment(Environment): This allows us to carry structured config data without using global vars. Idea inspired by Ardour. """ - def __init__(self, pathConfig, **kw): - Environment.__init__ (self,**kw) - self.path = Record (pathConfig) + def __init__(self, buildSetup, buildVars, **kw): + kw.update(VERSION = buildSetup.VERSION + ,TARGDIR = buildSetup.TARGDIR + ,DESTDIR = '$INSTALLDIR/$PREFIX' + ,toolpath = [buildSetup.TOOLDIR ] + ,variables = buildVars + ) + Environment.__init__ (self, **kw) + self.path = Record (extract_localPathDefs(buildSetup)) # e.g. buildExe -> env.path.buildExe self.libInfo = {} self.Tool("BuilderGCH") self.Tool("BuilderDoxygen") @@ -49,6 +54,7 @@ class LumieraEnvironment(Environment): register_LumieraResourceBuilder(self) register_LumieraCustomBuilders(self) + def Configure (self, *args, **kw): kw['env'] = self return apply(LumieraConfigContext, args, kw) @@ -123,7 +129,7 @@ def register_LumieraResourceBuilder(env): 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) + import IconSvgRenderer as renderer # load Joel's python script for invoking the rsvg-convert (SVG render) renderer.rsvgPath = env.subst("$TARGDIR/rsvg-convert") def invokeRenderer(target, source, env): @@ -151,43 +157,44 @@ def register_LumieraResourceBuilder(env): return (generateTargets, 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) + """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) + 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, 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 - 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) + def ConfigData(env, prefix, 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 + """ + source = path.join(prefix,str(source)) + subdir = getDirname(source, prefix) # removes source location path prefix + 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) buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS") @@ -205,8 +212,8 @@ def register_LumieraResourceBuilder(env): 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. + wrapped object by Python magic. But some calls are intercepted in order to inject + suitable default configuration based on the project setup. """ def __init__(self, originalBuilder): @@ -291,7 +298,7 @@ class LumieraModuleBuilder(WrappedStandardExeBuilder): explicit spec, falling back on the lib filename """ if 'soname' in kw: - soname = self.subst(kw['soname']) # explicitely defined by user + soname = self.subst(kw['soname']) # explicitly defined by user else: # else: use the library filename as DT_SONAME if SCons.Util.is_String(target): pathname = target.strip() @@ -331,7 +338,7 @@ class LumieraPluginBuilder(LumieraModuleBuilder): def register_LumieraCustomBuilders (lumiEnv): - """ install the customised builder versions tightly integrated with our buildsystem. + """ install the customised builder versions tightly integrated with our build system. 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 """ @@ -362,7 +369,7 @@ def register_LumieraCustomBuilders (lumiEnv): action = Action(makeLink, "Install link: $TARGET -> "+srcSpec) env.Command (target,source, action) - # adding SymLink direclty as method on the environment object + # adding SymLink directly 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 diff --git a/admin/scons/Options.py b/admin/scons/Options.py new file mode 100644 index 000000000..bbccb20bd --- /dev/null +++ b/admin/scons/Options.py @@ -0,0 +1,81 @@ +# -*- python -*- +## +## Options.py - SCons build: command line options and help +## + +# Copyright (C) Lumiera.org +# 2012, 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. +##################################################################### + + +from SCons.Script import PathVariable, EnumVariable, BoolVariable, Help + + + + + +def defineCmdlineVariables(buildVars): + """ several toggles and configuration variables can be set on the commandline, + current settings will be persisted in a options cache file. + you may define custom variable settings in a separate file. + Commandline will override both. + """ + buildVars.AddVariables( + ('ARCHFLAGS', 'Set architecture-specific compilation flags (passed literally to gcc)','') + ,('CC', 'Set the C compiler to use.', 'gcc') + ,('CXX', 'Set the C++ compiler to use.', 'g++') + ,PathVariable('CCACHE', 'Integrate with CCache', '', PathVariable.PathAccept) + ,PathVariable('DISTCC', 'Invoke C/C++ compiler commands through DistCC', '', PathVariable.PathAccept) + ,EnumVariable('BUILDLEVEL', 'NoBug build level for debugging', 'ALPHA', allowed_values=('ALPHA', 'BETA', 'RELEASE')) + ,BoolVariable('DEBUG', 'Build with debugging information and no optimisations', False) + ,BoolVariable('OPTIMIZE', 'Build with strong optimisation (-O3)', False) + ,BoolVariable('VALGRIND', 'Run Testsuite under valgrind control', True) + ,BoolVariable('VERBOSE', 'Print full build commands', False) + ,('TESTSUITES', 'Run only Testsuites matching the given pattern', '') +# ,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('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) + ) + + + +def prepareOptionsHelp(buildVars,env): + prelude = """ +USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] + Build and optionally install Lumiera. + Without specifying any target, just the (re)build target will run. + Add -c to the commandline to clean up anything a given target would produce + +Special Targets: + build : just compile and link + research: build experimental code (might fail) + 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 + +Configuration Options: +""" + Help(prelude + buildVars.GenerateHelpText(env)) + + + diff --git a/admin/scons/Platform.py b/admin/scons/Platform.py new file mode 100644 index 000000000..3aee76abd --- /dev/null +++ b/admin/scons/Platform.py @@ -0,0 +1,149 @@ +# -*- python -*- +## +## Platform.py - SCons build: platform configuration and library detection +## + +# Copyright (C) Lumiera.org +# 2012, 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. +##################################################################### + + +from SCons.Script import Exit +from Buildhelper import isCleanupOperation, isHelpRequest + + + + +def configure(env): + """ locate required libraries. + setup platform specific options. + Abort build in case of failure. + """ + if isCleanupOperation(env) or isHelpRequest(): + return env # skip configure in these cases + + conf = env.Configure() + # run all configuration checks in the build environment defined thus far + + # Perform checks for prerequisites -------------------------------------------- + problems = [] + if not conf.TryAction('pkg-config --version > $TARGET')[0]: + problems.append('We need pkg-config for including library configurations, exiting.') + + if not conf.CheckLibWithHeader('m', 'math.h','C'): + problems.append('Did not find math.h / libm.') + + if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): + problems.append('Functions for runtime dynamic loading not available.') + + if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): + problems.append('Did not find the pthread lib or pthread.h.') + else: + conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') + conf.env.Append(CCFLAGS = ' -pthread') + + if conf.CheckCHeader('execinfo.h'): + conf.env.Append(CPPFLAGS = ' -DHAVE_EXECINFO_H') + + if conf.CheckCHeader('valgrind/valgrind.h'): + conf.env.Append(CPPFLAGS = ' -DHAVE_VALGRIND_H') + else: + print 'Valgrind not found. The use of Valgrind is optional; building without.' + + 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') + + if not conf.CheckCXXHeader('tr1/memory'): + 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-libraries.') + else: + 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_system-mt','boost/system/error_code.hpp','C++'): + problems.append('We need the boost::system support library (including binary lib).') + if not conf.CheckLibWithHeader('boost_filesystem-mt','boost/filesystem.hpp','C++'): + problems.append('We need the boost::filesystem lib (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).') + + + if conf.CheckLib(symbol='clock_gettime'): + print 'Using function clock_gettime() as defined in the C-lib...' + else: + if not conf.CheckLib(symbol='clock_gettime', library='rt'): + problems.append('No library known to provide the clock_gettime() function.') + + if not conf.CheckPkgConfig('gavl', 1.0): + problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].') + else: + conf.env.mergeConf('gavl') + + if not conf.CheckPkgConfig('alsa', '1.0.23'): + problems.append('Support for ALSA sound output is required') + + if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): + problems.append('Unable to configure GTK--') + + if not conf.CheckPkgConfig('glibmm-2.4', '2.16'): + 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--') + + verGDL = '2.27.1' + if not conf.CheckPkgConfig('gdl-1.0', verGDL, alias='gdl'): + print 'No sufficiently recent (>=%s) version of GDL found. Maybe use custom package gdl-lum?' % verGDL + if not conf.CheckPkgConfig('gdl-lum', verGDL, alias='gdl'): + problems.append('GNOME Docking Library not found. We either need a sufficiently recent GDL ' + 'version (>=%s), or the custom package "gdl-lum" from Lumiera.org.' % verGDL) + + if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'): + problems.append('Need rsvg Library for rendering icons.') + + if not conf.CheckCHeader(['X11/Xutil.h', 'X11/Xlib.h'],'<>'): + problems.append('Xlib.h and Xutil.h required. Please install libx11-dev.') + + if not conf.CheckPkgConfig('xv') : problems.append('Need libXv...') + if not conf.CheckPkgConfig('xext'): problems.append('Need libXext.') + + + # report missing dependencies + if problems: + print "*** unable to build due to the following problems:" + for isue in problems: + print " * %s" % isue + print + print "build aborted." + Exit(1) + + print "** Gathered Library Info: %s" % conf.env.libInfo.keys() + + + # create new env containing the finished configuration + return conf.Finish() + diff --git a/admin/scons/Setup.py b/admin/scons/Setup.py new file mode 100644 index 000000000..5a064381d --- /dev/null +++ b/admin/scons/Setup.py @@ -0,0 +1,137 @@ +# -*- python -*- +## +## Setup.py - SCons build: setup, definitions and compiler flags +## + +# Copyright (C) Lumiera.org +# 2012, 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. +##################################################################### + +from SCons.Script import EnsurePythonVersion, EnsureSConsVersion, Variables, Decider + +from LumieraEnvironment import * +from Buildhelper import * +import Options + + + +#-------------------------------------------------------Configuration +TARGDIR = 'target' +VERSION = '0.pre.01' +TOOLDIR = './admin/scons' # SCons plugins +OPTCACHE = 'optcache' +CUSTOPTFILE = 'custom-options' + +# these are accessible via env.path.xxxx +buildExe = '#$TARGDIR' +buildLib = '#$TARGDIR/modules' +buildPlug = '#$TARGDIR/modules' +buildIcon = '#$TARGDIR/gui/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' +installUIRes = '#$DESTDIR/share/lumiera/' +installConf = '#$DESTDIR/lib/lumiera/config' + +#-------------------------------------------------------Configuration +buildSetup = Record(locals()) + + + + + +def defineBuildEnvironment(): + """ create a custom build environment, + define the basic compiler and linker flags, + define locations in source and target tree, + parse the commandline and pick up options + """ + EnsureSConsVersion(1,0) + EnsurePythonVersion(2,4) + Decider('MD5-timestamp') # detect changed files by timestamp, then do a MD5 + + buildVars = Variables([OPTCACHE, CUSTOPTFILE]) + Options.defineCmdlineVariables(buildVars) + env = LumieraEnvironment(buildSetup, buildVars) + + env.Replace( CPPPATH =["#src"] # used to find includes, "#" means always absolute to build-root + , CPPDEFINES=['LUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines + , CCFLAGS='-Wall -Wextra ' + , CFLAGS='-std=gnu99' + ) + handleVerboseMessages(env) + handleNoBugSwitches(env) + + env.Append(CPPDEFINES = '_GNU_SOURCE') + appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') +# appendCppDefine(env,'OPENGL','USE_OPENGL') + appendVal(env,'ARCHFLAGS','CCFLAGS') # for both C and C++ + appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') + appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') + + # setup search path for Lumiera plugins + 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/:.\\"') + + Options.prepareOptionsHelp(buildVars,env) + buildVars.Save(OPTCACHE, env) + return env + + + +def appendCppDefine(env,var,cppVar, elseVal=''): + if env[var]: + env.Append(CPPDEFINES = env.subst(cppVar) ) + elif elseVal: + env.Append(CPPDEFINES = env.subst(elseVal)) + +def appendVal(env,var,targetVar,val=None): + if env[var]: + env.Append( **{targetVar: env.subst(val) or env[var]}) + + +def handleNoBugSwitches(env): + """ set the build level for NoBug. + Release builds imply no DEBUG + whereas ALPHA and BETA require DEBUG + """ + level = env['BUILDLEVEL'] + if level in ['ALPHA', 'BETA']: + if not env['DEBUG']: + print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!' + env.Replace( DEBUG = 1 ) + env.Append(CPPDEFINES = 'EBUG_'+level) + elif level == 'RELEASE': + env.Replace( DEBUG = 0 ) + + +def handleVerboseMessages(env): + """ toggle verbose build output """ + if not env['VERBOSE']: + # SetOption('silent', True) + env['CCCOMSTR'] = env['SHCCCOMSTR'] = " Compiling $SOURCE" + env['CXXCOMSTR'] = env['SHCXXCOMSTR'] = " Compiling++ $SOURCE" + env['LINKCOMSTR'] = " Linking --> $TARGET" + env['LDMODULECOMSTR'] = " creating module [ $TARGET ]" + + + diff --git a/data/SConscript b/data/SConscript new file mode 100644 index 000000000..d2407b502 --- /dev/null +++ b/data/SConscript @@ -0,0 +1,25 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for Icons and Resources +## + +from Buildhelper import scanSubtree + +Import('env') + + +# define Icons to render and install +vector_icon_dir = 'icons/svg' +prerendered_icon_dir = 'icons/prerendered' + +icons = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir, ['*.svg'])] + + [env.IconResource(f) for f in scanSubtree(prerendered_icon_dir, ['*.png'])] + ) + +#define Configuration files to install (dir-prefix, name) +config = ( env.ConfigData('config','setup.ini', targetDir='$ORIGIN') + + env.ConfigData('config','dummy_lumiera.ini') + ) + + +Export('icons config') diff --git a/data/icons/README b/data/icons/README new file mode 100644 index 000000000..b56ffe9f2 --- /dev/null +++ b/data/icons/README @@ -0,0 +1,26 @@ +# +# Lumiera Icon Artwork +# +# Copyright (C) Lumiera.org +# 2008, Joel Holdsworth +# +# Icon Artwork and similar materials accompanying the Lumiera Application +# is dual-licensed under the GNU General Public License version 2 or above, +# and +# Creative Commons Attribution-ShareAlike 3.0 Unported License +# +# + + +This directory holds Icons and similar graphics resources for the Lumiera GUI. + +- prerendered: Raster image Icons rendered into a selection of sizes +- svg: Scalable Vector Grahpics Icons, to be rendered into suitable sizes + by the build process. Rendering is done with the help of lib Cairo. + The build creates an executable from src/tools/rsvg-convert.c + The invocation of the icon rendering is done with the help of a + Python script IconSvgRenderer.py, which first parses the SVG document + and then invokes rsvg-convert to generate the raster images. + + + diff --git a/icons/prerendered/16x16/panel-assets.png b/data/icons/prerendered/16x16/panel-assets.png similarity index 100% rename from icons/prerendered/16x16/panel-assets.png rename to data/icons/prerendered/16x16/panel-assets.png diff --git a/icons/prerendered/16x16/panel-timeline.png b/data/icons/prerendered/16x16/panel-timeline.png similarity index 100% rename from icons/prerendered/16x16/panel-timeline.png rename to data/icons/prerendered/16x16/panel-timeline.png diff --git a/icons/prerendered/16x16/panel-viewer.png b/data/icons/prerendered/16x16/panel-viewer.png similarity index 100% rename from icons/prerendered/16x16/panel-viewer.png rename to data/icons/prerendered/16x16/panel-viewer.png diff --git a/icons/prerendered/22x22/panel-assets.png b/data/icons/prerendered/22x22/panel-assets.png similarity index 100% rename from icons/prerendered/22x22/panel-assets.png rename to data/icons/prerendered/22x22/panel-assets.png diff --git a/icons/prerendered/22x22/panel-viewer.png b/data/icons/prerendered/22x22/panel-viewer.png similarity index 100% rename from icons/prerendered/22x22/panel-viewer.png rename to data/icons/prerendered/22x22/panel-viewer.png diff --git a/icons/prerendered/32x32/panel-assets.png b/data/icons/prerendered/32x32/panel-assets.png similarity index 100% rename from icons/prerendered/32x32/panel-assets.png rename to data/icons/prerendered/32x32/panel-assets.png diff --git a/icons/prerendered/32x32/panel-viewer.png b/data/icons/prerendered/32x32/panel-viewer.png similarity index 100% rename from icons/prerendered/32x32/panel-viewer.png rename to data/icons/prerendered/32x32/panel-viewer.png diff --git a/icons/svg/app-icon.svg b/data/icons/svg/app-icon.svg similarity index 100% rename from icons/svg/app-icon.svg rename to data/icons/svg/app-icon.svg diff --git a/icons/svg/tool-arrow.svg b/data/icons/svg/tool-arrow.svg similarity index 100% rename from icons/svg/tool-arrow.svg rename to data/icons/svg/tool-arrow.svg diff --git a/icons/svg/tool-i-beam.svg b/data/icons/svg/tool-i-beam.svg similarity index 100% rename from icons/svg/tool-i-beam.svg rename to data/icons/svg/tool-i-beam.svg diff --git a/icons/svg/track-disabled.svg b/data/icons/svg/track-disabled.svg similarity index 100% rename from icons/svg/track-disabled.svg rename to data/icons/svg/track-disabled.svg diff --git a/icons/svg/track-enabled.svg b/data/icons/svg/track-enabled.svg similarity index 100% rename from icons/svg/track-enabled.svg rename to data/icons/svg/track-enabled.svg diff --git a/icons/svg/track-locked.svg b/data/icons/svg/track-locked.svg similarity index 100% rename from icons/svg/track-locked.svg rename to data/icons/svg/track-locked.svg diff --git a/icons/svg/track-unlocked.svg b/data/icons/svg/track-unlocked.svg similarity index 100% rename from icons/svg/track-unlocked.svg rename to data/icons/svg/track-unlocked.svg diff --git a/icons/timeline-panel.svg b/data/icons/timeline-panel.svg similarity index 100% rename from icons/timeline-panel.svg rename to data/icons/timeline-panel.svg diff --git a/doc/SConscript b/doc/SConscript new file mode 100644 index 000000000..60c0964ab --- /dev/null +++ b/doc/SConscript @@ -0,0 +1,17 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for Documentation +## + +from Buildhelper import scanSubtree + +Import('env') + + +doxydoc = env.Doxygen('devel/Doxyfile') + +# env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=documentation) +env.Clean (doxydoc, doxydoc + ['devel/,doxylog','devel/warnings.txt']) + + +Export('doxydoc') diff --git a/doc/design/architecture/playRender.txt b/doc/design/architecture/playRender.txt index afcf2b61f..2d76f716e 100644 --- a/doc/design/architecture/playRender.txt +++ b/doc/design/architecture/playRender.txt @@ -70,8 +70,8 @@ proceeding along a timeline). Reconfiguration ^^^^^^^^^^^^^^^ -Some of these operation modes need to be prepared to an unpredictable live reconfiguration, -driven by user interactions: +Some of these operation modes need to be prepared to encounter an unpredictable live +reconfiguration, driven by user interactions: - any part of background rendering can be invalidated and restarted, while other parts should be re-integrated, possibly with adjusted position @@ -108,7 +108,7 @@ playback location, and it can be hooked up with a play-control GUI widget Each play-controller in turn gets associated with several *play/render-processes*, one for each independent media stream (channel) to be produced. Of course this -isn't an operating system process; rather, ach such process is a compound of entries +isn't an operating system process; rather, each such process is a compound of entries in a registration table, which serve the purpose of tying several other services together, which we initiate and use in order to make that render process happen. Most notably, we'll use the services of the actual engine, which provides us with kind of @@ -126,7 +126,7 @@ Viewer and Output connection ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Creating a player instance binds together three partners: a _timeline_, a _viewer_ and _the engine_. While the timeline provides the content to play, the _viewer connection_ -is crutial for working out the actual output sink(s) and thus the output format to use. +is crucial for working out the actual output sink(s) and thus the output format to use. Thus, a viewer connection is prerequisite for creating a player instance. Viewer connections exist as associations in the session/model -- as entities separate @@ -135,13 +135,13 @@ such a connection is (still) missing, building a player instance recurs to the s to get a suitable viewer _allocated_. The viewer connection can't be broken during the lifetime of that player instance (or putting it the other way: breaking that viewer connection, e.g. by forcing a different connection or by shutting down the viewer, -immediately terminates the player. This detaching works synchroneously, i.e. it -blocks untlil all the allocated _output slots_ could be released. +immediately terminates the player. This detaching works synchronously, i.e. it +blocks until all the allocated _output slots_ could be released. Live switching ^^^^^^^^^^^^^^ While the viewer connection can be treated as fixed during the lifespan of a player -instance, several life switching and reconfiguration operations might happen anytime: +instance, several life switching and reconfiguration operations might happen any time: The _model port_ (place where data is retrieved from calculation), the output characteristics (framerate, direction) and the delivery goals (playback position, loop playing, scrubbing) all may be changed during playback -- we need a way for the player to ``cancel'' and @@ -164,7 +164,7 @@ the quantisation, which leaves us with just a few possible junction points where to place quantisation: The backend, the GUI, the player, the session. - putting it into the backend seems to be the most reasonable at first sight: - We can ``do away'' with nasty things soon, especially if they are technicallities, + We can ``do away'' with nasty things soon, especially if they are technicalities, ``get a clean state soon'' -- and hasn't frame quantisation something to do with media data, which is handled in the backend? + @@ -174,7 +174,7 @@ amount of degraded information flows throughout the whole system; thus the general rule to do it as late as possible. Uncrippled information is enablement. And last but not least: the frame quantisation is connected to the _output_ format -- and the backend is likely within the whole -application the subsytem most remote and unaware of output requirements. +application the subsystem most remote and unaware of output requirements. - rounding/quantising in the GUI is extremely common within media applications; unfortunately there seems to be not a single rational argument supporting that habit. @@ -184,7 +184,7 @@ Which leaves us with the player and the session. Both positions could arguably be supported. Here, a more careful consideration shows, that the ``act of frame rounding'' can be decomposed: into the _act of quantisation_ and the _frame grid:. Basically its the session which has the ability -to form the *frame grid*, but it is lacking crutial information about +to form the *frame grid*, but it is lacking crucial information about the output. Only when connecting both -- which is the essence of the player -- frame quantisation can actually be performed. Thus, the player is the natural location to perform that quantisation operation. diff --git a/doc/design/backend/Scheduler.txt b/doc/design/backend/Scheduler.txt new file mode 100644 index 000000000..49793e06f --- /dev/null +++ b/doc/design/backend/Scheduler.txt @@ -0,0 +1,25 @@ +The Scheduler +------------- +:Author: CehTeh +:Date: 6/2007 + +//MENU: label Scheduler + +Scheduling is done with two priority queues, one for high priority jobs and one for low priority jobs. +These priority queues are ordered by absolute time values plus some job specific identified. + +There are following (non exhaustive) kinds of jobs: + +* started job +* job to be canceled +* unscheduled job +* dependency providing jobs + +Jobs implement a kind of future. We try hard to avoid any blocking waits. +The Job scheduler runs singlethreaded. Its only task is to schedule and delegate jobs to worker threads, +by itself it will never do any extensive processing. + +Each job has an pre configured behaviour for the case of failure or deadline miss. +Any canceling and expireing jobs gets noted in *Statistics* to adjust performance and timings +for optimal performance and I/O throughput. + diff --git a/doc/design/backend/index.txt b/doc/design/backend/index.txt index 44c57ca8b..19de56002 100644 --- a/doc/design/backend/index.txt +++ b/doc/design/backend/index.txt @@ -1,5 +1,114 @@ Design Documents: Backend ========================= -Eventually, this will have design documentation for the Backend. +What follows is a summary regarding the design of Lumiera's *Data Handling Backend* + +This is the foundation layer responsible for any high performance or high volume +data access. Within Lumiera, ther are two main kinds how data is handled: + +* The Session and the object models manipulated through the GUI is kept in memory. + It is backed by a _storage backend,_ which provides database like storage and + especially logging, replaying and ``Undo'' of all ongoing modifications.. +* Media data is handled _frame wise_ -- as described below. + +The backend uses *memory mapping* to make data available to the program. +This is somewhat different to the more common open/read/write/close file access, +while giving superior performance and much better memory utilization. +The data backend must be capable to handle more data than will fit into the memory +or even address space on 32 bit architectures. Moreover, a project might access more files +than the OS can handle at a any time, thus the for _Files used by the Backend,_ it needs a +*FilehandleCache* to manage file handle dynamically. + +Which parts of a file are actually mapped to physical RAM is managed by the kernel; +it keeps a *FileMapCache* to manage the *FileMaps* we've set up. +In the End, the application itself only requests *Data Frames* from the Backend. + +To minimize latency and optimize CPU utilization we have a *Prefetch thread* which operates +a *Scheduler* to render and cache frames which are _expected to be consumed soon_. The intention +is to manage the rendering _just in time_. + +The prefetcher keeps *Statistics* for optimizing performance. + + +Accessing Files +--------------- + ++FileDescriptor+ is the superclass of all possible filetypes, it has a weak reference to a ++FileHandle+ which is managed in within the +FilehandleCache+. On creation, only the existence +(when reading) or access for write for new files are checked. The +FileDescriptor+ stores some +generic metadata about the underlying file and intended use. But the actual opening is done on demand. + +The _content of files is memory mapped_ into the process address space. +This is managed by +FileMap+ entries and a +FileMapCache+. + +File Handles +~~~~~~~~~~~~ +A +FilehandleCache+ serves to store a finite maximum number of +FileHandles+ as a MRU list. +FileHandles are managed by the +FilehandleCache+; basically they are just storing the underlying OS file +handles and managed in a lazy/weak way, (re)opened when needed and aging in the cache when not needed, +since the amount of open file handles is limited aged ones will be closed and reused when the system +needs to open another file. + +File Mapping +~~~~~~~~~~~~ +The +FileMapCache+ keeps a list of +FileMaps+, which are currently not in use and subject of aging. +Each +FileMap+ object contains many +Frames+. The actual layout depends on the type of the File. +Mappings need to be _page aligned_ while Frames can be anywhere within a file and dynamically sized. + +All established ++FileMap++s are managed together in a central +FileMapCache+. +Actually, +FileMap+ objects are transparent to the application. The upper layers will just +request Frames by position and size. Thus, the +File+ entities associate a filename with the underlying +low level File Descriptor and access + +Frames +~~~~~~ ++Frames+ are the smallest datablocks handled by the Backend. The application tells the Backend to make +Files available and from then on just requests Frames. Actually, those Frames are (references to) blocks +of continuous memory. They can be anything depending on the usage of the File (Video frames, encoder frames, +blocks of sound samples). Frames are referenced by a smart-pointer like object which manages the lifetime +and caching behavior. + +Each frame referece can be in one out of three states: + +readonly:: + the backing +FileMap+ is checked out from the aging list, frames can be read + +readwrite:: + the backing +FileMap+ is checked out from the aging list, frames can be read and written + +weak:: + the +FileMap+ object is checked back into the aging list, the frame can't be accessed but we can + try to transform a weak reference into a readonly or readwrite reference + + +Frames can be addressed uniquely whenever a frame is not available. The backend can't serve a cached +version of the frame, a (probably recursive) rendering request will be issued. + +Prefetching +~~~~~~~~~~~ +There are 2 important points when we want to access data with low latency: + +. Since we handle much more data than it will fit into most computers RAM. + The data which is backed in files has to be paged in and available when needed. + The +Prefetch+ Thread manages page hinting to the kernel (posix_madvise()..) +. Intermediate Frames must eventually be rendered to the cache. + The Backend will send +Renderjobs+ to the +Scheduler+. + +Whenever something queries a +Frame+ from the backend it provides hints about what it is doing. +These hints contain: + +* Timing constraints + - When will the +Frame+ be needed + - could we drop the request if it won't be available (rendered) in-time +* Priority of this job (as soon as possible, or just in time?) +* action (Playing forward, playing backward, tweaking, playback speed, recursive rendering of dependent frames) + +.Notes +* The Backend will try to render related frames in groups. +* This means that following frames are scheduled with lower priority. +* Whenever the program really requests them the priority will be adjusted. + + +-> more about link:Scheduler.html[the Scheduling of calculation jobs] + diff --git a/doc/design/engine/OutputHandling.txt b/doc/design/engine/OutputHandling.txt new file mode 100644 index 000000000..fa3d5e268 --- /dev/null +++ b/doc/design/engine/OutputHandling.txt @@ -0,0 +1,15 @@ +Design: Output Handling +======================= +:Date: June 2011 +:Author: Ichthyostega + +//Menu: label Output handling + +Some ideas.... + +- abstract away the actual technology used for output +- have generic *output designations* and translate them into an *output slot* +- the OutputSlot interface can be designed to match the requirements of the Engine +- assume a mechanism to handle timeouts, glitches and skips within each concrete OutputSlot implementation + + diff --git a/doc/design/engine/index.txt b/doc/design/engine/index.txt index 7d9b5ea39..0852b6bac 100644 --- a/doc/design/engine/index.txt +++ b/doc/design/engine/index.txt @@ -1,5 +1,6 @@ Design Documents: Renderengine ============================== -Eventually, this will have design documentation for the Engine. +This section contains design documents regarding the overall workings of the Render Engine, +and the handling of output generation and output connections. diff --git a/doc/design/plugins/PluginBrainstorm.txt b/doc/design/plugins/PluginBrainstorm.txt index 0d07ca97e..b145cdf08 100644 --- a/doc/design/plugins/PluginBrainstorm.txt +++ b/doc/design/plugins/PluginBrainstorm.txt @@ -3,12 +3,81 @@ Plugin Brainstorm :Author: Christian Thäter :Date: 2008-09-15 -Raw Version ------------ +Lumiera will use a very simple and language neutral plugin system. The focus is on easy and independent distribution of plugins and small specific interfaces. Ultimate flexibility is of second concern. + +.Concept +Plugins are just shared libraries which offer well defined Interfaces. +A Plugin may offer more than one interface and may in turn request/use interfaces +from other Plugins or from the main application. -[cpp] -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Interface Definition +-------------------- + +Interfaces are declared in header files. They use some tool macros to give a convenient definition language. +Basically, Interfaces are fixed -- with the exception that new functions may be added. +link:PluginVersioning.html[Plugin Version Management] should stay out of the view most of the time. + +Plugin interfaces are simple C structs with some metadata at the beginning and function prototypes +added at the end. With some macros we can map simple functions to versioned interfaces. +Compiled plugins will stay compatible even if the interface is extended, while sourcecode need maintenance. + +An interface needs a name and a version. They define a block where the actual function prototypes can be added. +New prototypes have to be added at the end, existing prototypes must never be changed. +Each function prototype must be given with its different parts: + +- return type +- name +- arguments list +- version. + + +.Example +[source,c] +----------------------------------------- +LUMIERA_INTERFACE(foo, 1, + LUMIERA_IPROTO(void, bar, (void)), + LUMIERA_IPROTO(int, baz, (int i)) +); + +LUMIERA_INTERFACE(foo, 2, + LUMIERA_IPROTO(void, bar, (void)), + LUMIERA_IPROTO(int, baz, (float i)) +); +----------------------------------------- + +Note that the version 2 interface _changed the parameter from int to float_ for the 'baz' function. + +The interface/plugin framework will expand the above definitions into: + +[source,c] +----------------------------------------- +struct lumiera_interface_foo_1 +{ + struct lumiera_interface interface_header_; + void (*bar) (void); + int (*baz) (int i); +}; + +struct lumiera_interface_foo_2 +{ + struct lumiera_interface interface_header_; + void (*bar) (void); + int (*baz) (float i); +}; +----------------------------------------- + + +Implementation of Interfaces +---------------------------- +Interfaces can be implemented either in core code or through plugins. +In each case, such an _instantiation_ of an interface means that actual functions are mapped +to the corresponding slots in the interface structure. + + +.Implementing an interface +[source,c] +----------------------------------------- LUMIERA_INTERFACE_DECLARE (interface_descriptor, 0, /* The following slots are some human-readable descriptions of certain properties */ LUMIERA_INTERFACE_SLOT (const char*, name, (LumieraInterface)), @@ -18,4 +87,21 @@ LUMIERA_INTERFACE_DECLARE (interface_descriptor, 0, LUMIERA_INTERFACE_SLOT (const char*, license, (LumieraInterface)) /* TODO add more things here, dependencies, provisions etc */ ); -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +----------------------------------------- + + +Lumiera Plugin API +------------------- +The Lumiera Interface/Plugin framework provides some functions to manage Plugins. +Actually a user requests interfaces. The libraries which implement Plugins are managed transparently. +Interfaces are exported as instances and are not necessary singleton. This means that a single Plugin +can export the same interface type several times under different names. The naming rules for interfaces +need to be defined elsewhere. + +loading and opening a Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Plugins are looked up in `$LUMIERA_PLUGIN_PATH`, which is a colon separated list of directories, +and then in a specific ``Lumiera plugin dir'', where standard plugins get installed alongside +with the Application + + diff --git a/doc/design/plugins/PluginVersioning.txt b/doc/design/plugins/PluginVersioning.txt new file mode 100644 index 000000000..3bdffa0c2 --- /dev/null +++ b/doc/design/plugins/PluginVersioning.txt @@ -0,0 +1,44 @@ + PluginVersioningCases +====================== +:Author: MichaelPloujnikov +:Date Created: 200707121127 +:Date Changed: 200707160404 +:Count Changes: 46 + +//MENU: label Plugin Version + + +Compatibility matrix +-------------------- + +.Source compatibility +[grid="rows"] +`100`200`200~~~~ +*~CALLER~ \ ^CALLEE^**,OLD^**^,NEW^**^ +OLD,works,works but a recent interface definition must be available +NEW,works,works +~~~~ + +.Binary compatibility +[grid="rows"] +`100`200`200~~~~ +*~CALLER~ \ ^CALLEE^**,OLD^**^,NEW^**^ +OLD,works,works +NEW,caller gets "revision not sufficient" at runtime and should implement fallbacks,works +~~~~ + +^*^) CALLER is the user of an interface, CALLEE is the interface provider (usually a plugin) + +^**^) OLD means an initial revision, NEW means some later revision of an interface + +Observations +------------ + +Compiling a newer Plugin for some older main application release has some quirks (interface definitions are intended to be shipped with the main application). This should be rarely the case. + +When compiling, older Plugins should be updated to new interface revisions. +Caller should provide a fallback to older interface revisions for binary compatibility. + +Generally, sources should just be properly maintained and updated to use the most recent interfaces revision. + +For binary compatibility everything will work well, provided that the caller kept proper fallback functionality for older interface revisions. Plugins which are independently distributed (packaged) in binary form don't need to be updated with every new main application release and just work. diff --git a/doc/devel/meeting_summary/2008-11-12.txt b/doc/devel/meeting_summary/2008-11-12.txt new file mode 100644 index 000000000..c33fc3c74 --- /dev/null +++ b/doc/devel/meeting_summary/2008-11-12.txt @@ -0,0 +1,785 @@ +2008-11-12 Lumiera Developers Meeting +===================================== +:Author: Ichthyostega +:Date: 2008-11-12 + +Dec 11, 2012 on #lumiera 20:00 - 01:15 UTC + + +__Participants__ + + * cehteh + * ichthyo + * joelholdsworth + * raffa + +_Summary and transcript supplemented by Ichthyo 11/2011_ + + + +Platform and lib dependency status +---------------------------------- +Right away, there is agreement between all 3 core devs that the Lumiera project +should try to stay close to a *stable reference linux distribution*. Usually, +developers tend to stick to ``latest greatest'', causing lots of difficult to resolve +problems to packagers and ursers of their software. The Lumiera team wants to go into +the opposite direction: staying close to a mature and stable distro and rather try to +backport any newer libraries, when they are _really required_ for getting ahead with +the coding. Since _cehteh_ and _ichthyo_ are Debian users and _joelholdsworth_ tends +to use a moderately recent Ubuntu, the intention is to use Debian/stable as reference. + +Conclusion +~~~~~~~~~~ + + * use *Debian/stable* (currently Lenny) as reference distribution + * care for backports of any newer libraries required + * use VMs to verify the build on a moderate selection of more recent distros + + + +Problems with GUI Plugins +------------------------- +The core devs are aware of some challenging problems regarding plug-ins for the +GUI and effect plug-ins requiring an specific GUI control or option pane. Still the more +since the intention is for Lumiera to adapt _existing plug-in systems_ rather then +rolling yet-another new plug-in standard no one else cares to support. Initially, +specific Lumiera plug-ins will be provided only for some very basic modular functionality, +eg. fader, panner, blur. + +_joelholdsworth_ points out that configurability can't replace real GUI and workflow design. +He quotes from a recent Blender GUI discussion + +[quote, the Belnder UI reviews] +____________________________________________________________________ +Lastly, I’d like to address another misconception, this time about customizability. +There has been a notion that the solution to most of the UI problems can be solved with added customizability. +The notion goes that if the UI is inefficient, the user can simply customize it herself to suit her needs. +This claim is wrong for several reasons: + +- It is impossible to customize something you have not fully comprehended yet, + so it in no way helps the learning process. +- It makes an application less predictable, because the user cannot rely on the application + to always act the same. +- It takes focus away from the users content, and over to managing the application itself, + which is what we wanted to avoid in the first place. +- It undermines the portability of Blender, because you cannot rely on Blender + functioning the same way across different systems. +- Customizability does not equate to flexibility. +- 99% of Blenders user will use the default setup anyway. + +This is not to say that customizability is always bad - having the ability to change the hotkeys +from the defaults to match another 3D application such as Maya or Softimage XSI can make it easier +for users of those applications to adapt to Blender. +____________________________________________________________________ + +Consequently, Joel wants to try just doing the UI _right_, then add a little bit of customization back in here and there. +Additionally, agreement is to have a kind-of ``perspective switcher'' (like in Eclipse), so saving different +panel layouts would be covered easily + + +.-- Discussion about foreseeable problems with GUI Plugins -- +[caption="☉Transcript☉ "] +---------------------------- +[2008-11-12 21:53:50] I thought we could just discuss the problem informally + and share some thoughts what we think is most critical +[2008-11-12 22:06:01] there are plugins which don't provide a GUI, and some plugin systems allow + for a standard method for creating plugin guis + +[2008-11-12 22:05:46] so bundling GUI together with plugins is easier now, right? +[2008-11-12 22:05:59] we implement some kind of parameters system +[2008-11-12 22:06:03] yes +[2008-11-12 22:06:14] joelholdsworth: thats up to you .. but the plugin system is ready +[2008-11-12 22:06:27] yeah that's fine +[2008-11-12 22:06:38] if you want to extend the GUI you just expose and interface for it + +[2008-11-12 22:07:51] * cehteh thinks about some canvas widget which the plugin can use to draw stuff there +[2008-11-12 22:08:26] cehteh: as in overlays on the video canvas? +[2008-11-12 22:09:19] joelholdsworth: up to you, not necessary overlays + +[2008-11-12 22:05:07] I see some problems we should thik about +[2008-11-12 22:06:50] so, basically loading a processing routine from + a plugin and using it to process video/audio is no problem. Basically you define + some interface (first in C++) then translate it to the lumiera interface macros + but with the GUI there are several conceptual problems + +[2008-11-12 22:07:35] first: There are plugins which don't have a GUI. Thats fine +[2008-11-12 22:08:05] we need to define a parameter system, as joel pointed out +[2008-11-12 22:08:38] and then we need to fit the existing plugin systems, each one, to use this parameter system +[2008-11-12 22:08:45] thats type:key:value at simplest +[2008-11-12 22:08:51] maybe with some representation hints +[2008-11-12 22:08:58] there is more about it +[2008-11-12 22:09:03] representation hints +[2008-11-12 22:09:07] type of gui control +[2008-11-12 22:09:15] logarithmic/linear scale +[2008-11-12 22:09:17] zero point +[2008-11-12 22:09:19] range +[2008-11-12 22:09:27] but it is manageable +[2008-11-12 22:09:38] LADSPA is a prominent example +[2008-11-12 22:09:55] then, the problem in the GUI is how to arrange those controls +[2008-11-12 22:10:20] anyway, I think this alternative is the most simple one and the one which will + create the least problems +[2008-11-12 22:10:34] ichthyo: I figured the UI will just expose parameters +[2008-11-12 22:10:42] so you expose them semantically - maybe in a tree +[2008-11-12 22:10:51] and add rich presentation hints +[2008-11-12 22:11:00] that hit the UI how to lay out out +[2008-11-12 22:10:59] i think layout should be optional by some config/stylesheet +[2008-11-12 22:11:05] cehteh: possible. But those things are already defined by the + plugin systems we want to wrap up. +[2008-11-12 22:13:01] you have a list (or maybe tree) of parameters .. presentation aside + ... tree just because some parameters depend on the presence of some else + and you have a presentation .. + +[2008-11-12 22:11:53] next problem is: +[2008-11-12 22:12:16] the plugins need to allocate some space in the GUI and should not clutter + the workspace this is a tough really tough interface design problem + because some plugins aren't just some "optional stuff", but wired + right into the core operation + +[2008-11-12 22:12:54] well ichthyo: I'm not planning to allow plugins to "take over" + handling workflow. they get to extend very specific parts of the UI +[2008-11-12 22:13:09] e.g. params +[2008-11-12 22:13:11] or the canvas +[2008-11-12 22:13:15] or timeline +[2008-11-12 22:13:22] and only in very specific ways + +[2008-11-12 22:14:15] ichthyo: and most of the time plugins wont extend anything -- + their whole UI will be exposed semantically (with UI presentation hints) + (in addition to the plain semantics) +[2008-11-12 22:13:27] thats fine + +[2008-11-12 22:14:58] but do you see the interface design problem? + basically you want to design a smooth handling. but now you need to rely + on some module which is plugged in and you don't know much about this module + how is it possible to tweak effects easily while you just don't know much + about those effects? +[2008-11-12 22:15:23] yes I agree + +[2008-11-12 22:15:48] then there is another problem: +[2008-11-12 22:15:56] key bindings and MIDI bindings +[2008-11-12 22:16:10] you want to bind key shortcuts and external controllers to GUI elements +[2008-11-12 22:16:20] and of course plugins need to be included into this system +[2008-11-12 22:16:35] ok that's quite easy - you just right click on the control + (or click a tiny button) that opens up a menu that lets you set this stuff + +[2008-11-12 22:16:59] UI design? well that's what presentation hints help with +[2008-11-12 22:17:26] they set size, layout, the appropriate type of slider + for example to expose this value +[2008-11-12 22:17:29] and what happens when the respective plugin options window isn't opened at all? +[2008-11-12 22:17:47] or when you have multiple instances of the same plugin? +[2008-11-12 22:17:48] what do you mean? +[2008-11-12 22:18:11] for example: you use a sound panner plugin + and you want to adjust the sound pan in 3 dimensions by key shortcuts, + or to bind them to some knobs on an external control surface + and now you have multiple instances of the same panner plugin + so we need a naming scheme + +[2008-11-12 22:19:03] how i thought about this: + 1) plugin provides the list of its parameters (maybe as tree) + 2) there is a config file (config system) which gets installed + with the plugins which defines the default layout + 3) user might override that in his own configs ... + but still only defaults for new instances of the things + 4) the actual configured state is saved with the session + while all this config just is a definition which gets applied to a kindof stylesheet + +[2008-11-12 22:20:05] as tree for sure, because many plugins have nested windows for each instance +[2008-11-12 22:21:49] which means we need a naming/addressing scheme +[2008-11-12 22:22:20] ...which creates sort of a cascading scheme +[2008-11-12 22:22:42] I mean, a naming/addressing scheme +[2008-11-12 22:23:04] for distinguishing identical parameters in different instances of yet-unknown plugins +[2008-11-12 22:23:19] and now consider: where should these key bindings attach to? +[2008-11-12 22:23:39] to a specific instance, or to the suitable instance which is just in focus? +[2008-11-12 22:23:46] or both (probably) +[2008-11-12 22:24:03] you may want to bind a different knob to each different panner plugin +[2008-11-12 22:24:25] or alternatively, you may want to bind "the pan knob" to the "current pannner" +[2008-11-12 22:24:42] this would require that we introduce a focus concept +[2008-11-12 22:25:00] I think thorwil proposed something similar in the last discussion +[2008-11-12 22:25:16] so we have a notion of the "current track", "current clip" +[2008-11-12 22:25:49] and then there is another problem with plugin GUIs +[2008-11-12 22:26:10] many plugin systems, especially VST have custom GUIs +[2008-11-12 22:26:27] and may not even work properly without using those +[2008-11-12 22:26:48] which means, some GUI code in the plugin takes over. That is something I utterly dislike + +[2008-11-12 22:33:29] no thats not needed +[2008-11-12 22:33:29] in the session each object has a uuid thats its address +[2008-11-12 22:33:29] each parameter has a uuid too(interface system) +[2008-11-12 22:33:31] so you can say parameter 0x2344545645654534 of object 0x4543765732525t643 .... +[2008-11-12 22:33:34] "yet unknown" doesnt exist .. it is either unknown or known :) + +[2008-11-12 22:55:26] well ..the problem is just: you can't avoid things like a user binding + some external control surface to a certain gui control. All I wanted is + to bring the problem to everyones attention, also, that we need a parameter system + and that this parameter system is probably tree-like, not just flat +[2008-11-12 22:57:06] i want anythnig be addressable with uuid(manipulating_function)+uuid(object) +[2008-11-12 22:58:41] for me, this is just a opaque reference which is implemented in some way. So, assumed + I have the object representing the connectionn to the plugin, I can provide the GUI with + some means for re-addressing a certain parameter, e.g. for a key binding, and, of course + for re-establishing the same binding when re-creating the session from storage + +[2008-11-12 23:00:50] Further, with plugin guis instances there is yet another problem in that you probably + have more option windows of some plugins as you can show in any reasonable GUI + These issues were one of the reasons I made this "FeatureBundle" proposal some time ago, + Meaning there is a mechanism allowing you to bundle + - an existing external plugin + - an adaptation we do for connecting it to the gui + - maybe a script or gui + - customisation info we need for the gui layout + ...and provide all together as a single bundle the user can install +[2008-11-12 23:11:15] joelholdsworth: maybe you have already some ideas how to deal with all those plugin + option windows? they are a real problem for getting a smooth handling and workflow. + because: you'll spend a considerable amount of the whole project time with tewaking +[2008-11-12 23:12:28] (yes, that's what I am about to point out....) +[2008-11-12 23:13:05] ...if you work on tweaking, you typically have a small amount of plugin windows + which need to stay open with maybe subtabs for all active plugins + say some color correction on 2 tracks, and then you open and close variouos plugins + for several different clips ...plus, maybe the possibility to "pin" some of the plugin + windows, while others just allocate the next free slot + +[2008-11-12 23:13:14] I guess we need some modeless way of putting plugin controls in a panel +[2008-11-12 23:15:32] so maybe having panels open up - or do it like inkscape with a scrollable panel + container that gets longer as more panels are opened + +[2008-11-12 23:20:10] Cinelerra's plugin guis who constantly get lost behind the main window are really a mess +[2008-11-12 23:19:28] joelholdsworth: have you already any ideas about focus handling? +[2008-11-12 23:20:15] joelholdsworth: do we get a "current object", a "current track" or the like + which receives keybord input +[2008-11-12 23:20:37] keybaord focus, yes - that was the plan +[2008-11-12 23:20:54] thorwil recently pointed at the concept of having an "active element" within + a larger selected group of elements e.g. you have a "current clip" and within it + a currently active effect attachment and if you press the shortcut, you switch over + to the automation curve of this plugin if there is one, etc. etc... + +[2008-11-12 23:21:44] think about future ... multiple elements have focus from different devices +[2008-11-12 23:22:06] multitouch ... and other controlers +[2008-11-12 23:22:06] I think we have to treat keyboard focus as special +[2008-11-12 23:22:23] next X version will utilitze multiple mouse pointers +[2008-11-12 23:22:35] e.g. what's with the mouse wheel? +[2008-11-12 23:22:42] what's with a jog dial on a control surface +[2008-11-12 23:23:03] or with the play/stop buttons on a control surface? + +[2008-11-12 23:23:03] my plan is to treat the jog dial as special +[2008-11-12 23:23:23] yes again - special button assignment +[2008-11-12 23:23:23] sort of like the mouse wheel? +[2008-11-12 23:23:34] imagine user wants a 2nd normal pc keyboard as video controler +[2008-11-12 23:23:42] because all those rather follow sort of a focus +[2008-11-12 23:23:56] maybe together with modifier keys + +[2008-11-12 23:24:04] yes again that would work via an auxialiry controller setup config + the primary keybaord and mouse work in the normal way + we can't have two keyboard focusses - it's just won't work + we can have a second keyboard - a board of hotkeys +[2008-11-12 23:25:21] there's mouse, keyboard and auxiliary controllers +[2008-11-12 23:25:34] and those controllers would be mapped to the UI using a cunning dialog +[2008-11-12 23:25:58] and what I said some time ago today with the binding for the "current" pan + is a similar idea. Because also such a binding should rather follow the focus + it doesn't help if you bind it to a specific instance of the panner plugin + the moment you have more then 8 panners, it won't scale + rather, it would be helpful, if you had a "current" fade and pan and sound level, + which would follow the focus, so you could e.g. navigate the focus with the + right hand at the cursor keys, and leave the left hand on your control surface's knobs + please understand me right: I don't want to push features, and I will stay out of GUI + issues. All I want is that everyone in the project is aware of this problems +[2008-11-12 23:27:44] yes ok +[2008-11-12 23:28:45] I see the problem +[2008-11-12 23:28:58] ok thanks + +[2008-11-12 23:31:54] but it's fine we are all aware of the problem and seemingly share the same approach + config system + some sort of automatic persistent references within proc + some + indirection + maybe a focus system + +[2008-11-12 23:33:10] and another point I want to note is: all of us are rather reluctant to allow plugins + to create GUIs on their own +[2008-11-12 23:33:27] depends what you mean by that +[2008-11-12 23:33:29] i guess we cant avoid that +[2008-11-12 23:33:38] but note: there is a great urge from some some users for such things +[2008-11-12 23:33:39] for really external plugins +[2008-11-12 23:33:47] yes +[2008-11-12 23:34:01] i am more cared about them fireing threads up :P +[2008-11-12 23:34:13] thats part of the problem +[2008-11-12 23:34:37] some start registering their own key shortcuts and all sorts of horrible things +[2008-11-12 23:34:42] if you let each plugin create every bit of it's ui, you get a lot of code + copy/paste and a lot of diverging GUI, so you need to "help" the plugins + by putting them in a strict framework. Then, on top of that we allow + extensions which allow a little more flexibility + +[2008-11-12 23:35:20] Steinberg failed to address this problem properly and VST bears on this heritage until today +[2008-11-12 23:35:35] make it in a way where the 'correct' way is also the most convenient for any programmer is + all and the best we can do +[2008-11-12 23:35:49] * ichthyo nods +[2008-11-12 23:36:04] yes +[2008-11-12 23:36:07] note the 'any' ... +[2008-11-12 23:36:20] :-P +[2008-11-12 23:36:26] means mad programmers, beginner programmers, experienced programmers, html programmers ... + +[2008-11-12 23:36:52] but I'm thinking we do need to remember that the majority of our plugin developers + will be part of this project team +[2008-11-12 23:38:09] joelholdsworth: no, it won't be this way. Rather, there are some mainstream plugin systems. + People will foremost want to use plugins they know already, letting asside a small set of + core plugins, yes, those are the ones we provide as native lumiera plugins +[2008-11-12 23:38:42] hmm ok +[2008-11-12 23:38:44] a fader, a mask etc. +[2008-11-12 23:38:50] but anything besides that +[2008-11-12 23:39:06] every sound engenieer has his favorite eq and compressor +[2008-11-12 23:39:26] and he will rather swich to another application than being forced + to use a compressor he isn't fine with. Same for advanced video stuff +---------------------------- + + +Source code organisation +------------------------ +In the weeks before that meeting some discussions regarding the start-up of the main application +took place, including the question how to load and pull up the Lumiera GTK GUI as a plug-in. +Moreover, _cehteh_ just ``landed'' the branch with the first draft of his new plugin/interface +system. Which prompted us to sort out the locations and arrangement of those parts of the source +dealing with the application framework. The decision was to place that code into a small library, +which we call the *Lumiera common application services* (+liblumieracommon.so+), to be kept separate +from the *support library* (+liblumiera.so+). + +.-- Include dir and namespaces -- +[caption="☉Transcript☉ "] +---------------------------- +[2008-11-12 23:49:43] There is the namespace issue we left open on the last meeting +[2008-11-12 23:50:34] its really oblivious that you dont need to reflect the C++ namespaces + 1:1 in the directory structure. I still think having all our stuff into lumiera:: + would be good .. but :) +[2008-11-12 23:51:33] in any case, it is closely interconnected to the "interface namespace": + which means the following: there are some interfaces (opaque datatypes + plus forward decls for C, abstract base classes for C++) + plus the things defined via the interface/plugin system + this stuff is our "public interface" +[2008-11-12 23:53:01] and I really try hard to keep it separate from the implementation + +[2008-11-12 23:53:14] the interface/plugin system has now its own rules which are somewhat special + and good by that way. I am thinking about a interface_doc.h which can be used + to generate documentation from the interface language with some special gcc/cpp flags + +[2008-11-12 23:53:18] those interface parts will be contained in a "Lumiera sdk" + and probably all those interfaces (for C++) will go into namespace lumiera:: + because it's just the best and most natural pick + and, as said, I really want us to try hard to get this interface part + a real interface part and cleanly separated from the implementation + meaning: I don't want *.c and *.cpp files in this package/directory/namespace, + unless they are really needed there + +[2008-11-12 23:55:55] well interfaces themself need to be .c they need to be implemented + and the implementation might adapt the code for example the config system + sometimes returns internal state as success/failure indicator + but its interface just returns integers + +[2008-11-12 23:56:13] so the question regarding namespaces is connected to the question: + how is the implementation organized in relation to the interface part? + +[2008-11-12 23:59:41] config_interface.h needs to be installed someday (with a better name maybe) +[2008-11-13 00:01:52] do we want a src/includes/ ? +[2008-11-13 00:02:04] for includes which get installed +[2008-11-13 00:00:07] so, someone *using* the interface system probably will need to include config_interface.h +[2008-11-13 00:00:19] but he doesn't need config.c +[2008-11-13 00:00:29] config_interface.c is the mapping/actual implementation ... + which gets linked in but the user doesn't need it +[2008-11-13 00:00:52] yes, thats my point. So we should try to separate those cleanly. + + +[2008-11-12 23:59:38] so lets assume src/lumiera contains our interface + (I just take it as an example to make my point more clear) +[2008-11-13 00:01:01] Let me explain it on another more elaborate example + +[2008-11-13 00:01:47] I have a class "Session" which is located in a file session.hpp +[2008-11-13 00:01:58] but it is just a ABC +[2008-11-13 00:02:18] and I have a Class SessionImpl which is contained in sessionimpl.hpp and sessionimpl.cpp +[2008-11-13 00:02:44] and in the "interface package" there should be *only* session.hpp +[2008-11-13 00:03:11] probably this class Session should be moved into namespace lumiera:: +[2008-11-13 00:03:16] then the situation would be clean + +[2008-11-13 00:03:32] note: up to now i wrote only implementation code. +[2008-11-13 00:03:50] yes, me too + +[2008-11-13 00:04:07] but I already made the class hierarchy this way, i.e. everyone *uses* just the class Session +[2008-11-13 00:04:28] and only the class SessManagerImpl knows of the implementation class SessionImpl +[2008-11-13 00:04:32] and so on + +[2008-11-13 00:04:50] well thats not important yet... + I currently have some 'mess' of includes because its just easier for now to mix them + all together and relative easy to clean up when done, and I don't have a differentiation + between interface and implementation headers .. because + a) its all implementation code yet +[2008-11-13 00:06:06] b) i was under the impression that i only wanted to export interfaces + over 'Interfaces' +[2008-11-13 00:06:25] ...which is fine + +[2008-11-13 00:08:24] so .. public interfaces ... place in: + [ ] src/include/*.h + [ ] src/*/*_interface.h +[2008-11-13 00:08:45] I am for the former: put them into a separate directory +[2008-11-13 00:08:55] put them in a clearly separated namespace +[2008-11-13 00:08:29] but... on the long run I am sure we don't just get one "includes" package, + rather I guess we get a (very small) tree of interfaces. + That's the rationale behind my keeping the implementation namespaces completely apart + +[2008-11-13 00:08:53] ok +[2008-11-13 00:09:43] the _interface.c is the glue code which can stay in the implementation dir imo +[2008-11-13 00:10:16] actually i wanted a include dir too .. i just didnt start because i wanted to ask you too +[2008-11-13 00:10:41] ah another thing i asked joel before .. i dump the tests dir out of the Doxyfile +[2008-11-13 00:10:58] really? +[2008-11-13 00:11:09] main documentation should have less noise, + the include graphs are messed up with all test dependencies. + But we can make special doxyfiles for special purposes + Doxyfile.all Doxyfile.small Doxyfile.browser + +[2008-11-13 00:13:34] yes, thats fine. + So we make a "small" doc set with just the core facilities, and a "complete" doc set, + which includes: test cases and documentation on private members, so someone really + needing to find out how something works in detail can use the "complete" set. + At the end, I bet we will have almost 60% test code and 40% actual code + +[2008-11-13 00:16:29] ok, back to the include dir question: + My proposal would be to use src/lumiera/... and namespace lumiera::... + (and its children) for the public interfaces + +[2008-11-13 00:17:04] mhm thats the usual reversal operation +[2008-11-13 00:17:31] src/lumiera/include/ ... gets installed in /usr/include/lumiera :P +[2008-11-13 00:17:40] ok you dont have the include +[2008-11-13 00:17:52] but i'd rather use the name include +[2008-11-13 00:17:59] so src/lumiera/** -> /usr/include/lumiera/** +[2008-11-13 00:18:22] this would make the namespaces match 100% to the directories + +[2008-11-13 00:18:13] i dont like that much +[2008-11-13 00:18:18] better src/include +[2008-11-13 00:18:22] rationale: +[2008-11-13 00:18:43] when i look at some project i dont know and see a ./include + i know that there are the interfaces. If it is ./projectname then + i expect there a huge tree of code which makes the project +[2008-11-13 00:20:04] which is actually true for most project ... + at least i dont know anyone which names its includes projectname +[2008-11-13 00:20:14] thats a good argument +[2008-11-13 00:20:23] the projectname/include is somewhat common .. but bit bloated +[2008-11-13 00:20:48] so better include +[2008-11-13 00:20:49] yeah, thats a good point + +[2008-11-13 00:21:04] so namespace lumiera:: -> /src/include +[2008-11-13 00:21:35] namespace lumiera::edit -> /src/include/edit (just as an example) + +[2008-11-13 00:22:00] while namespace proc::builder -> src/proc/builder + +[2008-11-13 00:22:31] but i think keeping it flat would be ok +[2008-11-13 00:22:58] I didn't say I want it. But it may happen to be necessary or just better, + when the root of the interfaces gets too big +[2008-11-13 00:23:00] at least i dont plan to make a tree behind it +[2008-11-13 00:23:08] neither do I +[2008-11-13 00:23:11] but... well +[2008-11-13 00:23:14] i doubt that it will get very big +[2008-11-13 00:23:29] I've seen enough projects to prove the contrary +[2008-11-13 00:23:45] and when an interface dir as 60 entries it starts to get messy +[2008-11-13 00:23:37] even with 100 files there it will still be manageable +[2008-11-13 00:24:02] and if not .. we may reconsider that before we release +[2008-11-13 00:24:38] a deeper hierarchy which is very sparse is much more annoying + than a dir with up to 100 files + +[2008-11-13 00:24:49] but anyway, the important point is I really want to try hard to get public interfaces + and implementation code cleanly separated +[2008-11-13 00:25:31] for C++ absolutely .. for C i less that a *little* bit +[2008-11-13 00:25:54] for performance reasons and because most of the backend wont end in public interfaces anyway + +[2008-11-13 00:26:27] just out of curiosity: what are the performance reasons? +[2008-11-13 00:26:51] i have some static inline accessor functions .. + which need to be in the .h and need full public structure definitions + which expose private details. I think these will stay private + but if not i wont really care. + +[2008-11-13 00:27:06] ah, I see +[2008-11-13 00:27:59] probably those just count as "reasonable exceptions from the rule" +[2008-11-13 00:28:33] well C has no access protection at all .. you have to do whats allowed/documented +[2008-11-13 00:28:47] any improvisation counts as undefined behaviour .. point + +[2008-11-13 00:28:26] and besides: also within the implementation of one layer, + there are further interfaces, e.g the builder has an interface + + +[2008-11-13 00:32:03] actually, I wanted to write something down, but couldn't find the time +[2008-11-13 00:32:17] But I may well tell it just informally if that's ok ? + +[2008-11-13 00:33:02] I always assume we have separate interface namespace(s) + and implementation is in a different namespace then the exported interface + basically I see two scenarios: + + so, the first scenario I'd call "libarry type" + it means that the implementation namespace is nested + eg namespace mylib <-- the stuff the library users include and use in their code +[2008-11-13 00:34:23] namespace mylib::impl and mylib::impl::special <- the actual code making the library work + +[2008-11-13 00:34:50] second scenario is rather what I was aiming for + namespace lumiera <-- the interfaces +[2008-11-13 00:35:45] namespace proc::builder:.... <-- the implementation has a completely separate hierarchy + for each subsystem + +[2008-11-13 00:36:08] my rationale for prefering the second aproach is: +[2008-11-13 00:36:32] in our case, we are "implementation heavy". + We will certainly have much more implementation code than interfaces + we often will build more nested namespaces for the implementation, + but can live with just one shallow public interface + besides, you can just grab one directory and install it as "include", + the way we discussed + for a real library, the situation is reversed: Often you have much code close + to the interface, maybe even inline + e.g. think at boost: + half of the code is immediately inline within the interface classes you use + and the more technical parts are in shallow sub-namespaces below + the good side of course is, that the implementation code doesn't need + to pull in the interface, because he already sees it because of the nesting. + but for our situation, for me, the need to pull in explicitly any interface + you want to use or implement counts rather as a good thing + because it clearly documents what non-local parts you use + that was my rationale for keeping the implementation part + in a completely separate hierarchy. + +[2008-11-13 00:43:17] joelholdsworth: so you moved your gui code within namespace gui, is that correct? +[2008-11-13 00:43:50] yes that's right! +[2008-11-13 00:43:59] because, then the issue seems to be settled. + Originally I had yet more shallow trees. + But your argument with the matching directory names and the consistency + is an important point. So then, I'll go ahead and move my implementationn code + into a new root namespace proc and then start cleaning up the library part +[2008-11-13 00:44:47] great :) +[2008-11-13 00:45:20] meaning, you can expect quite some stuff moving between /src/common and /src/lib +[2008-11-13 00:45:40] on the long run, probably the intention should be for "common" to disappear +[2008-11-13 00:46:08] either stuff goes into namespace lumiera::, meaning it's an interface + and the header should go into "src/include" or stuff is heavy support lib + implementation stuff and thus goes below src/lib +---------------------------- + + +.-- build system, plugin tree -- +[caption="☉Transcript☉ "] +---------------------------- +[2008-11-13 00:47:06] which brings us to another question +[2008-11-13 00:47:18] how do we organize building plugins? + because: one plugin may contain several source files and headers, + but needs to be linked into one module + +[2008-11-13 00:47:55] plugins dir +[2008-11-13 00:48:11] and a tree there +[2008-11-13 00:48:22] plugins/video/effects +[2008-11-13 00:49:56] plugins/video/effects/foo/ ... with foobar.c foobaz.c links to foo.lum +[2008-11-13 00:52:38] plugins/video/effects/foo/foo.c builds foo.lum and + installs it in $(pkglibdir)/plugins/video/effects/foo.lum + +[2008-11-13 00:48:24] background is: I am aiming to get the build process as much rules-directed as possible +[2008-11-13 00:48:27] thus: +[2008-11-13 00:48:54] can we come up with a rule about how plugins will be built? +[2008-11-13 00:50:33] again the notorious namespace question +[2008-11-13 00:51:08] if it's a rather large plugin, e.g. a plugin providing an adapter for an + external media type libarary or such stuff then it probably can have nested namespaces +[2008-11-13 00:51:56] we really dont need a 1:1 relation between dirs and namespaces +[2008-11-13 00:52:08] so, then, how can the build system tell at what level of the tree below plugins + we'd need to start with building one shared libarary? + +[2008-11-13 00:52:46] would the following rule be ok for you? +[2008-11-13 00:52:58] start with dir src/plugins and descend +[2008-11-13 00:53:12] depth-first tree search +[2008-11-13 00:53:28] when you enter a directory which contains a real source file +[2008-11-13 00:53:46] then build everything below it into one shared module + +[2008-11-13 00:53:31] well i just suggesting here .. for the plugin loader ist relative simple +[2008-11-13 00:53:56] it just searches in the paths you give him (and doesnt descend itself) +[2008-11-13 00:54:19] no, I really want to configure as few pathes as possible +[2008-11-13 00:54:23] if you think thats ok, then do it + +[2008-11-13 00:54:57] note, the limitation is: you can't just put some isolated code + file *.c *cpp in some of the root directories... + the moment you add a *c or *cpp file in some new sub tree, + you'll find a .lum in the corresponding bin dir + +[2008-11-13 00:55:17] thats prolly a good choice anyways +[2008-11-13 00:55:28] plugins shall be only at the leaves of the tree + +[2008-11-13 00:55:42] be careful when linking +[2008-11-13 00:56:16] the autoconf build included a lot libs from configure which where not needed for plugins + +[2008-11-13 00:56:57] besides, I will set up a different "build environment" for plugins +[2008-11-13 00:57:16] in SCons, each build environment has a fixed set of libraries attached +[2008-11-13 00:57:28] you likely need specialized ones for some plugins +[2008-11-13 00:57:43] but you can inherit them +[2008-11-13 00:58:02] yes, I can inherit them +[2008-11-13 00:58:12] and I match them from the directory name +[2008-11-13 00:58:20] the rest is fully automatic + +[2008-11-13 00:58:14] * cehteh thinks +[2008-11-13 00:58:38] a bare plugin doesnt need any lib +[2008-11-13 00:59:04] not even the lumiera support lib? +[2008-11-13 00:59:11] liblumiera.a +[2008-11-13 00:59:12] ? +[2008-11-13 00:59:29] nope +[2008-11-13 00:59:43] only if it needs somthing from that + +[2008-11-13 01:00:01] -DLUMIERA_PLUGIN for compiling plugins +[2008-11-13 01:00:11] plus libdl +[2008-11-13 01:00:24] stop. no +[2008-11-13 01:00:34] libdl actually breaks the isolation :P +[2008-11-13 01:00:41] ah, only for the part which loads the lib +[2008-11-13 01:00:51] the host needs libdl not the plugin +[2008-11-13 01:00:54] but what is when a plugin wants to open another interface +[2008-11-13 01:01:11] it asks the host to do it +[2008-11-13 01:01:17] doesn't it need to link against your plugin system impl? +[2008-11-13 01:01:32] nope .. just the interface.h +[2008-11-13 01:02:49] interface.h defines some functions .. but these are not available for the plugin either + but the nature of plugins is to extend the system .. + thus follows that they will need some more specific libs in many cases + +[2008-11-13 01:02:29] please understand my goal: I want to make the build as automatic and rules based as possible +[2008-11-13 01:02:58] yes ... +[2008-11-13 01:03:09] for SCons, this is already mostly the case currently, + but the autotools build is a maintainance nightmare right now + because the paths and dependencies are so much hard wired there +[2008-11-13 01:03:24] i spent already a considerable time to fix it again and again +[2008-11-13 01:03:52] huh i feel comfortable with autotools +[2008-11-13 01:04:04] fine for you, but I fix it all the time + +[2008-11-13 01:06:44] for now I didn't use any special switches +[2008-11-13 01:06:59] I just defined exampleplugin to be an dynamic module +[2008-11-13 01:07:10] sounds reasonable +[2008-11-13 01:07:41] as said, in future I'll tell scons + "build everything below this root dir into a dynamic module" + and scons will derive the necessary compile and link commands + +[2008-11-13 01:10:32] ah, another little issue +[2008-11-13 01:10:42] we have two tools directories currently +[2008-11-13 01:10:53] one as "src/tool" +[2008-11-13 01:10:57] and another as +[2008-11-13 01:11:12] just "admin" +[2008-11-13 01:11:29] within admin, there is the icon rendering and the vgsuppression +[2008-11-13 01:12:01] the idea was, that all this special support tools would be in src/tool +[2008-11-13 01:12:52] admin are administrative scripts .. tools shall be moved into src/tool +[2008-11-13 01:13:00] ok +[2008-11-13 01:13:07] so joelholdsworth: +[2008-11-13 01:13:29] would it be ok for you to move the icon building executable into src/tool ? +[2008-11-13 01:13:41] and probably vgsuppression too? +[2008-11-13 01:14:00] so the build process doesn't need to build anything in admin +[2008-11-13 01:15:29] yes that's ok! +[2008-11-13 01:15:39] that really is just a tool +[2008-11-13 01:15:43] that is built lilke a tool, so I'd like to move it into src/tool +[2008-11-13 01:16:15] it's the standard rsvg utility with some options added +[2008-11-13 01:16:58] a propos: joelholdsworth: + I've built some additional directory checks into the accompanying python script + you have probably noticed that I call directly from the SConstruct into your + python script to invoke the icon rendering, and I built some additional checks + and move non-empty dirs away as "*.bak" +[2008-11-13 01:17:50] I hadn't actually looked +[2008-11-13 01:17:59] but yes that makes perfect snese +[2008-11-13 01:18:31] actually it is supposed to overwrite any prior renderings +[2008-11-13 01:18:39] ah? +[2008-11-13 01:19:00] so if you change the SVG the PNGs get created fresh +[2008-11-13 01:19:01] so I could change it and just do a recursive remove if the target dir exists? +[2008-11-13 01:19:10] yes +[2008-11-13 01:19:32] like obj files - it's supposed to just overwrite any previous bits +[2008-11-13 01:19:50] well the targets are just build temporaries +[2008-11-13 01:20:01] ok +[2008-11-13 01:22:25] ok. then regariding the build system, the only bit of cleanup is the tests +[2008-11-13 01:22:46] cehteh: we talked about it when I visited you +[2008-11-13 01:23:02] it's not urgent right now +[2008-11-13 01:23:14] but.. as we agreed at that time +[2008-11-13 01:23:39] we should make a separate tree for the test-support code, maybe in a subdirectory of /tests +[2008-11-13 01:24:01] and this tree of course reflects the main tree. That was the idea if I recall correct +[2008-11-13 01:25:11] ichthyo: yes .. i know +[2008-11-13 01:25:13] pending +[2008-11-13 01:25:18] yes, not urgent +[2008-11-13 01:25:26] works as-is for now +[2008-11-13 01:25:45] I have some similar cleanup for my part + +[2008-11-13 01:27:24] btw: do we need "test plugins"? +[2008-11-13 01:27:52] i.e. something similar to the src/plugin subdirs we discussed just a moment ago +[2008-11-13 01:28:53] you mean some which get installed? +[2008-11-13 01:29:09] no, just plugins which are needed only from within tests +[2008-11-13 01:29:18] yes we need them +[2008-11-13 01:29:19] i.e. /tests/plugin +[2008-11-13 01:29:39] and everything below will be built exactly similar as everything below /src/plugin +[2008-11-13 01:29:44] i currently build them in place example_plugin.c +[2008-11-13 01:29:54] just it will be only used for test runs +[2008-11-13 01:29:56] yes, ok + +[2008-11-13 01:32:18] another thing: shall plugins override existing interfaces or barf out? +[2008-11-13 01:32:33] both has pros and cons +[2008-11-13 01:32:46] both can easily circumvented with some effort +[2008-11-13 01:33:27] ehm, I don't fully understand the situation. + Could you explain it? Lets say: we have interface A + now a plugin wants to provide an implementation of interface A + i.e. the client of this plugin would use interface A to access it + +[2008-11-13 01:34:03] in a mail to you i explained that i would like mockup plugins which + override core functionality + +[2008-11-13 01:35:07] so, wouldn't the mockup just be another implementation of interface A ? +[2008-11-13 01:35:09] well you query an interface+implementation tuple +[2008-11-13 01:35:35] you cant just open a interface .. you open a implementation of an interface +[2008-11-13 01:35:49] yes, but you use this (interface+implemntation) just for opening the interface, or? +[2008-11-13 01:36:05] so mocplugin can provide blurfoo +[2008-11-13 01:36:15] even if blurfoo.lum provides it too + +[2008-11-13 01:40:32] imagine 2 *.lum modules implement and export exactly the same thing + (or at least announce it as exactly the same thing) + +[2008-11-13 01:40:57] I'd just try to relate it to the similar situation with classes +[2008-11-13 01:41:16] you have an interface and now you have two subclasses +[2008-11-13 01:41:12] nah classes are different +[2008-11-13 01:41:27] no they aren't. really different +[2008-11-13 01:41:43] subtle different +[2008-11-13 01:41:47] so both subclasses claim to implement the contract which is defined by the interface +[2008-11-13 01:42:04] now I have a factory which instantiates one of those subclasses +[2008-11-13 01:42:14] or the other or both +[2008-11-13 01:42:18] same situation as for the plugins + +[2008-11-13 01:42:44] err no +[2008-11-13 01:42:48] you have only one class +[2008-11-13 01:42:49] the client just wants an "instance" of the contract defined by the interface +[2008-11-13 01:43:01] and doesn't care which one it is +[2008-11-13 01:43:14] you didnt understand my problem because it doesnt exist in C++ +[2008-11-13 01:43:33] class foo {...}; +[2008-11-13 01:43:43] foo this_is_it; +[2008-11-13 01:43:44] foo this_is_it; +[2008-11-13 01:43:57] ... redefinition of the same instance is my problem +[2008-11-13 01:44:08] not even exactly +[2008-11-13 01:44:37] ah, in C++ the two would either be rejected by the compiler, + or they would be allowed if in different scopes +[2008-11-13 01:44:43] even worse the 2nd can say the it is a identical instance but it isnt +[2008-11-13 01:45:07] thats a good way to inject moc objects + +[2008-11-13 01:45:15] so now my solution: +[2008-11-13 01:46:01] *thinking* +[2008-11-13 01:46:29] minor versions must differ, biggier minor will win +[2008-11-13 01:46:41] (newer one for certain) +[2008-11-13 01:46:53] thats intended +[2008-11-13 01:47:11] now a mock object might claim it is some *exact* same .. +[2008-11-13 01:47:57] that will be rejected .. so a mock object must do some heavy work + to throw out the old one from the nest +[2008-11-13 01:48:10] thats ok for testing purposes + +[2008-11-13 01:48:12] well... at some point you will be able to tell the loader: + load this module for use as interface A +[2008-11-13 01:48:20] nah +[2008-11-13 01:48:37] but you forget that modules != interfaces +[2008-11-13 01:49:07] I understand the modules as being like a "subclass" of the interface +[2008-11-13 01:49:23] meaning, each of them can "stand-in" for the interface +[2008-11-13 01:49:18] nope wrong +[2008-11-13 01:49:41] modules are a collection of interfaces +[2008-11-13 01:49:47] completely unrelated +[2008-11-13 01:49:54] huh? +[2008-11-13 01:50:19] a module could provide a video effect and a gui plugin and a fancy format exporter at once +[2008-11-13 01:50:36] no one shall do that ... but modules are just collections of interfaces +[2008-11-13 01:50:37] which would be 3 interfaces +[2008-11-13 01:50:41] yes +[2008-11-13 01:51:00] but what then is the term for the implementation of an interface? +[2008-11-13 01:51:08] because, at an extension point +[2008-11-13 01:51:21] you just want to tell: this is the interface to be used at this extension point +[2008-11-13 01:51:28] but the module is just a bunch of interfaces .. and module itself doesnt give them a purpose +[2008-11-13 01:52:09] thats clear. Something like the feature bundle could give it a speficic purpose, + or something similar, which itself is loaded as plugin +[2008-11-13 01:52:46] modules are just the bucket .. it is pretty unspecified what you put in +[2008-11-13 01:53:01] yes thats fine, to start with +[2008-11-13 01:53:23] anything more specific can be built on top of such a system +[2008-11-13 01:53:35] for example take the video effect + at some point, you define an interface "VideoEffect" + probably this interface isn't loaded from a module, rather it's defined from the core, + but this doesn't matter for the point in question here + now, everything you can ever use as an implementation of this "VideoEffect" + must use this interface, because the client of the interface + (which in this case is the core which uses this interface for rendering) + doesn't care about the implementation and doesn't know any differences +---------------------------- + diff --git a/doc/devel/meeting_summary/2012-01-11.txt b/doc/devel/meeting_summary/2012-01-11.txt new file mode 100644 index 000000000..cce9fbd6a --- /dev/null +++ b/doc/devel/meeting_summary/2012-01-11.txt @@ -0,0 +1,35 @@ +2012-01-11 Lumiera Developers Meeting +===================================== +:Author: cehteh +:Date: 2012-01-11 + +Jan 11, 2011 on #lumiera 20:00 + +__Participants__ + + * cehteh + * ichthyo + * benn + * raffa + +Conclusions +----------- + +. ichthyo removed most of the tiddly wikis, and worked the content into the website +. cehteh reports that Lumiera got another donation (75Eur), arrangements with + the ffis to get access (view) about the donations account are under way. We'll + ask donors then if they want to be published or stay anonym and will set up + a wiki page listing donations and expenses. +. ichthy rewrote the SCons build, as discussed last time +. cehteh writes a very short RfC, to document that we're using SCons for now. +. possibly no one going to LAC, too far away +. we discussed a link checker / link resolver for the website. + The idea is to have a semi automatic tool, which is used locally when + authoring website content to find cross references. +. benn and ichthyo follow up on the libregraphics magazine and try to get into + discussion with them and see what can be done within our limited time. + ichthyo respond to the mail, and put you (benn and ct) on CC. +. when it comes to have a working example for media file output, we stick to the + mainstream solutions ffmpeg and or gstreamer, but care not to lock ourselves + into a single solution. Concluded that we do this over plugin interfaces and + it mostly boils down to support ffmped .. and investigate something simpler too. diff --git a/doc/devel/meeting_summary/index.txt b/doc/devel/meeting_summary/index.txt index a65295735..6a1d1cb06 100644 --- a/doc/devel/meeting_summary/index.txt +++ b/doc/devel/meeting_summary/index.txt @@ -12,6 +12,15 @@ Anyone interested in Lumiera development is also encouraged to read mailing list archives and other documentation. +During Summer 2011 we kept up our monthly IRC meetings -- rather casual exchange +for the time being. + +- 9 Nov 2011 +- 12 Oct 2011 +- 14 Sep 2011 +- 10 Aug 2011 +- 13 Jul 2011 +- 8 Jun 2011 11 May 2011 @@ -143,7 +152,7 @@ Topics Summary ^^^^^^^ -_(missing)_ + * link:2008-11-12.html[Transcript (supplemented by Ichthyo in 2011)] diff --git a/doc/devel/report.txt b/doc/devel/report.txt index 7734b66dc..deec2fd15 100644 --- a/doc/devel/report.txt +++ b/doc/devel/report.txt @@ -1,7 +1,7 @@ Statistics and Reports ====================== -//Menu: label Statisticts +//Menu: label Statistics ++++++++++++++++++++++++++++++++++++++ diff --git a/doc/devel/rfc/CCodingStyleGuide.txt b/doc/devel/rfc/CCodingStyleGuide.txt index 72914077d..7325b3961 100644 --- a/doc/devel/rfc/CCodingStyleGuide.txt +++ b/doc/devel/rfc/CCodingStyleGuide.txt @@ -65,16 +65,11 @@ For each `struct namespace_foo_struct` we have following typedefs: [source,C] ---- -typedef struct namespace_foo_struct namespace_foo; // canonical -typename -typedef const namespace_foo * const_NamespaceFoo; // pointer to const -object -typedef namespace_foo* link:NamespaceFoo[]; // canonical -pointer/handle -typedef namespace_foo ** link:NamespaceFoo[]_ref; // when intend to -mutate the handle itself -typedef const namespace_foo ** const_NamespaceFoo_ref; // for const object -handle +typedef struct namespace_foo_struct namespace_foo; // basic struct name +typedef namespace_foo* NamespaceFoo; // canonical object pointer/handle +typedef const namespace_foo * const_NamespaceFoo; // pointer to const object +typedef namespace_foo ** NamespaceFoo_ref; // when intend to mutate the handle itself +typedef const namespace_foo ** const_NamespaceFoo_ref; // same for const object handle ---- Examples: diff --git a/doc/devel/rfc/LumieraForwardIterator.txt b/doc/devel/rfc/LumieraForwardIterator.txt index e93ca8861..5bb298127 100644 --- a/doc/devel/rfc/LumieraForwardIterator.txt +++ b/doc/devel/rfc/LumieraForwardIterator.txt @@ -149,6 +149,12 @@ for the most common STL containers, plus Map, key and value extractors. Ichthyostega:: 'Sa 16 Apr 2011 00:20:13 CEST' +minor change: removed support for post-increment. It doesn't fit with the concept +and caused serious problems in practice. A correct implementation of post-increment +would require a ``deep copy'' of any underlying data structures. + +Ichthyostega:: 'Sa 07 Jan 2012 21:49:09 CET' ~~ + //endof_comments: diff --git a/doc/devel/rfc/MakeSconsTheOfficialBuildSystem.txt b/doc/devel/rfc/MakeSconsTheOfficialBuildSystem.txt new file mode 100644 index 000000000..53958c986 --- /dev/null +++ b/doc/devel/rfc/MakeSconsTheOfficialBuildSystem.txt @@ -0,0 +1,83 @@ +Make Scons the official build System +==================================== + +// please don't remove the //word: comments + +[grid="all"] +`------------`----------------------- +*State* _Final_ +*Date* _Mi 11 Jan 2012 21:45:58 CET_ +*Proposed by* Christian Thaeter +------------------------------------- + +******************************************************************************** +.Abstract +_Bless Scons the default build system for Lumiera._ +******************************************************************************** + +Description +----------- +//description: add a detailed description: + +So far we using autotools and scons in parallel. Over time the need arose to have one +reliable supported build system. This shall be scons. + + +Tasks +~~~~~ +// List what needs to be done to implement this Proposal: +// * first step ([green]#✔ done#) +// * second step [,yellow]#WIP# +Nothing to do except for releases scons *must* be working and all non functional +build systems will be stripped from releases (branches?). + + +Discussion +~~~~~~~~~~ + +Pros +^^^^ +// add a fact list/enumeration which make this suitable: +// * foo +// * bar ... + + + +Cons +^^^^ +// fact list of the known/considered bad implications: + + + +Alternatives +^^^^^^^^^^^^ +//alternatives: explain alternatives and tell why they are not viable: + + + +Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: + + + +Conclusion +---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: + + + +Comments +-------- +//comments: append below + +.State -> Final +//add reason +Decided on the December 2011 Developer meeting. + Mi 11 Jan 2012 22:28:36 CET Christian Thaeter + +//endof_comments: + +'''' +Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/rfc_pending/EngineInterfaceOverview.txt b/doc/devel/rfc_pending/EngineInterfaceOverview.txt index d33461893..e9e931661 100644 --- a/doc/devel/rfc_pending/EngineInterfaceOverview.txt +++ b/doc/devel/rfc_pending/EngineInterfaceOverview.txt @@ -163,7 +163,8 @@ buffers. Dispatching Step ~~~~~~~~~~~~~~~~ -The dispatcher translates a render process into sequences of node invocations, +The dispatcher translates a render process (actually a _calculation stream_ as +part of a render process) into sequences of node invocations, which then can be analysed further (including planning the invocation of prerequisites) and scheduled. This mapping is assisted by the engine model API (to find the right exit node in the right segment), the render process (for @@ -212,7 +213,7 @@ Tasks ([green]#-> yes, implementation deferred#) * find out about the exact handling of multichannel data streams ([green]#✔ done#) * design and prototypical implementation of frame quantisation ([green]#✔ done#) - * design a buffer descriptor [red]#TODO# + * design a buffer descriptor ([green]#✔ done#) * design a buffer designation scheme [red]#TODO# * expand on the node identification scheme [red]#TODO# * clarify how control data frames can be addressed [red]#TODO# @@ -223,7 +224,11 @@ Discussion Pros/Cons/Alternatives ^^^^^^^^^^^^^^^^^^^^^^ -Possible variants to consider.... +Currently we're focussing on how to implement _this_ concept, not on +evaluating alternatives. Especially the idea of scheduling individual frame jobs +is a core concept of Lumiera. This RfC tries to bridge from the session model to +an engine based on these concepts. It's the attempt to link two concepts already +defined and decided on.... Rationale diff --git a/doc/devel/rfc_pending/StreamTypeSystem.txt b/doc/devel/rfc_pending/StreamTypeSystem.txt index ea07777c2..12323606f 100644 --- a/doc/devel/rfc_pending/StreamTypeSystem.txt +++ b/doc/devel/rfc_pending/StreamTypeSystem.txt @@ -1,30 +1,37 @@ +Stream Type System +================== + +// please don't remove the //word: comments + [grid="all"] `------------`----------------------- -*State* _Idea_ +*State* _Draft_ *Date* _2008-10-05_ *Proposed by* link:Ichthyostega[] ------------------------------------- -Stream Type System ------------------- -Especially in the Proc-Layer, we need a framework to deal with different -"kinds" of media streams. + +******************************************************************************** +.Abstract +Especially in the Proc-Layer, within the Builder and at the interface to the +Engine we need sort of a framework to deal with different »kinds« of +media streams. + This is the foundation to be able to define what can be connected and to separate out generic parts and isolate specific parts. - +******************************************************************************** Description -~~~~~~~~~~~ +----------- +//description: add a detailed description: The general idea is that we need meta information, and -- more precisely -- that _we_ need to control the structure of this metadata. Because it has immediate consequences on the way the code can test and select the appropriate path to deal with some data or a given case. This brings us in a difficult situation: - * almost everything regarding media data and media handling is notriously + * almost everything regarding media data and media handling is notoriously convoluted * because we can't hope ever to find a general umbrella, we need an extensible solution @@ -41,30 +48,36 @@ role for some of the more advanced things possible within the session. Terminology -^^^^^^^^^^^ +~~~~~~~~~~~ * *Media* is comprised of a set of streams or channels * *Stream* denotes a homogeneous flow of media data of a single kind - * *Channel* denotes a elementary stream, which can't be further separated _in - the given context_ + * *Channel* denotes an elementary stream, which -- _in the given context_ -- + can't be decomposed any further * all of these are delivered and processed in a smallest unit called *Frame*. Each frame corresponds to a time interval. - * a *Buffer* is a data structure capable of holding a Frame of media data. + * a *Buffer* is a data structure capable of holding one or multiple Frames of media data. * the *Stream Type* describes the kind of media data contained in the stream +Concept of a Stream Type +~~~~~~~~~~~~~~~~~~~~~~~~ + +The Goal of our Stream Type system is to provide a framework for precisely +describing the ``kind'' of a media stream at hand. The central idea is to +structure the description/classification of streams into several levels. +A complete stream type (implemented by a stream type descriptor) contains +a tag or selection regarding each of these levels. + Levels of classification ^^^^^^^^^^^^^^^^^^^^^^^^ -The description/classification of streams is structured into several levels. A -complete stream type (implemented by a stream type descriptor) containts a tag -or selection regarding each of these levels. * Each media belongs to a fundamental *kind of media*, examples being _Video, Image, Audio, MIDI, Text,..._ This is a simple Enum. * Below the level of distinct kinds of media streams, within every kind we - have an open ended collection of *Prototypes*, which, whithin the high-level + have an open ended collection of *Prototypes*, which, within the high-level model and for the purpose of wiring, act like the "overall type" of the media stream. Everything belonging to a given Prototype is considered to be - roughly equivalent and can be linked together by automatic, lossles + roughly equivalent and can be linked together by automatic, lossless conversions. Examples for Prototypes are: stereoscopic (3D) video versus the common flat video lacking depth information, spatial audio systems (Ambisonics, Wave Field Synthesis), panorama simulating sound systems (5.1, @@ -85,15 +98,15 @@ _library_ routines, which also yield a _type classification system_ suitable for their intended use. Most notably, for raw sound and video data we use the http://gmerlin.sourceforge.net/[GAVL] library, which defines a fairly complete classification system for buffers and streams. For the relevant operations in -the Proc-Layer, we access each such library by means of a Facade; it may sound +the Proc-Layer, we access each such library by means of a Façade; it may sound surprising, but actually we just need to access a very limited set of operations, like allocating a buffer. _Within_ the Proc-Layer, the actual implementation type is mostly opaque; all we need to know is if we can connect two streams and get an conversion plugin. Thus, to integrate an external library into Lumiera, we need explicitly to -implement such a Lib Facade for this specific case, but the intention is to be -able to add this Lib Facade implementation as a plugin (more precisely as a +implement such a Lib Façade for this specific case, but the intention is to be +able to add this Lib Façade implementation as a plugin (more precisely as a "Feature Bundle", because it probably includes several plugins and some additional rules) @@ -105,8 +118,8 @@ with, determining a suitable prototype for a given implementation type is sort of a tagging operation. But it can be supported by heuristic rules and an flexible configuration of defaults. For example, if confronted with a media with 6 sound channels, we simply can't tell if it's a 5.1 sound source, or if -it's a pre mixed orchesrta music arrangement to be routed to the final balance -mixing or if it's a prepared set of spot pickups and overdubbed dialogue. But a +it's a pre mixed orchestra music arrangement to be routed to the final balance +mixing or if it's a prepared set of spot pick-ups and overdubbed dialogue. But a heuristic rule defaulting to 5.1 would be a good starting point, while individual projects should be able to set up very specific additional rules (probably based on some internal tags, conventions on the source folder or the @@ -132,42 +145,46 @@ connections and conversions into each other. * Conversions and judging the possibility of making connections at the level of implementation types is coupled tightly to the used library; indeed, most - of the work to provide a Lib Facade consists of coming up with a generic + of the work to provide a Lib Façade consists of coming up with a generic scheme to decide this question for media streams implemented by this library. Tasks -^^^^^ +~~~~~ +// List what needs to be done to implement this Proposal: * draft the interfaces ([green]#✔ done#) - * define a fallback and some basic behaviour for the relation between + * define a fall-back and some basic behaviour for the relation between implementation type and prototypes [,yellow]#WIP# * find out if it is necessary to refer to types in a symbolic manner, or if it - is sufficient to have a ref to a descriptor record or Facade object. - * provide a Lib Facade for GAVL [,yellow]#WIP# + is sufficient to have a ref to a descriptor record or Façade object. + * provide a Lib Façade for GAVL [,yellow]#WIP# * evaluate if it's a good idea to handle (still) images as a separate distinct kind of media +Discussion +~~~~~~~~~~ Alternatives ^^^^^^^^^^^^ -Instead of representing types my metadata, leave the distinction implicit and +//alternatives: explain alternatives and tell why they are not viable: +Instead of representing types by metadata, leave the distinction implicit and instead implement the different behaviour directly in code. Have video tracks and audio tracks. Make video clip objects and audio clip objects, each -utilizing some specific flags, like sound is mono or stereo. Then either -switch, swich-on-type or scatter out the code into a bunch of virtual +utilising some specific flags, like sound is mono or stereo. Then either +switch, switch-on-type or scatter out the code into a bunch of virtual functions. See the Cinelerra source code for details. In short, following this route, Lumiera would be plagued by the same notorious problems as most existing video/sound editing software. Which is, implicitly -assuming "everyone" just does "normal" things. Of course, users always were and -always will be clever enough to work around this assumption, but the problem -is, all those efforts will mostly stay isolated and can't crystalize into a +assuming ``everyone'' just does ``normal'' things. Of course, users always were +and always will be clever enough to work around this assumption, but the problem +is, all those efforts will mostly stay isolated and can't crystallise into a reusable extension. Users will do manual tricks, use some scripting or rely on project organisation and conventions, which in turn creates more and more -coercion for the "normal" user to just do "normal" things. +coercion for the ``average'' user to just do ``normal'' things. To make it clear: both approaches discussed here do work in practice, and it's more a cultural issue, not a question guided by technical necessities to select @@ -175,6 +192,8 @@ the one or the other. Rationale +--------- +//rationale: Give a concise summary why it should be done *this* way: * use type metadata to factor out generic behaviour and make variations in behaviour configurable. @@ -189,13 +208,17 @@ Rationale * provide the possibility to incorporate very project specific knowledge as rules. +//Conclusion +//---------- +//conclusion: When approbate (this proposal becomes a Final) +// write some conclusions about its process: Comments -------- - +//comments: append below As usual, see the http://www.lumiera.org/wiki/renderengine.html#StreamType[Proc-Layer impl doku] for more information and implementation details. @@ -214,8 +237,8 @@ number of inputs and outputs) need in some way to be connected. The fact that we don't have a rule based system for deciding queries currently is not much of a problem. A table with some pre configured default answers for a small number of common query cases is enough to get the first clip rendered. -(Such a solution is already in place and working.) - -- link:Ichthyostega[] 2008-10-05 +(Such a solution is already in place and working.) + + -- link:Ichthyostega[] 2008-10-05 Woops fast note, I didn't read this proposal completely yet. Stream types could or maybe should be coopertatively handled together with the backend. Basically @@ -226,9 +249,9 @@ number, plus adding the capabilitiy of per frame metadata. This indices get abstracted by "indexing engines" it will be possible to have different kinds of indices over one file (for example, one enumerating single frames, one enumerating keyframes or gops). Such a indexing engine would be also the place -to attach per media metadata. From the proc layer it can then look like +struct -frameinfo* get_frame(unsigned num)+ where +struct frameinfo+ (not yet defined) -is something like +{ void* data; size_t size; struct metadata* meta; ...}+ +to attach per media metadata. From the proc layer it can then look like `struct +frameinfo* get_frame(unsigned num)` where `struct frameinfo` (not yet defined) +is something like `{ void* data; size_t size; struct metadata* meta; ...}` + -- link:ct[] 2008-10-06 Needs Work @@ -239,5 +262,7 @@ agreed that we want this concept as proposed here. Do 14 Apr 2011 03:06:42 CEST Christian Thaeter +//endof_comments: +'''' Back to link:/documentation/devel/rfc.html[Lumiera Design Process overview] diff --git a/doc/devel/template/new_rfc.sh b/doc/devel/template/new_rfc.sh index a08ac3d06..eccaecc8d 100644 --- a/doc/devel/template/new_rfc.sh +++ b/doc/devel/template/new_rfc.sh @@ -12,9 +12,9 @@ ${TITLE//?/=} *Proposed by* $(git config --get user.name) <$(git config --get user.email)> ------------------------------------- -[abstract] ******************************************************************************** - +.Abstract +_give a short summary of this proposal_ ******************************************************************************** Description diff --git a/doc/devel/uml/fig130309.png b/doc/devel/uml/fig130309.png index d63c8432b..6afb6d279 100644 Binary files a/doc/devel/uml/fig130309.png and b/doc/devel/uml/fig130309.png differ diff --git a/doc/devel/uml/fig130437.png b/doc/devel/uml/fig130437.png index e12e4b48b..925723480 100644 Binary files a/doc/devel/uml/fig130437.png and b/doc/devel/uml/fig130437.png differ diff --git a/doc/devel/uml/fig131205.png b/doc/devel/uml/fig131205.png index 17826d19b..5d46c3249 100644 Binary files a/doc/devel/uml/fig131205.png and b/doc/devel/uml/fig131205.png differ diff --git a/doc/devel/uml/fig142725.png b/doc/devel/uml/fig142725.png index 6e8c570d8..91c795876 100644 Binary files a/doc/devel/uml/fig142725.png and b/doc/devel/uml/fig142725.png differ diff --git a/doc/devel/uml/fig143877.png b/doc/devel/uml/fig143877.png new file mode 100644 index 000000000..1e628bc41 Binary files /dev/null and b/doc/devel/uml/fig143877.png differ diff --git a/doc/devel/uml/fig144005.png b/doc/devel/uml/fig144005.png new file mode 100644 index 000000000..6ca73fc49 Binary files /dev/null and b/doc/devel/uml/fig144005.png differ diff --git a/doc/devel/uml/fig145157.png b/doc/devel/uml/fig145157.png new file mode 100644 index 000000000..dac8e23cb Binary files /dev/null and b/doc/devel/uml/fig145157.png differ diff --git a/doc/devel/uml/fig151685.png b/doc/devel/uml/fig151685.png new file mode 100644 index 000000000..77b41e446 Binary files /dev/null and b/doc/devel/uml/fig151685.png differ diff --git a/doc/index.txt b/doc/index.txt index 60dbc239d..36ab3139a 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -16,6 +16,16 @@ Informations about the structure of Lumiera, they give an overview on the main c Programming-oriented details about the functional layers of Lumiera as well as a Doxygen documentation : * link:technical/index.html[Technical Documents] + * link:technical/code/codingGuidelines.html[Style and Coding Guidelines] + +=== the TiddlyWiki === +Currently, Lumiera is still in the design- and evolution phase. +There is an embedded JavaScript wiki (TiddlyWiki) within the source tree, mostly +used as design notebook, featuring day-to-day design sketches, notes but also +quite some more persistent planning. Finished documentation text is constantly +moved over to the documentation section(s) of the Lumiera website. + +-> access the Proy-Layer link:http://lumiera.org/wiki/renderengine.html[TiddlyWiki online here] == Media and Presentations == diff --git a/doc/technical/backend/index.txt b/doc/technical/backend/index.txt index 58fd1a5e9..4e7c2bda9 100644 --- a/doc/technical/backend/index.txt +++ b/doc/technical/backend/index.txt @@ -1,8 +1,9 @@ Technical Documentation: Backend ================================ -Eventually, this will have technical documentation for the Backend. +Here we collect bits of technical documentation for the Backend. For now, we have: * link:ConfigLoader.html[Config Loader brainstorming from 2008] + * link:scheduler.html[Scheduler and Jobs] diff --git a/doc/technical/backend/scheduler.txt b/doc/technical/backend/scheduler.txt new file mode 100644 index 000000000..f62da23dd --- /dev/null +++ b/doc/technical/backend/scheduler.txt @@ -0,0 +1,40 @@ +Scheduler and Job handling +========================== + +The purpose of the _Scheduler_ is to run small self contained _Jobs_ +ordered by priority and observing specific timing constraints. + +Scheduler implementation ideas +------------------------------ + +Use multiple priority queues + +- background work +- foreground high-priority +- soft-realtime actions + +About Jobs +---------- +A job is a closure to run a small and limited action or operation, which +in itself _should not block_. Job may depend on each other and on resources +to be provided. A job may be conained in multiple queues and may be marked +as _canceled_ -- in which case the job function will never run and the job +will be discarded on occasion. + +Job States +~~~~~~~~~~ + +[source,C] +-------------- +enum job_state +{ + done, // already done, nothing to do + running, // job is running + waiting, // waits for some resource (annother job) + rejected, // sorry, cant do that dave, time will run out + expired, // time expired + aborted // got aborted +} +-------------- + + diff --git a/doc/technical/build/Dependencies.txt b/doc/technical/build/Dependencies.txt new file mode 100644 index 000000000..92cd38db7 --- /dev/null +++ b/doc/technical/build/Dependencies.txt @@ -0,0 +1,90 @@ +Dependencies +------------ +:Author: CehTeh +:Date: 3/2008 + + +Lumiera is written for GNU/Linux. We try to make the best out of modern system programming techniques +to reach the best possible performance. Lumiera shall scale with the provided Hardware, +the more RAM and the more/faster CPU's you have the better. +Nevertheless lower end 32bit machines are supported too. + +Secondary targets will be other free operating systems which offer a decent Posix API. + +Porting to other more or less similar platforms will be possible, if -- by coincidence -- +Someone(TM) helps with porting. + +Having said that -- for the time being, the core team won't spend much effort on porting. + +Platform +-------- +We work and test on PC hardware, 32 and 64 bit. It is intended that Lumiera supports +other platforms running run GNU/Linux. +Lumiera expects a 'standard' desktop installation running a Xserver. + +Graphics:: + There are no special requirements for the graphic system. + Hardware accelleration will likely be added later through extensions, + but will remain strictyl optional. (For now we'll watch the evolution + in that area and might revisit that topic when there are more compelling + and widely supported solutions available) + +Disks:: + Video editing requires decent disk speed, so it is suggested to use a + fast/big array of disks configured as raid. + +Special Hardware:: + Sopport for special hardware would be possible, but depends on certain conditions ++ +* we need access / donations for the hardware +* specs and APIs must be open. +* someone to do the actual interfacing and support needs to join the team + + +Languages and Tools +------------------- + +* C / C++ + + - a C99 / C++98 compatible compiler + - GCC 4.4 or better is fine. Basically we try to use just the stock language. + On rare occasions, we _did_ use some GCC extensions, but there would be workarounds, + should this become a problem. + - std::tr1 extensions for C++ (smart-ptrs, hashtables, function objects) + +* BOOST (listed below are the DEBIAN package names) + - libboost-dev (at least 1.40) + - libboost-program-options-dev + - libboost-program-options-dev + - libboost-filesystem-dev + - libboost-regex-dev + +* Script languages + - Python (2.5) for build scripts + - bash (some test scripts use bash specific extensions) + - Lua is planned to become a general glue and binding language + +Build Tools +~~~~~~~~~~~ + +* Git +* SCons +* pkg-config +* Doxygen + +We maintain a Debian package (debhelper, CDBS, git-buildpackage) + +Libraries +~~~~~~~~~ + +* BOOST +* NoBug +* http://gmerlin.sourceforge.net/gavl.html[GAVL] (for raw media support) +* for the GUI: gtkmm-2.4 gdl-1.0 libglibmm-2.4 cairomm-1.0 xv + - libgtkmm-2.4-dev + - libcairomm-1.0-dev + - libgdl-1-dev + - libglibmm-2.4-dev, requiring glib2.0 and gthread-2.0 + - libxv-dev + - librsvg-2.0 and librsvg2-dev for rendering Icons + + diff --git a/doc/technical/build/LumieraDebianPackage.txt b/doc/technical/build/LumieraDebianPackage.txt new file mode 100644 index 000000000..9e97f3162 --- /dev/null +++ b/doc/technical/build/LumieraDebianPackage.txt @@ -0,0 +1,89 @@ +The Lumiera Debian Package +========================== +:Author: Hermann Voßeler deb@ichthyostega.de +:Date: 11/2011 + +//Menu: label Debian Package + +Since several of the Lumiera core developers run a Debian flavour as their primary development platform, +it seems natural to care for the debian packaging of Lumiera ourselves. Moreover, we've declared +Debian/Stable to be our *reference platform* -- we'll provide any additionally required, more recent +packages through our own link:http://Lumiera.org/debian[Debian Apt-Repository] (Debian depot). + +TIP: readers new to debian packages may want to have a look into our + link:{ldoc}/user/tutorials/DebianBuilding.html[Debian build tutorial], + describing the commands for building and the structure of a debian + (source) package in general + + +Package build process +--------------------- +As our whole infrastructure relies heavily on the Git version management tool, it's only natural +also to organise the (debian) packaging with the help of Git. Fortunatlely, there is a nifty tool +called link:https://honk.sigxcpu.org/piki/projects/git-buildpackage/[git-buildpackage], written +by Guido Günther exactly for this purpuse: It treats the _debianisation_ as a branch in the +Git repository, forking off the mainline at the release point. + +For Lumiera, this _debianisation branch_ is called 'deb' and can be found in the +link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=summary[git:/git.lumiera.org/lumiera/debian] repository. + +Installing Lumiera +~~~~~~~~~~~~~~~~~~ +The link:SCons.html[SCons build] generates a relocatable distribution directory structure, +holding the main executable, additional tools and utilities, plus the core libraries and resources. +Here _relocatable_ means that this subtree can be moved and placed anywhere on the system. +As long as the relative directory layout remains intact, the executables will be able to find +and load the accompanying libraries and resources. By invoking the +scons install+ target, +this directory structure is placed into the installation target directory. + +Actually, this lookup process at application startup is performed in two phases + +- in the first phase the main application locates the directly linked shared libraries. + Especially those libraries belonging first class to the Lumiera application are built + with a relative searche path ('rpath' with $ORIGIN token). These libraries are placed + into the +modules/+ subfolder + +- after successfully launching the executable, the second phase performes a lookup programatically, + starting from the path location of the executable, trying to find a +setup.ini+ which defines + additional plug-ins and resources to load. Notably, the GUI to launch is loaded as a + plug-in through this mechanism. Moreover, this bootstrap configuration defines the + additional platform and user configuration to load for further outfitting of the system. + +LSB Installation Layout +~~~~~~~~~~~~~~~~~~~~~~~ +This organisation is used as foundation for packaging and installing. The primariy application +distribution structure will be located into a subfolder below +/usr/lib/+. Only the main application +executable will be symlinked into +/usr/bin/+. The LSB explicitly allows for such a layout, which is +typically used by large application bundles (OpenOffice, Gimp, Eclipse). Since the application +startup encompasses two phases, loading the extended configuration programmatically after +launching the application, such extended resources can easily be relocated into a separate +folder below +/usr/share/+, as required by LSB. + + +Releases, Branches and Conventions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Official releases are marked by a tag on the master branch. Usually, we'll also fork a +*release branch* at that point, featuring bugfixes only. From here we'll merge to the +*debian branch* + + +Package build commands +~~~~~~~~~~~~~~~~~~~~~~ +To (re)build the debian package + +. +git clone git:/git.lumiera.org/lumiera/debian+ +. +mkdir pack.deb+ +. +cd debian+ +. +git-buildpackage --git-upstream-branch=+ _RELEASE-TAG_ + +-- here _RELEASE-TAG_ denotes the point in the Git history, which should become +the reference source and be packaged into the *.orig.tar.gz. Usually, it's just +sufficient to use 'master' for that purpose. + + +Debian-Depot for installation via Apt +------------------------------------- +In addition to the packaging, we maintain a dedicated Apt-Repository for automated +installation and upgrades. We try to build the package for several Debian derived +distributions (like Ubuntu). -> link:../infra/debianDepot.html[more on the repository organisation] + diff --git a/doc/technical/build/SCons.txt b/doc/technical/build/SCons.txt new file mode 100644 index 000000000..46b8647a3 --- /dev/null +++ b/doc/technical/build/SCons.txt @@ -0,0 +1,191 @@ +SCons Build-System +================== +:author: Ichthyo +:date: 2012 + +//MENU: label SCons Build + +Lumiera uses a build system based on link:http://scons.org[SCons] + +SCons is an open source software construction tool based on Python build definition scripts. +Within these build scripts, we define a data structure to describe the parts and dependencies +of our software. When executed, SCons evaluates those definitions and the actual files in +the source tree to derive a build strategy, which is then performed. + +SCons core concepts +------------------- +^_this section is based on the introductory pages on the link:http://www.scons.org/wiki/BasicConcepts[SCons Wiki]_^ + +.SCons Environment +When SCons starts building the project, it creates its own environment with dependency trees, +helper functions, builders and other stuff. The SCons environment is created in memory and some parts of it +are saved to disk to speed up things on the next start. The definition of the build happens within this +artificial build environment. This often confuses people who used Makefiles, where environment is actually +the System Environment. + +.System Environment +the familiar operating system container with environment variables such as PATH, HOME etc. +It is usually accessible via os.environ mapping in Python and therefore in SCons too. +SCons doesn't import any settings from System Environment automatically (like flags for compilers, +or paths for tools), because it's designed to be a cross platform tool with _predictable behaviour._ +That's why, if you rely on any system PATHs or environment variables -- you need to extract +those settings explicitly in your build definition. + +.SConstruct +when SCons executes, it performs a build definition python script written by the user. +By convention, this main script is called 'SConstruct' and is located in the root of the source tree. +It is a full featured Python module executed within a specifically prepared environment. + +.SConscript +these files are also SCons scripts, but they are placed in subdirectories of the project. +Typically they are used to organize hierarchical builds and are included from the main SConstruct file +from the project root. Often, all of the actual build definitions reside in SConscript files in +the sub-trees of the project. + +.Builder +The SCons buildsystem revolves around the metaphor of a _Builder_. This is a SCons object that you explicitly +invoke from the scripts to _define_ that there is something to build, transforming a _source_ into a _target_. +So the target depends on the sources, and typically those _source nodes_ were created by previous builder invocations. +The power of SCons is in the fact that dependencies are tracked automatically. When source files change, the system +is able to derive which targets to rebuild. + +.Scanner +when defining a builder, SCons relies on modular scanner components to ``understand'' the source of the build step. +They may scan source files to discover additional dependencies referenced inside. Thus, SCons comes with built-in +knowledge about the source files and artefacts to be created by a typical build, and further types can be added +through plug-ins. + +.Tool +any further, external component that adds Builders, Scanners and other helpers to SCons environments +for use within scripts. There are special tools for _configuring the platform_ to detect libraries and +further requirements. Relying on those tools. the build environment will be outfitted to reflect the +needs of the specific build. Sub-environments with special configuration may be created. + +.Target +any _node_ or ``build step'' encountered through the definition of the build is a _target_. The actual build +will be triggered by requesting a target, which typically might be just an executable known to reside at some +location in the tree. Special _alias targets_ may be defined, based on other targets, to trigger off standard +build situations. Especially, a _default_ target may be defined. + +'''' + +Organisation of the Lumiera SCons build +--------------------------------------- +Within our build system, we exploit the power of the Python programming language to create abstractions +tailored to the needs ouf our project. Located in the `admin/scons` subdirectory, you'll find a collection +of Python modules defining these building blocks. + +- the *LumieraEnvironment* is created as a subclass of the standard SCons build environment; it is + outfitted with pre-configured custom builders for executables, libraries, extension module, Lumiera plug-in + and icon resources. +- all these *custom builders* implement a set of conventions and directory locations within the tree. + These are defined (and can be adjusted) in the *Setup.py* module. This way, each builder automatically + places the generated artefacts at standard build and installation locations. +- for defining individual targets and builder invocations, we rely on *build helpers* to process whole + *source sub trees* rather than individual files. Mostly, just placing a source file into the appropriate + sub tree is sufficient to get it compiled, linked and installed in a standard way. + +Sub-trees +~~~~~~~~~ +.the source tree +All sourcecode of the core application resides below `src/`. Building these components is controlled by +the SConscript residing in this application source root. By convention, this is also the root for header +includes -- _all headers should be included relative_ to `src/`. + +.the three layers +Within this application core tree, there are sub-trees for the main layers comprising the application. +Each of these sub-trees will be built into a shared library and then linked against the application framework +and common services residing in `src/common`. Besides, there is a sub-tree for core plug-ins and support tools. + +.the GTK Gui +one of these sub-trees, residing in `src/gui` forms the upper layer or user-interaction layer. Contrary to +the lower layers, the GUI is _optional_ and the application is fully operational _without Gui._ Thus, the +GTK Gui is built and loaded as Lumiera a plug-in. + +.unit tests +Since we're developing test-driven, about half of the overall code can be found in unit- and integration +tests, residing below `test/`. There is a separate SConscript file, defining the various kinds of test +artefacts to be created. + +- plain-C tests are defined in _test-collections_, grouped thematically into several subdirectories. + Here, each translation unit provides a separate +main()+ function and is linked into a stand-alone + executable (yet still linked against the appropriate shared libraries of the main application layers) +- the tests covering C++ components are organised into test-suites, residing in separate sub-trees. + Currently (as of 1/2012), there is the *library suite* and the *proc components suite*. Here + individual translation units define individual test case classes, which are linked together with + a testrunner +main()+ function. + +.research +There is a separate subtree for research and experiments. The rationale being to avoid re-building most +of the core application when it comes to experimenting and trying out new technologies. + +.icons and resources +the +data/+ subtree holds resources, configuration files and icons for the Gui. Most of our icons +are defined as SVG graphics. The build process creates a helper executable (+rsvg_convert+) to render +these vector graphics with the help of lib Cairo into icon collections of various sizes. + +.documentation +Most of the documentation is written in Asciidoc and provided online at link:{ldoc}[the documentation section] +of our website. The plain-text sources of this documentation tree is shipped alongside with the code. +Besides, we build *Doxygen* API documentation there, and we create design and technical specs and drawings +in SVG and in UML. + +.the target directory +This is where the results of the build process are created. Lumiera is organised into a +_self contained folder structure_. As long as the relative locations, as found within +target/+, +are kept intact, the Application will be able to start up and find all its resources. Consequently, +there is no need to ``install'' Lumiera (and the ``install'' target just copies this folder structure +into the standard installation locations of a typical Unix system) + +Unfortunately SCons is a bit wired regarding the object files created during the build process. +So currently, we're just building in-tree. Apologies for that. + + +Invoking the Build +~~~~~~~~~~~~~~~~~~ +All of the build process is launched through the `scons` python script, usually installed into +`/usr/bin` when installing the SCons package onto the system. Just invoking + + scons -h + +prints a summary of all custom options, targets and toggles defined for our build. + +Targets +^^^^^^^ +- *build* is the default target: it creates the shared libs, the application, core plug-ins and the Gui. +- *testcode* additionally builds the research and uint test code +- *check* builds testcode and runs our testsuites +- *research* builds just the research tree +- *doc* builds documentation (currently just Doxygen) +- *all* builds the Application, testsuites and documentation +- *install* builds and installs the Lumiera Application + +By convention, invoking +scons -c+ will _clean up_ everything the given target _would_ build. +Thus, invoking ++scons -c /++ is the most global clean operation: it will clean up al build artefacts and +will un-install Lumiera (recall: every defined node, or directory is also a target). + +Configure checks +^^^^^^^^^^^^^^^^ +SCons doesn't know a separate ``configure'' step. The necessary dependency detection is performed +before each build. Currenntly, we expect _all dependencies to be installed first-class_ into the +system. Please use your package manager. + +Caching and MD5 sums +^^^^^^^^^^^^^^^^^^^^ +SCons stores MD5 sums of all source files, all configure checks and all the command lines used +to invoke compilers and external tools. The decision, what needs to be rebuilt is based entirely +on these checksums. For one, this means that configure checks are re-run only when necessary. +It also means that changes to some compiler switches will automatically cause all affected parts +of the application to be re-built. And of course it means, that you only ever compile what is +necessary. + +With SCons, there is no need for the usual ``cleaning paranoia''. Similarily, there is no need +for CCache (but using DistCC rocks !). Unfortunatly, calculating those MD5 sums requires some +time on each build, even if the net result is that nothing will happen at all. + +Configuration options +^^^^^^^^^^^^^^^^^^^^^ +We provide several custom configuration options (run +scons -h + to get a summary). All of these +options are *sticky*: once set, the build system will recall them in a file '.optcache' and apply +them the same way in subsequent builds. It is fine to edit '.optcache' with a text editor. + diff --git a/doc/technical/build/index.txt b/doc/technical/build/index.txt index a9b3261da..41a7dd76d 100644 --- a/doc/technical/build/index.txt +++ b/doc/technical/build/index.txt @@ -3,13 +3,17 @@ Lumiera build system As work progresses, we will add more information on the Lumiera build system. +//Menu: label Build System +//Menu: prepend child 'Dependencies' +//Menu: prepend child 'SCons' + + build -- continuous integration -- packaging -* SCons -* Autotools -* Dependencies +* link:SCons.html[Buildsystem] +* link:Dependencies.html[Dependencies] * link:BuildDroneDraft.html[»Builddrone« concept from 2008] -* Packaging: Debian RPM -* Lumiera debian depot +* Packaging: link:LumieraDebianPackage.html[Debian] RPM +* Lumiera link:../infra/debianDepot.html/[debian depot] diff --git a/doc/technical/code/codingGuidelines.txt b/doc/technical/code/codingGuidelines.txt new file mode 100644 index 000000000..1a0aeb89e --- /dev/null +++ b/doc/technical/code/codingGuidelines.txt @@ -0,0 +1,117 @@ +Coding Guidelines +================= +:Date: Autumn 2011 + + +_this page summarises some style and coding guidelines for the Lumiera code base_ + +Style Guide +----------- +The Lumiera project uses GNU indentation style with slight adaptations. + +- *no tabs* please. The typical ``semi indent'' of GNU style thus becomes 2 spaces. +- maximum line length is rather around *110 characters*. +- originally, GNU style focussed on plain-C code. + + We thus apply some relaxations and clarifications ... + + * the braces for a class scope are indented by 2 spaces + * the access modifiers start at this brace level, while all declarations and definitions + within the class are again indented by 2 spaces + * the line breaking rules are relaxed. Definitions and statements may be written as single line, + provided that the length remains below 110 chars and the result _remains legible_. Otherwise, + we'll fall back to the default and wrap the lines. More specifically + + ** function declarations may be written in one line + ** same for definitions with just a single statement in the body + ** same for simple if-statements without else-branch. + + * the space between function name and opening parenthesis of the argument list is not + enforced when this doesn't make sense, esp. for argument-less functions, chained calls + or constructor syntax. But in all other cases, we really value this additional space, + it improves legibility. + * template argument declarations are _always_ written on a separate line, above the + return type declaration. This rule holds even if the rest of a definition can be + written within a single line. + * the opening brace of namespaces is placed on the same line. Optionally, the namespace + body may be indented, as long as this helps underpinning the nesting structure. But + there is no need to use 3 indents on a 3 level nested namespace. One level is enough + to highlight the presence of a nesting. + +Naming conventions +~~~~~~~~~~~~~~~~~~ +Naming conventions are used to characterise the kind of element at hand and give a visual +clue to the reader. We use our own conventions -- there is no point in arguing that this +and that library or language uses other conventions. + +- type names start with an uppercase letter +- variable and function names start with lowercase. +- fields within a class, especially the private ones are decorated with a trailing underscore +- a leading underscore may be used to emphasize the strictly internal or technical nature of a type, + variable or function +- namespaces are all-lowercase +- macros and constants are preferably all-caps (at least where this makes sense) + +There is a preference for *CamelCase* -- yet underscores are acceptable, especially when the +name is more like a sentence than just a compound term. + +plain-C names +^^^^^^^^^^^^^ +Since C has no namespaces, we strictly require a +lumiera_+ prefix on all non-local names and definitions. +Generally, names should be formed according to the following pattern: + + namespace[_object][_verb[_subjects]][_version] + +In case a definition actually denotes an object, there should be + +- a basic struct name: `typedef struct namespace_foo_struct namespace_foo;` +- plus an object pointer/handle: `typedef namespace_foo* NamespaceFoo;` + +The object pointer/handle should be passed as 1^st^ argument with the name +self+ + + +General Code Arrangement and Layout +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Headers and translation units are named `*.hpp` and `*.cpp` rsp. `*.h` and `*.c` + + Multilingual headers are called `*.h` +- Each header should be named according to the primary facility it exposes. For the filesystem name, + the +CamelCaseWords+ of this type are translated into +camel-case-words.hpp+ +- Each file should start with the GNU licence preamble. The headline should give a one-line summary. + The primary author(s) and the year of the initial copyright claim should be mentioned. +- Each header should be focussed on a specific purpose. Preferably it starts with a file-level + doxygen comment explaining the intention and anything not obvious from reading the code. +- when arranging headers and compilation units, please take care of the compilation times and the + code size. Avoid unnecessary includes. Use forward declarations where applicable. + Yet still, _all immediately required includes should be mentioned_ (even if already included by + another dependency) +- The include block starts with our own dependencies, followed by a second block with the library + dependencies. After that, optionally some symbols may be brought into scope (through +using+ clauses). + Avoid cluttering top-level namespaces. Never import full namespaces (no +using namespace boost;+ please!) +- the includes for our own dependencies shall be given relative to source-root (or test root). Don't use + relative includes for headers located in the same directory, or -- worse still -- in the parent directory. +- sometimes, the actual translation units will combine several facilities for technical reasons, e.g. + when sharing an implementation-level class or even singleton instance. + Anonymous namespaces should be used liberally to avoid unnecessary exports. +- template code mostly needs to reside in headers. (same for metaprogramming code). + We employ the simple inclusion model (``Borland model'') for template instantiation. +- But in some specific situations it is preferable to drive explicit instantiations from within + a +*.cpp+ file. Most notably this is the case when defining some core class hierarchies. + Such explicit instantiations should be limited to just a view obvious places. They should be + written into a block at the end of some central implementation file. See +assetmanager.cpp+ + for an example. +- deliberately there is _no single top-level namespace._ The +namespace lumiera+ is the root of + our _exported interfaces_ -- intended for use by external scripts and libraries. Everything + implementation related is arranged in per-subsystem trees of namespaces. The APIs of the + subsystems are exported explicitly into the +lumiera+ namespace. + + +Design Guidelines and Values +---------------------------- +Code is written for *being read by humans*; code shall convey its meaning even to the casual reader. +On the long run, this language nature of code is more important than any performance tweaks. Recall, +every idiot can figure out how to make a computer perform something. Yet the real challenge is to +write legible code. Code that operates exactly the way you'd guess just from reading it. +Black magic and all kinds of surprise trickery and cleverness are nothing to be proud off. + +-> please have a look at the link:/project/background/CleanCodeDevelopment.html[Clean Code] page +for a coherent system of design principles + diff --git a/doc/technical/code/darkCorners.txt b/doc/technical/code/darkCorners.txt new file mode 100644 index 000000000..03bd9311c --- /dev/null +++ b/doc/technical/code/darkCorners.txt @@ -0,0 +1,54 @@ +Dark Corners +============ + +_this page accounts for some problematic areas, sketchy solutions, +nonportable hacks, terrorism and other misdemeanour_ + +Library +------- + +Equality of Functors +~~~~~~~~~~~~~~~~~~~~ +One of the more important recent additions to the C++ language are function objects. +In addition to the features actually provided by the boost implementation, the tr1 report +also requires function instances to implement an equality operator. Unfortunately the +implementation approach choosen by boost makes a 100% correct implementation of +comparision very dificult, if not impossible. Thus, the boost developers refused +to implement this feature. + +The bad news is that really using the power of opaque function objects quickly drove +us (Lumiera) into a situation where such an equalty test and a hash calculation on +function objects would be necessary. The whole point of using function objects is +the ability to ``erase'' specific details, which has the downside that the resulting +generic objects are opaque and often dificult to manage, when it comes to storing +and retrieving objects building on such functors. + +Thus I built an hack, based on the implementation details of boost::function. +In +functor-util.hpp+ we define a +class HijackedFunction+, which has the same +data layout as the original boost::function. After forcibly casting such an function +(reference or pointer) into a +HijackedFunction+, we're able to inspect and evaluate +the implementation pointers for equality comparison and hash value calculation. +This approach works and actually detects copied functions to be _equal_, but is +unable to pinpoint _equivalence_, e.g. functors bound to the same function with +the same arguments through separate but otherwise identical invocations of +bind+. +Besides, should boost or the standard library implementors eventually change the +implementation, this workaround will break. + + +Size of standard library facilities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sometimes we need to know the size of an STL or Boost class, but can't afford +to include the header and just write a `sizeof()`. Because including some of those +headers incurs quite some price in terms of compilation time and even size of the +debug executable. + +Obviously, a simple solution would be to measure those sizes and hardcode them. +But what about portability? To get out of that dilemma, I created a traits class +which mimics the implementation memory layout of those facilities in question, +simplified as much as possible. As long as the GNU libstdc++ or Boost don't +change their implementation layout, this give us precise and fast size bounds. + +When relying on that hack, we should make sure always to place some kind of +STATIC_ASSERT into the corresponding implementation files to ensure the real +facilites actually _do fit_ into the guessed storage dimensions. + diff --git a/doc/technical/code/index.txt b/doc/technical/code/index.txt new file mode 100644 index 000000000..f0f1553f8 --- /dev/null +++ b/doc/technical/code/index.txt @@ -0,0 +1,49 @@ +Code Base Organisation +====================== + +//Menu: label Code Base + + +This section of Lumiera's technical documentation deals with the code base as such, +not so much with the internals of the Lumiera application. It is targeted towards +people participating in Lumiera development. + +The Lumiera source base is huge and expected to grow for some time to come. Just +judging by the _lines of code_, Lumiera can be considered as a *medium sized system*, +which gives us some hints towards the adequate practices, conventions and the +degree of formalism to apply. + +Generally speaking, a *small system* doesn't exceed the mental capacity of a single +knowledgeable human. A single person can know everything and understand everything +in such a code base. Thus, any formalism and organisational overhead is detrimental; +we're best off doing away with ``Methods'' and just rely on taste and craftsmanship. + +But there is a hard limit for this approach, kind of a _sonic wall_ -- usually somewhere +around a size of 60000 LoC. At that point, the adequate value system flips around as a +whole. Since beyond that point, no single person can understand and know everything about +such a system, _communication and understanding become the primary concerns_ of the +development process. Suddenly, we _do need_ some organisation, some rituals and +a methodical approach. Yet still we don't need a high degree of formalism, since +at least we can easily _know and recall everyone involved_. + +To give a delineation to the other side, a constantly growing project might eventually +reach the point where the actual coding of new functionality makes up less than 25% +of the overall work required. More people might be involved than anyone can recall or +overlook reasonably. A code size of 1 Million LoC seems to exceed any kind of sufficiently +precise imagination. From that point on, a rigorosely formalsed organisation is vital +to keep such a *large scale project* from floundering. + +//// +Conventions +----------- +_to be written_ + +Guidelines +---------- +//// + +'''' + * also see the link:{ldoc}/devel/rfc.html[Design Process] for ongoing discussions + * see the link:/devs-vault/[Developers Vault] for frequently used developer's resources + + diff --git a/doc/technical/howto/DebugGdbPretty.txt b/doc/technical/howto/DebugGdbPretty.txt index b46c70a72..e1a2c6f97 100644 --- a/doc/technical/howto/DebugGdbPretty.txt +++ b/doc/technical/howto/DebugGdbPretty.txt @@ -5,16 +5,24 @@ Since some time, GDB supports Python written extension modules, especially for pretty printing of data structures. A selection of pre-defined pretty printers for STL containers is part of the standard distribution. The graphical debugger frontend provided by the Eclipse CDT automatically uses this debugger provided presentation -to show the value of variables in the detail pane of the variables view, while the -individual variable entries always show the expandable structure view. +to show the value of variables in the detail pane of the variables view. The most recent +version of CDT (Version 8 for Eclipe 3.7 »Indigo«) is even able to populate the structure view +based on the python pretty printer's output, which is a big step ahead towards easy debugging +of more elaborate data structures based on STL containers. + Installation notes ------------------ -This feature 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+ +This feature requires an python enabled GDB (>6.8.50). Actually, even the most recent stable +GDB (Version 7.2) is recommended, because it contains some relevant bugfixes. Debian users +might want to backport the the GDB package from Debian/Wheezy (GDB-7.2). +Moreover, you need to check out and install a recent version of the python pretty-printers +from the GNU Subversion repository: + +* 'svn checkout' + http://gcc.gnu.org/viewcvs/trunk/libstdc%2B%2B-v3/python/[svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python stlPrettyPrinter] +* you need to activate them explicitly through your +~/.gdbinit [source,python] ---- @@ -61,4 +69,8 @@ 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. +NOTE: to get the full support in _Eclipse Indigo + CDT-8_, you need to activate + the setting ``enable pretty printers in variable/expression tree'', which + can be accessed as Window/Preferences > C++ / debug / GDB + diff --git a/doc/technical/howto/HashFunctions.txt b/doc/technical/howto/HashFunctions.txt new file mode 100644 index 000000000..b30e23989 --- /dev/null +++ b/doc/technical/howto/HashFunctions.txt @@ -0,0 +1,111 @@ +Hash functions (C++) +==================== + +_This page is for collecting know-how related to hash functions and hash tables._ + +The original STL was lacking proper support for hashtables, hash based associative arrays +and hash calculation in general. To quite some developers, hash tables feel like some kind +of _impure_ data structure -- unfortunately the properties of modern CPUs turned the balance +significantly in favour of hash tables due to memory locality. Pointer based datastructures +can't be considered especially _performant_ as they were in the good old times. + +The tr1 extension and the new C++11 standard amended the problem by defining a framework +for hash functions and hash tables. When sticking to some rules, custom written hash functions +can be picked up automatically by the standard library and -containers. + +Standard Hash Definitions +------------------------- + +Hash values:: + hash values are unsigned integral numbers of type 'size_t' ++ +Basically this means that the range of hash values roughly matches the memory address space. +But it also means that this range is _platform dependant_ (32 or 64bit) and -- given the usual +hash calculation based on modulus (wrap around) -- that generated hash values are nonportable. + +Hash function:: + a hash function calculates a hash value for objects of its argument type. Thus, for every + supported type, there is a dedicated hash function. Quite some hash functions are generated + from function templates though. + +Hash functor:: + a function object able to calculate hash values when invoked. The standard library and the + corresponding boost libraries accept functors of type 'hash' to calculate hash values + for objects or values of type 'TY' + +Hash based containers:: + While the standard Set and Map types (including the Multiset and Multimap) are based on + balanced binary trees, the new C\+\+11 standard includes hash based variants (with name + prefix +unordered_+). These hashtable based containers require a +hash+ functor + to be able to derive the hash value of any encountered key value. Hash functors may + be provided as additional type parameter to the container; if omitted, the compiler + tries to find a (maybe custom defined) hash functor by *ADL* (see below) + + +C++11 versus Boost +~~~~~~~~~~~~~~~~~~ +The Boost library *functional-hash* provided the foundation for the definition now accepted +into the new C++ standard. Yet the boost library provides some additional facilities not +part of the standard. Thus we're bound to choose + +* either including ++ and +using std::tr1::hash+ +* or including ++ and +using boost::hash+ + +The boost version additionally provides pre defined hash functors for STL containers holding +custom types -- and it provides an easy to use extension mechanism for writing hash functions +for custom types. Effectively this means that, assuming usage of the boost-include, the actual +implementation and the way it is picked up is _different but compatible_ to the standard way. + +Boost: hashing custom types +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The extension mechanism used by the boost version is best explained by looking +at the code + +.boost/functional/hash/extensions.hpp +[source,C] +---- + template struct hash + : std::unary_function + { + std::size_t operator()(T const& val) const + { + return hash_value(val); + } + } +---- +So this templated standard implementation just _invokes an unqualified function_ +with the name +hash_value(val)+ -- when instantiating this template for your custom +class or type, the compiler will search this function not only in the current scope, +but also in the namespace defining your custom type +T+ (this mechanism is known as +``**A**rgument **D**ependant **L**ookup''). Meaning that all we'd need to do is to define a +free function or friend function named +hash_value+ alongside with our custom data types (classes). + +To further facilitate providing custom hash functions, boost defines a function ++boost::hash_combine(size_t seed, size_t hashValue)+, allowing to _chain up_ the +calculated hash values of the parts forming a composite data structure. + +- see Lumiera's link:http://git.lumiera.org/gitweb?p=LUMIERA;a=blob;f=src/proc/asset/category.hpp;h=b7c8df2f2ce69b0ccf89439954de8346fe8d9276;hb=master#l104[asset::Category] + for a simple usage example +- our link:http://git.lumiera.org/gitweb?p=LUMIERA;a=blob;f=src/lib/symbol-impl.cpp;h=9e09b4254ac57baefeb0a0c06ccd423318e923c1;hb=master#l67[lib::Symbol datatype] + uses the standard implementation of a string hash function combining the individual + character's hashes. + + + +LUID values +----------- +Lumiera's uniform identifier values shouldn't be confused with regular hash values. +The purpose of LUID values is to use just plain random numbers as ID values. But, because +of using such a incredibly large number space (128bit), we can just assume any collision +between such random LUID to be so unlikely as to reasonably ignore this possibility +altogether. Let's say, the collision of random LUID values won't ever happen, same as +the meltdown of an atomic power plant, which, as we all know, won't ever happen either. + +Relation to hash values +~~~~~~~~~~~~~~~~~~~~~~~ +When objects incorporate sich an unique LUID, this provides for a prime candidate to +derive hash values as a side-effect of that design: Since incorporating an LUID typically +means that this object has an _distinguishable identity_, all objects with the same LUID +should be considered _equivalent_ and thus hash to the same value. Consequently we can just +use a +size_t+ prefix of the LUID bitstring as hash value, without any further calculations. + diff --git a/doc/technical/howto/UsingBoost.txt b/doc/technical/howto/UsingBoost.txt new file mode 100644 index 000000000..5b3bab017 --- /dev/null +++ b/doc/technical/howto/UsingBoost.txt @@ -0,0 +1,167 @@ +Using Boost Libraries +===================== + +//Menu: label using boost + +_some arbitrary hints and notes regarding the use of link::http://www.boost.org[Boost Libraries] in Lumiera_ + +Notable Features +---------------- +Some of the Boost Libraries are especially worth mentioning. You should familiarise yourself with +those features, as we're using them heavily throughout our code base. As it stands, the C/C\++ language(s) +are quite lacking and outdated, compared with today's programming standards. To fill some of these gaps +and to compensate for compiler deficiencies, some members of the C\++ committee and generally very +knowledgeable programmers created a set of C\++ libraries generally known as *Boost*. Some of these +especially worthy additions are also proposed for inclusion into the C++ standard library. + +.memory +The `` rsp. `` libraries define a family of smart-pointers to serve +several needs of basic memory management. In almost all cases, they're superior to using `std::auto_ptr`. + +When carefully combining these nifty templates with the RAII pattern, most concerns for memory +management, clean-up and error handling simply go away. (but please understand how to avoid +circular references and care for the implications of parallelism though) + +.functional +The `function` template adds generic functor objects to C++. In combination with the `bind` function +(which binds or ties an existing function invocation into a functor object), this allows to ``erase'' (hide) +the difference between functions, function pointers and member functions at your interfaces and thus enables +building all sorts of closures, signals (generic callbacks) and notification services. Picking up on these +concepts might be mind bending at start, but it's certainly worth the effort (in terms of programmer +productivity) + +.hashtables and hash functions +The `unordered_*` collection types amend a painful omission in the STL. The `functional_hash` library +supplements hash function for the primitive types and a lot of standard constructs using the STL; moreover +there is an extension point for using custom types in those hashtables +(-> read more link:HashFunctions.html[here...]) + +.noncopyable +Inheriting from `boost::noncoypable` inhibits any copy, assignment and copy construction. It's a highly +recommended practice _by default to use that for every new class you create_ -- unless you know for sure +your class is going to have _value semantics_. The C++ language has kind of a ``fixation'' on value +semantics, passing objects by value, and the language adds a lot of magic on that behalf. Which might lead +to surprising results if you aren't aware of the fine details. + +.type traits +Boost provides a very elaborate collection of type trait templates, allowing to ``detect'' several +properties of a given type at compile time. Since C++ has no reflection and only a very weak introspection +feature (RTTI, run time type information), using these type traits is often indispensable. + +.enable_if +a simple but ingenious metaprogramming trick, allowing to control precisely in which cases the compiler +shall pick a specific class or function template specialisation. Basically this allows to control the +code generation, based on some type traits or other (metaprogramming) predicates you provide. Again, +since C++ is lacking introspection features, we're frequently forced to resort to metaprogramming +techniques, i.e to influence the way the compiler translates our code from within that very code. + +.STATIC_ASSERT +a helper to check and enforce some conditions regarding types _at compile time_. +Because there is no support for this feature by the compiler, in case of assertion failure +a compilation error is provoked, trying to give at least a clue to the real problem by +creative use of variable names printed in the compiler's error message. + +.metaprogramming library +A very elaborate, and sometimes mind-bending library and framework. While heavily used within +Boost to build the more advanced features, it seems too complicated and esoteric for general purpose +and everyday use. Code written using the MPL tends to be very abstract and almost unreadable for +people without math background. In Lumiera, we _try to avoid using MPL directly._ Instead, we +supplement some metaprogramming helpers (type lists and tuples), written in a simple LISP style, +which -- hopefully -- should be decipherable without having to learn an abstract academic +terminology and framework. + +.lambda +In a similar vein, the `boost::lambda` library might be worth exploring a bit, yet doesn't add +much value in practical use. It is stunning to see how this library adds the capability to define +real _lambda expressions_ on-the-fly, but since C++ was never designed for language extensions of +that kind, using lambda in practice is surprisingly tricky, sometimes even obscure and rarely +not worth the hassle. (An notable exception might be when it comes to defining parser combinators) + +.operators +The `boost::operators` library allows to build families of types/objects with consistent +algebraic properties. Especially, it eases building _equality comparable_, _totally ordered_, +_additive_, _mulitplicative_ entities: You're just required to provide some basic operators +and the library will define all sorts of additional operations to care for the logical +consequences, removing the need for writing lots of boilerplate code. + +.lexical_cast +Converting numerics to string and back without much ado, as you'd expect it from any decent language. + +.boost::format +String formatting with `printf` style directives. Interpolating values into a template string for +formatted output -- but typesafe, using defined conversion operators and without the dangers of +the plain-C `printf` famility of functions. But beware: `boost::format` is implemented on top of +the C++ output stream operations (`<<` and manipulators), which in turn are implemented based +on `printf` -- you can expect it to be 5 to 10 times slower than the latter, and it has +quite some compilation overhead and size impact (-> see our own +link:http://git.lumiera.org/gitweb?p=lumiera/ichthyo;a=blob;f=src/lib/format-string.hpp;h=716aa0e3d23f09269973b7659910d74b3ee334ea;hb=37384f1b681f5bbfa7dc4d50b8588ed801fbddb3[formatting front-end] +to reduce this overhead) + +.variant and any +These library provide a nice option for building data structures able to hold a mixture of +multiple types, especially types not directly related to each other. `boost::variant` is a +typeseafe union record, while `boost::any` is able to hold just any other type you provide +_at runtime_, still with some degree of type safety when retrieving the stored values. +Both libraries are compellingly simple to use, yet add some overhead in terms of size, +runtime, and compile time. + +.regular expressions +Boost provides a full blown regular expression library, supporting roughly the feature set of +perl regular expressions. The usage and handling is somewhat brittle though, when compared +with perl, python, java, etc. + +.program-options and filesystem +Same as the aforementioned, these two libraries just supply a familiar programming model for these tasks +(parsing the command line and navigating the filesystem) which can be considered quasi standard today, +and is available pretty much in the same style in Java, Python, Ruby, Perl and others. + + +Negative Impact +--------------- +Most Boost libraries are _header only_ and all of them make heavy use of template related features of C++. +Thus, _every inclusion of a Boost library_ might lead to _increased compilation times._ We pay that penalty +per compilation unit (not per header). Yet still, using a boost library within a header frequently included +throughout the code base might dangerously leverage that effect. + +debug mode +~~~~~~~~~~ +Usually, when developing, we translate our code without optimisation and with full debugging informations +switched on. Unfortunately, C++ templates were never designed to serve as a functional metaprogramming language +to start with -- but that's exactly what we're (ab)using them for. The Boost libraries drive that to quite +some extreme. This leads to lots and lots of debugging information to be added to the object files, +mentioning each and every intermediary type created in the course of expanding the metaprogramming +facilities. Even seemingly simple things may result in object files becoming several megabytes large. + +Fortunately, all of this overhead gets removed when _stripping_ your executable and libraries (or when +compiling without debug information). So this is solely an issue relevant for the developers, as it increases +compilation, linking and startup times. + +runtime overhead and template bloat +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The core Boost libraries (the not-so experimental ones) have a reputation for being of very high +quality. The're written by experts with a deep level of understanding of the language, the usual +implementation and the performance implications. Mostly, those quite elaborate metaprogramming +techniques where chosen exactly to minimise runtime overhead or code size. + +Since each instantiation of a template constitutes a completely new class, carelessly written +template code can lead to heavily bloated executables. Every instantiated _function_ and every +class with _virtual methods_ (i.e. with a VTable) adds to the weight. But this negative effect can +be balanced by the ability of reducing inline code. According to my own experience, I'd be much +more concerned _about my own code adding template bloat,_ then being concerned about the Boost +libraries (those people know very well what they're doing...) + +some practical guidelines +~~~~~~~~~~~~~~~~~~~~~~~~~ +- `boost::format`, `boost::variant`, `boost::any`, `boost::lambda` and the more elaborate metaprogramming + stuff adds considerable weight. A good advice is to confine those features to the implementation level: + use them within individual translation units (`*.cpp`) where this makes sense, but don't cast + general interfaces in terms of those library constructs. +- the _functional_ tools (`function`, `bind` and friends), the _hash functions_, _lexical_cast_ and the + _regular expressions_ create a moderate overhead. Probably fine in general purpose code, but you should + be aware that there is a price tag. About the same as with many STL features. +- the `shared_ptr` `weak_ptr`, `intrusive_ptr` and `scoped_ptr` are really indispensable and can be used + liberally everywhere. Same for `enable_if` and the `boost::type_traits`. The impact of those features + on compilation times and code size is negligible and the runtime overhead is zero, compared to _performing + the same stuff manually_ (obviously a ref-counting pointer like `smart_ptr` has the size of two raw pointers + and incurs an overhead on each creation, copy and disposal of a variable -- but that's besides the point I'm + trying to make here, and generally unavoidable) + diff --git a/doc/technical/howto/index.txt b/doc/technical/howto/index.txt index 20450cd49..49970fc99 100644 --- a/doc/technical/howto/index.txt +++ b/doc/technical/howto/index.txt @@ -12,3 +12,5 @@ similar usefull pieces of information targeted at Lumiera developers. See also == Notepad - link:DebugGdbPretty.html[Python pretty printers for GDB] +- link:HashFunctions.html[Notes regarding standard hash functions] + diff --git a/doc/technical/index.txt b/doc/technical/index.txt index 147751f86..d623cbfb6 100644 --- a/doc/technical/index.txt +++ b/doc/technical/index.txt @@ -1,6 +1,7 @@ Technical Documentation ======================= +// Menu : prepend child library // Menu : prepend child backend // Menu : prepend child proc // Menu : prepend child gui @@ -29,7 +30,9 @@ components. == Tools .Development -* link:http://www.lumiera.org/doxy/[*Doxygen generated documentation*] : Complete reference for the code of Lumiera. +* link:http://www.lumiera.org/doxy/[*Doxygen generated documentation*] : API documentation of the Lumiera code. +* organisation of the link:code/index.html[Code Base] in general +* link:code/codingGuidelines.html[Coding Style] and further guidelines .Building * link:build/index.html[*Buildsystem*] : Installation & compilation tools, dependencies and packaging. diff --git a/doc/technical/infra/debianDepot.txt b/doc/technical/infra/debianDepot.txt index f599950b2..8573e3803 100644 --- a/doc/technical/infra/debianDepot.txt +++ b/doc/technical/infra/debianDepot.txt @@ -25,7 +25,7 @@ To (re)build the debian package . +git clone git:/git.lumiera.org/lumiera/debian+ . +mkdir pack.deb+ . +cd debian+ -. +git-buildpackage --git-upstream-branch=+_RELEASE-TAG_ +. +git-buildpackage --git-upstream-branch=+ _RELEASE-TAG_ -- here _RELEASE-TAG_ denotes the point in the Git history, which should become the reference source and be packaged into the *.orig.tar.gz. Usually, it's just @@ -76,15 +76,15 @@ everyday usage ~~~~~~~~~~~~~~ import a package:: - +reprepro -V -C experimental include lenny lumiera_0.pre.01-1+maverick_i386.changes+ + +reprepro -V -C experimental include squeeze lumiera_0.pre.01-1+squeeze_i386.changes+ + this adds the given binary lumiera package, together with all sources and the original -tarball to the 'lenny' repository, into the 'experimental' section +tarball to the 'squeeze' repository, into the 'experimental' section dump out an entire repository:: - +reprepro -V export lenny+ + +reprepro -V export squeeze+ + -this will __re__generate all of the indices, signatures and metadata of the 'lenny' repository +this will __re__generate all of the indices, signatures and metadata of the 'squeeze' repository Configuration @@ -98,12 +98,12 @@ I choose this layout because of the marginal relevance of this depot management; want to create _yet another not so useful Git repository..._) * primary link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=conf/distributions;hb=refs/heads/depot[configuration] - * Logfile of imports: link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=log/lenny.log;hb=refs/heads/depot[for Lenny] + * Logfile of imports: link:http://git.lumiera.org/gitweb?p=lumiera/debian;a=blob;f=log/squeeze.log;hb=refs/heads/depot[for Debian/Squeeze] [NOTE] .some special details to note in our setup ======================================================================================================================= -- each block in the 'distributions' file defines a repository for a ``distribution'' (e.g. Lenny, Lucid, Maverick). +- each block in the 'distributions' file defines a repository for a ``distribution'' (e.g. Squeeze, Lucid, Maverick). Within such a repo, there are sections named 'Components'. - The _override_ files mentioned in the configuration allow to overwrite / replace arbitrary fields in the metadata of all packages added to that distribution. diff --git a/doc/technical/library/index.txt b/doc/technical/library/index.txt new file mode 100644 index 000000000..19a1ba716 --- /dev/null +++ b/doc/technical/library/index.txt @@ -0,0 +1,41 @@ +Library documentation +===================== + +//Menu: label Support Lib + +Alongside with the core Lumiera Application, we're also creating several *Libraries*. + +This section holds pages with in-depth technical documentation regarding those. Please don't +expect a coherent or complete coverage for the time being -- library functionality is typically +``discovered'' while in the middle of the implementation, then factored out, reused and remoulded +over time. + +Interface Library +----------------- +The Lumiera Application is designed deliberately in a way to be controllable by scripts. All of +the Session, the models and the backend are fully operational without the GUI. Any significant +functionality can be addressed through the primary Façade Interfaces. + +But, in order to make such an open and extensible application a reality, we need to build up +an 'interface layer' -- provided as library for client code and script adapters to link against. +This library allows to access the primary external interfaces and exposes everything needed to +work with the specifics of the application. + +[,yellow]#TODO as of 1/2012:# _this is just a nebulous plan for now._ Nothing of the work required +really to address facilities from the outside has been done. All we've managed to achieve is actually +to use clean interfaces and decouple the parts of the application appropriately. + +- we still need to identify those core concepts which need to be exposed +- we need to refactor the corresponding data types to resolve cross dependencies to the implementation +- then we could think about moving those data types into an _official Lumiera interface_ + +Support Library +--------------- +There is a huge collection of generic building blocks, re-used more or less frequently. +The're written in a generic way, but in accordance to the rest of the source base, relying only +on the general error handling and logging framework used within Lumiera. (Some facilities may depend +on other parts of the Application; this can be qualified as a bug -- please complain when you've found +something of that sort). + + + + diff --git a/doc/technical/library/iterator.txt b/doc/technical/library/iterator.txt new file mode 100644 index 000000000..e06b9bf4e --- /dev/null +++ b/doc/technical/library/iterator.txt @@ -0,0 +1,141 @@ +Iterators and Pipelines +======================= + +The link:http://c2.com/cgi/wiki?IteratorPattern[Iterator Pattern] allows to +expose the contents or elements of any kind of collection, set or container +for use by client code, without exposing the implementation of the underlying +data structure. Thus, iterators are one of the primary API building blocks. + +Lumiera Forward Iterator +------------------------ +While most modern languages provide some kind of _Iterator,_ the actual semantics +and the fine points of the implementation vary greatly from language to language. +Unfortunately the C++ standard library uses a very elaborate and rather low-level +notion of iterators, which doesn't mix well with the task of building clean interfaces. + +Thus, within the Lumiera core application, we're using our own Iterator concept, +initially defined as link:{ldoc}/devel/rfc/LumieraForwardIterator.html[RfC], +which places the primary focus on interfaces and decoupling, trading off +readability and simplicity for (lesser) performance. + +.Definition +An Iterator is a self-contained token value, +representing the promise to pull a sequence of data + + - rather then deriving from an specific interface, anything behaving + appropriately _is a Lumiera Forward Iterator._ (``duck typing'') + - the client finds a typedef at a suitable, nearby location. Objects of this + type can be created, copied and compared. + - any Lumiera forward iterator can be in _exhausted_ (invalid) state, which + can be checked by +bool+ conversion. + - especially, default constructed iterators are fixed to that state. + Non-exhausted iterators may only be obtained by API call. + - the exhausted state is final and can't be reset, meaning that any iterator + is a disposable one-way-off object. + - when an iterator is _not_ in the exhausted state, it may be _dereferenced_ + (`*i`), yielding the ``current'' value + - moreover, iterators may be incremented (`++i`) until exhaustion. + + +Motivation +~~~~~~~~~~ +The Lumiera Forward Iterator concept is a blend of the STL iterators and +iterator concepts found in Java, C#, Python and Ruby. The chosen syntax should +look familiar to C++ programmers and indeed is compatible to STL containers and +ranges. To the contrary, while a STL iterator can be thought of as being just +a disguised pointer, the semantics of Lumiera Forward Iterators is deliberately +reduced to a single, one-way-off forward iteration, they can't be reset, +manipulated by any arithmetic, and the result of assigning to an dereferenced +iterator is unspecified, as is the meaning of post-increment and stored copies +in general. You _should not think of an iterator as denoting a position_ -- +just a one-way off promise to yield data. + +Another notable difference to the STL iterators is the default ctor and the ++bool+ conversion. The latter allows using iterators painlessly within +for+ +and +while+ loops; a default constructed iterator is equivalent to the STL +container's +end()+ value -- indeed any _container-like_ object exposing +Lumiera Forward Iteration is encouraged to provide such an `end()`-function, +additionally enabling iteration by `std::for_each` (or Lumiera's even more +convenient `util::for_each()`). + +Implementation +~~~~~~~~~~~~~~ +As pointed out above, within Lumiera the notion of ``Iterator'' is a concept +(generic programming) and doesn't mean a supertype (object orientation). Any +object providing a suitable set of operations can be used for iteration. + +- must be default constructible to _exhausted state_ +- must be a copyable value object +- must provide a `bool` conversion to detect _exhausted state_ +- must provide a pre-increment operator (`++i`) +- must allow dreferentiation (`*i`) to yield the current object +- must throw on any usage in _exhausted state_. + +But, typically you wouldn't write all those operations again and again. +Rather, there are two basic styles of iterator implementations, each of which +is supported by some pre defined templates and a framework of helper functions. + +Iterator Adapters +^^^^^^^^^^^^^^^^^ +Iterators built based on these adaptor templates are lightweight and simple to use +for the implementor. But they don't decouple from the actual implementation, and +the resulting type of the iterator usually is rather convoluted. So the typical +usage scenario is, when defining some kind of custom container, we want to add +a `begin()` and `end()` function, allowing to make it behave similar to a STL +container. There should be an embedded typedef `iterator` (and maybe `const_iterator`). +This style is best used within generic code at the implementation level, but is not +well suited for interfaces. + +-> see 'lib/iter-adapter.hpp' + +Iteration Sources +^^^^^^^^^^^^^^^^^ +Here we define a classical abstract base class to be used at interfaces. The template +`lib::IterSource` is an abstract promise to yield elements of type TY. It defines +an embedded type `iterator` (which is an iterator adapter, just only depending on +this abstract interface). Typically, interfaces declare to return an +`IterSource::iterator` as the result of some API call. These iterators will +hold an embedded back-reference to ``their'' source, while the exact nature of this +source remains opaque. Obviously, the price to pay for this abstraction barrier is +calling through virtual functions into the actual implementation of the ``source''. + +Helpers to define iterators +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +For both kinds of iterator implementation, there is a complete set of adaptors based +on STL containers. Thus, it's possible to expose the contents of such a container, +or the keys, the values or the unique values just with a single line of code. Either +as iterator adapter (-> see 'lib/iter-adapter-stl.hpp'), or as iteration source +(-> see 'lib/iter-source.hpp') + + +Pipelines +--------- +The extended use of iterators as an API building block naturally leads to building +_filter pipelines_: This technique form functional programming completely abstracts +away the actual iteration, focussing solely on the selecting and processing of +individual items. For this to work, we need special manipulation functions, which +take an iterator and yield a new iterator incorporating the manipulation. (Thus, +in the terminology of functional programming, these would be considered to be +``higher order functions'', i.e. functions processing other functions, not values). +The most notable building blocks for such pipelines are + +filtering:: + each element yielded by the _source iterator_ is evaluated by a _predicate function,_ + i.e. a function taking the element as argument and returning a `bool`, thus answering + a ``yes or no'' question. Only elements passing the test by the predicate can pass + on and will appear from the result iterator, which thus is a _filtered iterator_ + +transforming:: + each element yielded by the _source iterator_ is passed through a _transformnig function,_ + i.e. a function taking an source element and returing a ``transformed'' element, which + thus may be of a completely different type than the source. + +Since these elements can be chained up, such a pipeline may pass several abstraction barriers +and APIs, without either the source or the destination being aware of this fact. The actual +processing only happens _on demand_, when pulling elements from the end of the pipeline. +Oten, this end is either a _collecting step_ (pulling all elements and filling a new container) +or again a IterSource to expose the promise to yield elements of the target type. + +Pipelines work best on _value objects_ -- special care is necessary when objects with _reference +semantics_ are involved. + diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index 5688d9dad..919d365c8 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -48,20 +48,28 @@ The extra components are: Coding Style ~~~~~~~~~~~~ -The Lumiera team agreed on using GNU coding style with some exceptions (no -tabs, line wrap must not always be enforced). Otherwise we are a bit pedantic -to be consistent to make the codebase uniform. Function naming conventions -and other details are described in several RFCs. +The Lumiera team agreed on using GNU coding style with slight adaptations. +Generally speaking, we strive to keep the code base consistent and stick to +widely accepted guidelines and best practices. See our separate +link:{ldoc}/technical/code/codingGuidelines.html[Coding Guidelines] page. +Function naming conventions and other details are also described in several RFCs. + Documentation ~~~~~~~~~~~~~ +The central location for all design and technical documentation is the Lumiera +website you're reading right now. Besides that, a summary and introduction +for various components can be found in the file-level doxygen comments, while +details are usualy explained in the class and function level comments. -Every public function should be documented with doxygen comments. The overall -design is outlined in this text and documented in the various detail pages and -accompanying RFCs. Bootstrapping the Lumiera design used several other places, -in several TiddlyWikis, an UML model and cehteh's private wiki, most of this -information is asciidoced meanwhile and in progress to be integrated in the -central documentation hierarchy. +==== the TiddlyWiki +Currently, Lumiera is still in the design- and evolution phase. Documentation +is written as an ongoing effort. +There is an embedded JavaScript wiki (TiddlyWiki) within the source tree, mostly +used as design notebook, featuring day-to-day design sketches and notes, but also +quite some more persistent planning. Finished documentation text is constantly +moved over to the documentation section(s) of the Lumiera website. + +-> access the Proy-Layer link:http://lumiera.org/wiki/renderengine.html[TiddlyWiki online here] Test Driven Development @@ -73,7 +81,7 @@ implemented until they pass their tests. While increasing the initial development effort significantly, this approach is known to lead to clearly defined components and overall increases code quality. In practice, this approach might not be suitable at times, -nevertheless we try to sick to it as far as possible +nevertheless we try to stick to it as far as possible and maintain fairly complete test coverage. @@ -92,14 +100,20 @@ several important parts of the applications are loaded as plug-ins, starting with the GUI. -User Interfaces ---------------- +User Interface(s) +----------------- The purpose of the user interface(s) is to act on the _high-level data model_ contained within the Session, which belongs to the _processing layer_ below. User interfaces are implemented as plug-ins and are pulled up on demand, they won't contain any relevant persistent state beyond presentation. +_As of 2011, the one and only interface under active development is +the Lumiera GTK GUI,_ based on GTK-2 / gtkmm. The sources are in tree +(directory 'src/gui') and it is integrated into the standard build and +installation process. By default, running the 'lumiera' executable will +load and start this GUI as a Lumiera module from 'modules/gtk_gui.lum' + Processing Layer @@ -157,9 +171,65 @@ Low Level Model ~~~~~~~~~~~~~~~ _tbw_ -Play/Render processes -~~~~~~~~~~~~~~~~~~~~~ -_tbw_ + +Play/Rendering subsystem +~~~~~~~~~~~~~~~~~~~~~~~~ +Within Lumiera, »Player« is the name for a subsystem responsible for organising and tracking +_ongoing playback and render processes._ The player subsystem does not perform or even manage +any render operations, nor does it handle the outputs directly. + +Yet it addresses some central concerns: + +uniformity:: + All playback and render processes are on equal footing, handled in a similar way. + +integration:: + The player cares for the necessary integration with the other subsystems ++ +it consults the _Output Management,_ retrieves the necessary informations from the _Session_ +and coordinates the forwarding of Backend calls. + +time quantisation:: + The player translates continuous time values into discrete frame counts. ++ +To perform this _quantisation,_ the help of the session for building a TimeGrid +for each output channel is required. + + +The player service +^^^^^^^^^^^^^^^^^^ +Client code accesses the player (subsystem) through the play-facade (`lumiera::Play`). +The exposed service allows to _set up an output connection for playback or rendering,_ +resulting in a play-controller object. + +.Play::Controller +This controller frontend represents the presence of such an active output connection +and incorporates a state machine supporting the usual things you'd expect to do with +a player (Play, pause, FFwd, Rew, scrubbing, jumping, looping). This controller object +is a copyable smart-handle -- all instances act as if wired in parallel. + +.time control +The play-controller frontend makes heavy use of `time::Control`. This is a mediator +to accept and forward _mutations_ on time values and time ranges, possibly involving +frame quantisation. After attaching it to a target time value, it accepts changes, +offsets and nudging, translates these into the appropriate target modifications +and notifies any attached _change listeners_. + +.play process +Ongoing effort to calculate a stream of frames for playback or rendering. + +The play process is an conceptual entity linking together several activities in the backend +and the render engine. It maintains a registration entry for the process to keep track of +associated entities, resources allocated and calls dispatched as a consequence. Besides +each play process is wired to at leas one play-controller acting as frontend interface +and information hub for the client code. + +NOTE: the player is in no way engaged in any of the actual calculation and management tasks +necessary to make the stream of calculations actually happen. The play process code contained +within the player subsystem is largely comprised of organisational concerns and not especially +performance critical. + +- the backend is responsible for dispatching the calculation stream and scheduling calculation jobs +- the render engine has the ability to carry out individual frame calculations +- the OutputSlot exposed by the output manager is responsible for accepting timed frame delivery @@ -182,9 +252,9 @@ reopened on demand. Hardlinked files are recognized and opened only once. All file access is done by memory mapping to reduce data copies between userland and kernel. Moreover the kernel becomes responsible to schedule paging (which will be augmented by lumiera) to make the best use of available -resources. Memory is mapped in biggier possibly overlapping windows of -resonable sized chunks. Requests asking for a contingous set of data from the -file in memory. +resources. Memory is mapped in larger windows of reasonable sized chunks, possibly +overlapping each other. Clients may request a specific continuous set of data from +the file to be accessible as memory block. .Indexing @@ -199,13 +269,41 @@ Manages serveral classes of threads in pools. The threadpool is reasonable dumb. Higher level management will be done by the Schedulers and Jobs. +Engine Interface +~~~~~~~~~~~~~~~~ +While on itself just a thin interface and adaptation layer forwarding calls to +the primary backend facilities, the Engine Interface is the primary point of service +accessed by Proc-Layer to use the backend services for rendering content. + +.Calculation Streams +The Engine Interface is cast in terms of an _calculation stream_ entity. This is +a stream of expected and ongoing frame calculations for multiple channels, to be +managed as a compound. The calculated frames will be delivered into an output slot +right away. No assumptions are made regarding the ordering of these individual +calculations -- they may be performed in parallel, constrained by input and +resource prerequisites solely. + +.Frame Dispatcher +For the actual processing, calculation streams need to be translated into individual +calculation jobs to be scheduled. For each uniform _segment of the effective timeline,_ +the typical recursive descent call characteristic for _pull processing_ results in a +Job Ticket. + +.Job Ticket +This structural descriptor of the actual calculations to be performed is the base +for creating individual jobs: Already specialised for a distinct segment of the +effective timeline and tailored for the needs of a given calculation stream, +the job ticket acts as blueprint for the actual jobs to be enqueued +with the _Scheduler._ + + Schedulers ~~~~~~~~~~ Scheduling Queues for different purposes: .Deadline -Higher priority jobs ordered by a deadline time plus some (negative) hystersis. Jobs are +Higher priority jobs ordered by a deadline time plus some (negative) hysteresis. Jobs are started when they approach their deadline. Jobs who miss their deadline are never scheduled here. @@ -291,6 +389,27 @@ Plugin loader ~~~~~~~~~~~~~ _tbw_ +Advice framework +~~~~~~~~~~~~~~~~ +This is a ``whiteboard'' system, allowing implementation-level services to _publish_ +some piece of information as _advice_, while other parts of the system may pick up +this advice just by a name token, without requiring a direct knowledge of the +original _advisor._ The _Advice System_ is a singleton service maintaining a +lookup and registration data structure. Individual _piece of advice_ elements +are stored _as value copy_. Publishing new advice requires locking, but accessing +advice is lock-free (actually there needs to be a memory barrier ``somewhere'', +otherwise the advice requesting client might not see new advice) + +.Advice topics +Advice is organised into categories, based on the type of the advice item and +some additional symbolic identifiers. Actually these are syntactically represented +similar to the _atoms_ of a rules based system (``Prolog syntax''). Currently (2010) +only ground terms (completely static symbols) are supported. But the intention is to +extend the system to allow for variables in these terms. This will turn the matching +of advice provisions and requests into an unification, allowing the advice item to +be parametrised. + + Rules system ~~~~~~~~~~~~ _tbw_ @@ -308,17 +427,40 @@ Lua Scripting _tbw_ + Library ------- The Lumiera support library contains lots of helper functionality factored out from the internals and re-used. It is extended as we go. +practical shortcuts +~~~~~~~~~~~~~~~~~~~ +The header 'lib/util.hpp' defines some shortcuts heavily used throughout +the code base. The idea is to highlight a common semantic meaning, while +hide differentiation on the technical details. + +isnil:: indicate a _missing value_, irrespective if this is a NULL pointer, + an empty string or an empty container. Several of our custom wrappers also + support this notion. + +contains:: indicate that a value is _somehow contained_ within a collection, + irrespective if this is a set, a map, a vector or string (substring test) + +sanitise:: make any string usable as identifier. + +In a similar vein, the header 'lib/util-foreach.hpp' provides a generic +``for-each-element'' mechanism, which works for all STL containers, but +also for all _Lumiera Forward Iterators_. The loop body is provided +as a functor. In case this functor is a predicate (boolean result), +the +and_all+ and +has_any+ functions allow to test for conjunction +and disjunction. + Locking ~~~~~~~ -Based on object monitors. Performance critical code -uses mutexes, condition vars and rwlocks direcly. +General purpose Locking is based on object monitors. Performance critical code +in the backend uses mutexes, condition vars and rwlocks direcly. Intentionally no semaphores. - C++ locks are managed by scoped automatic variables @@ -328,15 +470,80 @@ Intentionally no semaphores. Time ~~~~ -Time values are represented by an opaque date type `lumiera::Time` +Time values are represented by a family of opaque date types with overloaded operators. The implementation is based on `gavl_time_t`, -an integral (µsec) time tick value. Any Time handling and conversions -is centralised in library routines. +an integral (µsec) time tick value. Thus, the arithmetic on time values +and time spans is limited and any Time handling and conversion is +centralised in library routines. We distinguish between time values and a _quantisation_ into a frame or sample grid. In any case, quantisation has to be done once, explicitly and as late as possible. See the link:{rfc}/TimeHandling.html[Time handling RfC]. +.time values +The Lumiera library defines several flavours of time values. All of +these internal time values have in common that they are _opaque_ and not +directly related to any human readable or external (wall clock) time. +Moreover, most of these time values are immutable, yet there are two +mechanisms to allow for changing time values (TimeVar and Mutation). + +.quantised time +Special flavours of these time values additionally carry the reference +to an (frame) alignment grid, while being time value entities in all other +respects. But _only those quantised time values_ expose functions to +convert the internal opaque time into a human readable or externally +relevant time format -- including SMPTE or frame counts. + +.time (alignment) grid +Thus, any usage of time values is forced to refer to such a time alignment +grid explicitly, at least when leaving the realm of the internal opaque +time values. This is the point where the *time quantisation* is actually +performed, imposing some information loss (as any rounding operation does). + +A time alignment grid is exactly that: a set of functions to perform +this lossy conversion. Implicitly this involves the definition of an +_time origin_ (reference point where the external time is zero), and +typically this also includes the definition of a _frame rate_ (but +in the most general case, this frame rate might be variable and +change at various places of the time axis). Consequently, all time +grids are Assets and defined as part of the concrete session. + + +Time Code +^^^^^^^^^ +Historically, Time Code was seen as the foundation of any film editing. +Similarly, the first generation of editing systems used Time Code as a +foundation. Today, we consider such a design as questionable. + +Lumiera takes a different approach here: Time code is reduced to a mere +mode of presentation, i.e. a formatting of existing time values. It is +void of any substantial meaning. To the contrary, the operation of +_frame quantisation_ (see above) is considered to be fundamental, +causing a irreversible loss of information. The design of time handling +chosen within Lumiera forces you to decide on using a specific _time grid_, +prior to being able to format an internal (opaque) time value in any kind +of time code. And only as late as when _actually retrieving_ this time code +formatted value, the actual quantisation (grid alignment) happens. + +In practice, establishing a time grid requires knowledge about the output +format. Thus, an (sufficiently resolved) *output designation* is required +to perform any grid alignment a and time code formatting. Typically this +happens when a timeline or similar part of the High-Level-Model is connected +to a concrete output or an global bus defining a frame rate already. The model +contents as such are _frame rate independent_. + +The following time code formats are supported, both for programmatic access +and display within the GUI + +- frame count +- SMPTE +- SMPTE drop-frame _[,yellow]#TODO# as of 2011_ +- hours:mins:secs _[,yellow]#TODO# as of 2011_ +- fractional seconds _[,yellow]#TODO# as of 2011_ +- musical bars:beats _[,yellow]#TODO# as of 2011_ + +As a corollary, as any rendering is based on frame numbers, it requires an +output connection or something similar to establish a frame grid. + Errors ~~~~~~ @@ -425,7 +632,33 @@ mayfail() Singletons ~~~~~~~~~~ -_tbw_ +Deliberately, Lumiera stays below the level of utilising a dependency injection framework. +Consequently, we access most services _by type name_, pulling up a single implementation +instance on demand. Rather than placing this singleton lifecycle logic into the individual +implementation classes, we use a _singleton factory_, managing the lifecycle through static +variables and placing the singleton object into a static memory block. Singleton initialisation +is protected by a monitor per singleton type, while shutdown is triggered by the clean-up of +a static variable. This results in the general policy that within Lumiera, performing any +``business'' code in the application shutdown phase (after exiting +main()+) is _strictly +prohibited._ Generally speaking, destructors _must not perform any significant work_ and +are are expected to be failsafe. + +.accessing the singleton instance +By convention, when clients are expected actively to access the singleton instance, +the interface class holds the singleton factory as a public static member with the +name +instance+. This allows clients to write `SomeService::instance()` to get a +reference to the implementation. + +.subclasses and mock testing support +There is a mechanism to ``push aside'' the existing singleton instance and shadow +it temporarily with a mock implementation. Besides, there is a variation of the +general singleton factory, allowing to fabricate a specific subclass of the +exposed interface. Both of these facilities are rather technically involved +and require some specific set-up -- fortunately it turned out that they are +used only occasionally and rarely required. (Probably this is a result of +Lumiera being developed _test-driven_ -- things are being written mostly +in a unit test friendly fashion). + Extensible Factory ~~~~~~~~~~~~~~~~~~ @@ -437,27 +670,162 @@ _tbw_ Iterators ~~~~~~~~~ +Iterators serve to decouple a collection of elements from the actual data type +implementation used to manage those elements. The use of iterators is a +design pattern. +-> see link:{ldoc}/technical/library/iterator.html[detailed library documentation] + Lumiera Forward Iterator ^^^^^^^^^^^^^^^^^^^^^^^^ -_tbw_ +Within Lumiera, we don't treat _Iterator_ as a base class -- we treat it as a _concept_ +for generic programming, similar to the usage in the STL. But we use our own definition +of the iterator concept, placing the primary focus on interfaces and decoupling. +Our ``Lumiera Forward Iterator'' concept deliberately removes most of the features +known from the STL. Rather, such an iterator is just the promise for pulling values +_once_. The iterator can be disposed when _exhausted_ -- there is no way of resetting, +moving backwards or doing any kind of arithmetic with such an object. The _exhausted +state can be detected by a +bool+ conversion (contrast this with STL iterators, where +you need to compare to an +end+ iterator). Beyond that, the usage is quite similar, +even compatible to +std::for_each+. Iterator Adapters ^^^^^^^^^^^^^^^^^ -_tbw_ +We provide a collection of pre defined adapter templates to ease building +Lumiera Forward Iterators. + +- a generic solution using a _iteration control_ callback API +- the `lib::RangeIter` just wraps up a pair of iterators for ``current position'' + and ``and'' -- compatible with the STL +- there is a variant for automatically dereferencing pointers +- plus a set of adapters for STL containers, allowing to expose each value, each + key, distinct values and so on. + +Iterator Adapters are designed for ease of use, they don't conceal the underlying +implementation (and the actual type is often quite convoluted). + +Iteration Sources +^^^^^^^^^^^^^^^^^ +To the contrary, the `lib::IterSource` template is an abstract base class. +This allows to expose the promise to deliver values through any kind of API, without +disclosing the actual implementation. Obviously, this requires the use of virtual +functions for the actual iteration. + +Again, there are pre-defined adaptors for STL containers, but the actual container +is concealed in this case. Itertools ^^^^^^^^^ -_tbw_ +Iterators can be used to build pipelines. This technique from functional programming +allows to abstract away the actual iteration completely, focussing only on the way +individual elements are processed. To support this programming style, several support +templates are provided to build _filtering iterators, transforming iterators,_ to pick +only _unique values,_ to _take a snapshot on-the-fly_ etc. There are convenience +builder functions for those operations, figuring out the actual source and destination +types by template metaprogramming. + + +Front-end for boost::format +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Formatting values with `printf` is a notorious source for intricate errors. +Additionally, using (s|n)`printf` can be clunky in practice, and it doesn't +support custom defined string conversions, which are an important diagnostic +aid when working with objects. We might +link:{ldoc}/technical/howto/UsingBoost.html[use Boost], which provides the +`boost::format` library to address those problems. Unfortunately including this +header-only library solution incurs a significant overhead, both in terms of +compilation time and code size. While maybe still acceptable at the implementation +level, using boost::format is thus certainly a ``no go'' for any code residing +in headers frequently included. + +To work around these problems, we provide a front-end wrapper, defined in +'lib/format-string.hpp'. This allows to keep the actual boost::format based +implementation confined to a single translation unit, while still being able +to use all primitive types as usual with boost::format or printf. Additionally, +our frontend automatically invokes a custom or built-in string conversion, if +applicable, it dereferences pointers and catches all errors encountered while +in formatting. So it's well suited for usage in error handling code. + +[source,C] +------------------------------------------------------------ +#include "lib/format-string.hpp" +using util::_Fmt; + +double total = 22.9499; +const char * currency = "€"; + +cout << _Fmt("price %+5.2f %s") % total % currency << endl; +------------------------------------------------------------ + +WARNING: `boost::format` is known to be about 10 times slower than `printf` -- + best to avoid it for performance critical code. Wrappers and Opaque Holders ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- smart handle -- unified value/ptr/reference holder -- ownership managing collection -- opaque holder to ``piggypack'' an object inline, - without the need for heap allocated storage -- vector of references +.smart handle +This pervasively used handle type is based on the reference counting +smart-pointer type of C\++ (`boost::shared_ptr` and C++11). Typically +this also includes a pointer to some kind of implementation service. + +Yet still, handles based on `lib::Handle` should not be confused with +smart pointers. Rather, we use the ref-counting mechanism to invoke a custom +clean-up callback when the last handle goes out of scope. Typically, the +implementation service is kept entirely opaque, while the copyable handle +objects also implement a front-end interface for client access. + +.unified holder for value/ptr/reference +_tbw_ + +.ownership managing collection +When implementing services, frequently we encountered the situation that +a manager object creates and owns some component elements or sub-services. +The library provides two special collections, which also take ownership +of their contents and care for automatic clean-up. Especially, contrary +to the STL containers, those custom containers support use of +_non-copyable object_ (as a rule, all objects with reference semantics +are defined non-copyable in Lumiera). + +- the `ScopedPtrVect` is a vector taking ownership of heap allocated objects +- the `ScopedCollection` is a fixed-size collection, holding all the child + objects within a single (heap allocated) storage block + + +.opaque holder +There is a family of holder objects, all based on placement-new of the +contained object into an embedded buffer. The purpose is to ``piggyback'' +an object inline, without the need for heap allocated storage. Frequently +the motivation for this usage pattern is *type erasure*: the detailed knowledge +context used to build some kind of object is discarded prior to further use, +relying on generic information and the hidden parametrisation henceforth. + +.polymorphic values +The C++ language has direct support for _value semantics_ and allows to build +value objects to be treated as first class citizens. Unfortunately this doesn't +fit well with the chosen approach to object orientation, where polymorphism relies +on reference semantics. Thus, most of the fundamental design patterns drive us into +having an object manager somewhere hidden within the implementation level, to +manage the memory for maintaining the subclass instances to be concealed +at the usage site. + +To avoid this dilemma, we utilise the technique of the opaque holder to provide +objects with value semantics, while actually placing the instance of a subclass +into the inline buffer. Clients access this embedded object by automatic type +conversion to the interface type, which gives us polymorphism. While the +definition of such a beast is quite involved, the runtime overhead is +surprisingly low. When compared with standard polymorphism, creating +objects and invoking operations has zero overhead, while copying +and assignment incur the cost of an additional virtual call, +assuming the target objects cooperate by mixing in a +copy management interface. + +.vector of references +a minimal interface for an array-like entity, but exposing only references +to the contained elements. Obviously this means to use a virtual call for +the subscript operation. This interface allows interfaces to expose something +_array-like_ without committing to a specific implementation type for the +exposed elements within the ``array''. The Lumiera library provides a set +of standard implementations for this +lib::RefArray+ interface, including +a vector based and a directly array based variant. + Unique Identifiers ~~~~~~~~~~~~~~~~~~ @@ -477,16 +845,42 @@ at runtime (type erasure) Typed Lookup ^^^^^^^^^^^^ -_planned_ a system of per-type lookup tables, based on `EntryID`, together +_[,yellow]#planned#_ a system of per-type lookup tables, based on `EntryID`, together with an type specific access functor. Together, this allows to translate transparently and typesafe from symbolic ID to object instance, which is an prerequisite for integrating a rules based system. Besides, these -tables allow unique IDs per type +tables allow unique IDs per type + +-> more details about this concept link:{l}/wiki/renderengine.html#EntryID%20TypedID%20TypedLookup[in the TiddlyWiki] Allocators ~~~~~~~~~~ -_tbw_ +Lumiera utilises several custom allocators, each tailored to a specific purpose. +All these allocator-_frontends_ share a common pooling allocation backend + +WARNING: currently (as of 2011) the low-level pooled allocator within the + backend isn't implemented; instead we do just heap allocations. + See Ticket #231 + +.Allocation Cluster +This allocation scheme is used within the context of the Builder and the +Low-Level-Model. The predominant usage trend in this realm is to create +and wire a family of small objects right after each other, within a build +process. These objects are intended to work together and will be discarded +all at once, after hot-swapping a new version of that model segment. + +.Typed Allocation Manager +This allocation framework is used at various places when a large number of +similar objects is expected to be coming and going. New objects are +placement-constructed into the allocated space and immediately wrapped +with a ref-counting smart-ptr to manage ownership. + +.Simple Allocator +Based on the allocator interface of the STL, allowing just for plain +allocations and de-allocations without any further instance and lifecycle +management. Currently (as of 2011) this allocator isn't used much -- it is +conceivable that later we'll detect some specific STL based containers to be +performance critical with respect to allocation. Memory Pools @@ -525,6 +919,11 @@ Functor Utils ^^^^^^^^^^^^^ _tbw_ +Duck Typing +^^^^^^^^^^^ +_tbw_ + + Preprocessor Metaprogramming ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -546,7 +945,7 @@ fail. NULL strings are propagated to "" empty strings. Polymorphic Programming in C ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Just a macro for simplyfying vtable function calls +Just a macro for simplifying vtable function calls VCALL(Handle, function, arguments...) translates to Handle->vtable->function (Handle, arguments...) diff --git a/doc/user/intro/intro.txt b/doc/user/intro/intro.txt index 0e32a454b..dcbf377ab 100644 --- a/doc/user/intro/intro.txt +++ b/doc/user/intro/intro.txt @@ -1,6 +1,6 @@ Lumiera (as seen) from Outer Space ================================== -:Author: Christian Thäter ct@pipapo.org +:Author: Lumiera_Core_Developers :Date: Summer 2010 @@ -18,13 +18,10 @@ they get to work with. It is aimed for workflow designers any anyone who wants to know how the program works in general. ****************************************************************************** - -About this Document -------------------- - // all things starting with '//' are asciidoc comments and drafts/notes while // working on this document +.About this Document This document is meant to be read electronically, it contains a lot hyper-links between explanations denoted by an arrow ->. Lumiera is still in development, we describe here planned features without explicitly tagging them; @@ -38,8 +35,13 @@ Vision // objective and goals of the project -Lumiera claims to be `professional', this is quite a vague term and needs -some explanation what it means to us: +Lumiera claims to be a _professional non-linear video editor_. To start with, we should +point out that ``professional'' does not necessarily mean ``commercial'' or ``industrial''. +It's more of an attitude or mindset -- doing work seriously, and to be subject to any +kind of wider goal, demand, or purpose. When it comes to editing film, this might be +artistry, a narration or meaning to convey, a political message or something to show +to your audience. Anyhow, for the tools, the editing software used to this end, +we can identify several properties and requirements, to be labeled ``professional'': Reliability:: Whatever happens, your work must be safe, protected against software @@ -47,34 +49,34 @@ some explanation what it means to us: never crash, in practice even crashes or power outages should not result in lost work. - Productivity:: + Quality:: + If you work with high quality, cinema grade digital video material you + want to be sure that you can deliver crisp quality without compromise, + throughout the whole workflow to your final product. All rendering + must be reproducible to the bit. + + Performance and Productivity:: Professionals want to get things done, in time, but optionally with control over every aspect. Balancing these goals should be the central concern for workflow design and usability. - Quality:: - If you work with high quality, cinema grade digital video material you - want to be sure that you can deliver this crisp quality without - compromise throughout you workflow to your final product. All rendering - must be reproducible to the bit. - - Scalability:: + Scalability and Adaptability:: Projects and budgets differ, hardware advances, Lumiera must scale in different dimensions and use the available resources as best as it - can. From small Laptops to multicore Computers and Renderfarms. + can. From small Laptops to multi core computers and Renderfarms. - Future Proofness:: + Durability:: Soft and Hardware advances at a fast pace. We must not lock into the current state of technology but being flexible to extend the System without breaking compatibility. Projects you create nowadays with Lumiera should be usable in foreseeable future, at least there needs to be a guaranteed upgrade path. -< + Fundamental Forces ------------------ -// the basic ideas which drive the lumiera design +// the basic ideas which drive the Lumiera design The Lumiera design is guided by a small number of basic principles. Keeping these in mind will help to understand how actually more interesting things can diff --git a/doc/user/tutorials/DebianBuilding.txt b/doc/user/tutorials/DebianBuilding.txt index 5e33957b6..8c249b667 100644 --- a/doc/user/tutorials/DebianBuilding.txt +++ b/doc/user/tutorials/DebianBuilding.txt @@ -35,7 +35,7 @@ should _not_ be done as root. . add a suitable source line to your *Apt configuration* ('/etc/apt/sources.lst') + ---- -deb-src http://lumiera.org/debian/ lenny experimental +deb-src http://lumiera.org/debian/ squeeze experimental ---- . get all the *build dependencies* + diff --git a/icons/Makefile.am b/icons/Makefile.am deleted file mode 100644 index 0f34d924b..000000000 --- a/icons/Makefile.am +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (C) Lumiera.org -# 2008, Joel Holdsworth -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -svgdir = $(top_srcdir)/icons/svg -prerendereddir = $(top_srcdir)/icons/prerendered -icondir = $(top_builddir) -iconcommand = python $(top_srcdir)/admin/render_icon.py - -16x16 = $(icondir)/16x16 -22x22 = $(icondir)/22x22 -24x24 = $(icondir)/24x24 -32x32 = $(icondir)/32x32 -48x48 = $(icondir)/48x48 - -16x16pre = $(prerendereddir)/16x16 -22x22pre = $(prerendereddir)/22x22 -24x24pre = $(prerendereddir)/24x24 -32x32pre = $(prerendereddir)/32x32 -48x48pre = $(prerendereddir)/48x48 - -icons = \ - $(16x16)/app-icon.png $(22x22)/app-icon.png $(24x24)/app-icon.png $(32x32)/app-icon.png $(48x48)/app-icon.png \ - $(16x16)/tool-arrow.png $(22x22)/tool-arrow.png $(24x24)/tool-arrow.png $(32x32)/tool-arrow.png $(48x48)/tool-arrow.png \ - $(16x16)/tool-i-beam.png $(22x22)/tool-i-beam.png $(24x24)/tool-i-beam.png $(32x32)/tool-i-beam.png $(48x48)/tool-i-beam.png \ - $(16x16)/track-disabled.png \ - $(16x16)/track-enabled.png \ - $(16x16)/track-locked.png \ - $(16x16)/track-unlocked.png \ - $(16x16)/panel-assets.png $(22x22)/panel-assets.png $(32x32)/panel-assets.png \ - $(16x16)/panel-timeline.png \ - $(16x16)/panel-viewer.png $(22x22)/panel-viewer.png $(32x32)/panel-viewer.png - -dist_pkgdata_DATA += $(icons) - -all: $(icons) - -clean-local: - rm -rf $(16x16) $(22x22) $(24x24) $(32x32) $(48x48) - -# ========== SVG Icons ========== - -# App Icon - -$(16x16)/app-icon.png $(22x22)/app-icon.png $(24x24)/app-icon.png $(32x32)/app-icon.png $(48x48)/app-icon.png : $(svgdir)/app-icon.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) - -# Timeline Tools - -$(16x16)/tool-arrow.png $(22x22)/tool-arrow.png $(24x24)/tool-arrow.png $(32x32)/tool-arrow.png $(48x48)/tool-arrow.png : $(svgdir)/tool-arrow.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) -$(16x16)/tool-i-beam.png $(22x22)/tool-i-beam.png $(24x24)/tool-i-beam.png $(32x32)/tool-i-beam.png $(48x48)/tool-i-beam.png : $(svgdir)/tool-i-beam.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) - -# Timeline Tracks -$(16x16)/track-disabled.png : $(svgdir)/track-disabled.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) -$(16x16)/track-enabled.png : $(svgdir)/track-enabled.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) -$(16x16)/track-locked.png : $(svgdir)/track-locked.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) -$(16x16)/track-unlocked.png : $(svgdir)/track-unlocked.svg $(top_builddir)/rsvg-convert - $(iconcommand) $< $(icondir) - -# ========== Prerendered Icons ========== - -# Panels - -$(16x16)/panel-assets.png: $(16x16pre)/panel-assets.png - cp $(16x16pre)/panel-assets.png $(16x16) -$(22x22)/panel-assets.png: $(22x22pre)/panel-assets.png - cp $(22x22pre)/panel-assets.png $(22x22) -$(32x32)/panel-assets.png: $(32x32pre)/panel-assets.png - cp $(32x32pre)/panel-assets.png $(32x32) - -$(16x16)/panel-timeline.png: $(16x16pre)/panel-timeline.png - cp $(16x16pre)/panel-timeline.png $(16x16) - -$(16x16)/panel-viewer.png: $(16x16pre)/panel-viewer.png - cp $(16x16pre)/panel-viewer.png $(16x16) -$(22x22)/panel-viewer.png: $(22x22pre)/panel-viewer.png - cp $(22x22pre)/panel-viewer.png $(22x22) -$(32x32)/panel-viewer.png: $(32x32pre)/panel-viewer.png - cp $(32x32pre)/panel-viewer.png $(32x32) diff --git a/research/DIR_INFO b/research/DIR_INFO new file mode 100644 index 000000000..59b2583f4 --- /dev/null +++ b/research/DIR_INFO @@ -0,0 +1 @@ +Experiments and Investigations. Not installed diff --git a/research/SConscript b/research/SConscript new file mode 100644 index 000000000..cde733bf9 --- /dev/null +++ b/research/SConscript @@ -0,0 +1,22 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for experiments and investigations. +## Things defined here usuall won't be installed +## + +Import('env core support_lib') + + + +envR = env.Clone() +# envR.Append(CCFLAGS=' -O3 ') + +# build additional test and administrative tools.... +experiments = [ envR.Program('try', ['try.cpp'] + support_lib) #### to try out some feature... + ] + +# +# define Phony targets +# - 'scons research' triggers building of experimental code +# +env.Alias('research', experiments ) diff --git a/research/try.cpp b/research/try.cpp new file mode 100644 index 000000000..d03b9d2da --- /dev/null +++ b/research/try.cpp @@ -0,0 +1,165 @@ +/* try.cpp - for trying out some language features.... + * scons will create the binary bin/try + * + */ + +// 8/07 - how to control NOBUG?? +// execute with NOBUG_LOG='ttt:TRACE' bin/try +// 1/08 - working out a static initialisation problem for Visitor (Tag creation) +// 1/08 - check 64bit longs +// 4/08 - comparison operators on shared_ptr +// 4/08 - conversions on the value_type used for boost::any +// 5/08 - how to guard a downcasting access, so it is compiled in only if the involved types are convertible +// 7/08 - combining partial specialisation and subclasses +// 10/8 - abusing the STL containers to hold noncopyable values +// 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)? +// 4/10 - pretty printing STL containers with python enabled GDB? +// 1/11 - exploring numeric limits +// 1/11 - integer floor and wrap operation(s) +// 1/11 - how to fetch the path of the own executable -- at least under Linux? +// 10/11 - simple demo using a pointer and a struct +// 11/11 - using the boost random number generator(s) +// 12/11 - how to detect if string conversion is possible? +// 1/12 - is partial application of member functions possible? + + +/** @file try.cpp + ** Research: perform a partial application of a member function. + ** The result of this partial application should be a functor expecting the remaining arguments. + ** The idea was to use this at various library functions expecting a functor or callback, so to + ** improve readability of the client code: clients could then just pass a member pointer, without + ** the need to use any tr1::bind expression. + ** + ** \par Costs in code size + ** While this turned out to be possible, even without much work, just based on the existing + ** templates for partial functor application (function-closure.hpp), the resulting code size + ** is rather sobering. Especially in debug mode, quite some overhead is created, which makes + ** usage of this convenience feature in general purpose library code rather questionable. + ** When compiling with -O3 though, most of the overhead will be removed + ** + ** The following numbers could be observed: + ** \code + ** debug / stripped // debug-O3 / stripped + ** just using a member pointer: 39013 / 7048 42061 / 7056 + ** using tr1::bind and function: 90375 / 15416 65415 / 9376 + ** partial apply, passing functor: 158727 / 23576 97479 / 11296 + ** partial apply with mem pointer: 119495 / 17816 78031 / 9440 + ** \endcode + */ + + +#include "lib/meta/tuple.hpp" +#include "lib/meta/function-closure.hpp" + +//#include +#include + +using lib::meta::Types; +using lib::meta::Tuple; +//using std::tr1::placeholders::_1; +//using std::tr1::placeholders::_2; +using std::tr1::function; +using std::tr1::bind; + +using std::string; +using std::cout; +using std::endl; + + +namespace lib { +namespace meta{ +namespace func{ + + +template +struct _PupS + { + typedef typename _Fun::Ret Ret; + typedef typename _Fun::Args::List Args; + typedef typename Splice::Front ArgsFront; + typedef typename Splice::Back ArgsBack; + typedef typename Types::Seq ArgsToClose; + typedef typename Types::Seq ArgsRemaining; + typedef typename _Sig::Type ReducedSignature; + + typedef function Function; + }; + +template +inline +typename _PupS::Function +papply (SIG f, A1 a1) +{ + typedef typename _PupS::ArgsToClose ArgsToClose; + typedef Tuple ArgTuple; + ArgTuple val(a1); + return PApply::bindFront (f, val); +} + +template +inline +typename _PupS::Function +papply (SIG f, A1 a1, A2 a2) +{ + typedef typename _PupS::ArgPrefix ArgsToClose; + typedef Tuple ArgTuple; + ArgTuple val(a1,a2); + return PApply::bindFront (f, val); +} + + +}}} // namespace lib::meta::func + +class Something + { + int i_; + + void + privateFun(char a) + { + char aa(a + i_); + cout << "Char-->" << aa < FunP; + + FunP + getBinding() + { +// function memf = bind (&Something::privateFun, _1, _2); +// return lib::meta::func::papply (memf, this); + return lib::meta::func::papply (&Something::privateFun, this); + } + +// typedef void (Something::*FunP) (char); +// +// FunP +// getBinding() +// { +// return &Something::privateFun; +// } + }; + + + + + +int +main (int, char**) + { + Something some(23); + Something::FunP fup = some.getBinding(); + + fup ('a'); + + cout << "\n.gulp.\n"; + + return 0; + } diff --git a/src/SConscript b/src/SConscript new file mode 100644 index 000000000..de4d03a91 --- /dev/null +++ b/src/SConscript @@ -0,0 +1,48 @@ +# -*- python -*- +## +## SConscript - SCons buildscript for the Lumiera Application. +## Definitions how to build the main tree +## + +from Buildhelper import srcSubtree +from Buildhelper import scanSubtree + +Import('env icons config') + +# define the source file/dirs comprising each artifact to be built. + +lLib = env.SharedLibrary('lumiera', srcSubtree('lib'), install=True) +lApp = env.SharedLibrary('lumieracommon', srcSubtree('common'), install=True, LIBS=lLib) +lBack = env.SharedLibrary('lumierabackend', srcSubtree('backend'),install=True) +lProc = env.SharedLibrary('lumieraproc', srcSubtree('proc'), install=True) + +core = lLib+lApp+lBack+lProc +core_lib = core +support_lib = lLib + +lumiera = ( env.Program('lumiera', ['lumiera/main.cpp'] + core, install=True) + + config + ) + +# Install the lumiera application: +# symlink the executable into the bin dir +env.SymLink('#$DESTDIR/bin/lumiera',env.path.installExe+'lumiera','../lib/lumiera/lumiera') + + +# building Lumiera Plugins +plugins = [] # currently none + + +# the Lumiera GTK GUI +envGtk = env.Clone() +envGtk.mergeConf(['gtkmm-2.4','gthread-2.0','cairomm-1.0','gdl','xv','xext','sm']) +envGtk.Append(LIBS=core) + +guimodule = envGtk.LumieraPlugin('gtk_gui', srcSubtree('gui'), install=True) +gui = ( guimodule + + icons + + [env.GuiResource(f) for f in env.Glob('gui/*.rc')] + ) + + +Export('lumiera core core_lib support_lib plugins gui') diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 7540f1445..854d44f26 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -31,7 +31,7 @@ liblumierabackend_la_SOURCES = \ $(liblumierabackend_la_srcdir)/filehandle.c \ $(liblumierabackend_la_srcdir)/filehandlecache.c \ $(liblumierabackend_la_srcdir)/fileheader.c \ - $(liblumierabackend_la_srcdir)/mediaaccessfacade.cpp \ + $(liblumierabackend_la_srcdir)/media-access-facade.cpp \ $(liblumierabackend_la_srcdir)/mmap.c \ $(liblumierabackend_la_srcdir)/mmapcache.c \ $(liblumierabackend_la_srcdir)/mmapings.c \ diff --git a/src/backend/backend.c b/src/backend/backend.c index 0f2c62b8a..694050c9e 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -102,7 +102,8 @@ lumiera_backend_init (void) lumiera_backend_pagesize = sysconf(_SC_PAGESIZE); - TODO ("add config options to override following defaults"); + /////////////////////////////////////////////////////////////////////TICKET #838 add config options to override following defaults" + const char* filehandles = lumiera_tmpbuf_snprintf (SIZE_MAX, "backend.file.max_handles = %d", @@ -168,7 +169,7 @@ lumiera_backend_mpool_purge (enum lumiera_resource_try itr, void* data, void* co (void) context; (void) data; (void) itr; - TODO("mpool_purge ((MPool) data)"); + ///////////////////////////////////////////////////////////TICKET #837 actually implement mpool purging return LUMIERA_RESOURCE_NONE; } diff --git a/src/proc/engine/buffhandle.cpp b/src/backend/engine/scheduler-frontend.cpp similarity index 75% rename from src/proc/engine/buffhandle.cpp rename to src/backend/engine/scheduler-frontend.cpp index 24573990e..3cf1698ab 100644 --- a/src/proc/engine/buffhandle.cpp +++ b/src/backend/engine/scheduler-frontend.cpp @@ -1,8 +1,8 @@ /* - BuffHandle - Buffer handling support for the render engine + SchedulerFrontend - access point to the scheduler within the renderengine Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 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 @@ -21,12 +21,14 @@ * *****************************************************/ -#include "proc/engine/buffhandle.hpp" +#include "backend/engine/scheduler-frontend.hpp" +namespace backend{ namespace engine { - - - /** */ - - -} // namespace engine + + + /** */ + + + +}} // namespace backend::engine diff --git a/src/backend/engine/scheduler-frontend.hpp b/src/backend/engine/scheduler-frontend.hpp new file mode 100644 index 000000000..887435191 --- /dev/null +++ b/src/backend/engine/scheduler-frontend.hpp @@ -0,0 +1,55 @@ +/* + SCHEDULER-FRONTEND.hpp - access point to the scheduler within the renderengine + + 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 BACKEND_ENGINE_SCHEDULER_FRONTEND_H +#define BACKEND_ENGINE_SCHEDULER_FRONTEND_H + + + +//using std::list; + + +namespace backend{ +namespace engine { + + + /** + * @todo this is planned to become the frontend + * to the render node network, which can be considered + * at the lower end of the middle layer; the actual + * render operations are mostly implemented by the backend + * ////////TODO WIP as of 12/2010 + */ + class SchedulerFrontend + { + public: + ///// TODO: find out about the public operations + // note: the play controller lives in the proc-layer, + // but is a subsystem separate of the sesison. + + private: + + }; + +}} // namespace backend::engine +#endif diff --git a/src/backend/media-access-facade.hpp b/src/backend/media-access-facade.hpp index 3e51d96fa..9604360af 100644 --- a/src/backend/media-access-facade.hpp +++ b/src/backend/media-access-facade.hpp @@ -83,8 +83,8 @@ namespace backend { /** - * Descriptor holding the global informations, - * needed for further handling this media within Lumiera. + * Descriptor holding the global information record + * required for further handling this kind of media within Lumiera. */ struct MediaDesc { diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index 60413b037..6ce1aaa02 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -188,7 +188,7 @@ namespace backend { /** @note by design there is no possibility to find out - * just based on the thread handle, if the thread is alive. + * just based on the thread handle if some thread is alive. * We define our own accounting here based on the internals * of the thread wrapper. This will break down, if you mix * uses of the C++ wrapper with the raw C functions. */ @@ -200,7 +200,7 @@ namespace backend { /** Synchronisation barrier. In the function executing in this thread - * needs to be a corresponding Thread::sync() call. Blocking until + * needs to be a corresponding Thread::syncPoint() call. Blocking until * both the caller and the thread have reached the barrier. */ void diff --git a/src/backend/threadpool.c b/src/backend/threadpool.c index b0ac7c0b3..ed035c209 100644 --- a/src/backend/threadpool.c +++ b/src/backend/threadpool.c @@ -76,8 +76,9 @@ lumiera_threadpool_destroy(void) { LUMIERA_CONDITION_SECTION (cond_sync, &threadpool.pool[i].sync) { - TODO ("check threads deadlines, kill them when they are stalled"); - TODO ("for threads without deadline use a timeout from config system, 500ms or so by default"); + //////////////////////////////////////////TICKET #843 check threads deadlines, kill them when they are stalled" + //////////////////////////////////////////TICKET #843 for threads without deadline use a timeout from config system, 500ms or so by default + LUMIERA_CONDITION_WAIT(llist_is_empty (&threadpool.pool[i].working_list)); } } @@ -136,7 +137,9 @@ lumiera_threadpool_acquire_thread (enum lumiera_thread_class kind, llist_insert_head (&threadpool.pool[kind].working_list, &ret->node); ENSURE (ret, "did not create a valid thread"); - TODO ("no error handing, let the resourcecollector do it, no need when returning the thread"); + //////////////////////////////////////////////////////////////////////TICKET #844 no error must be pending here + //////////////////////////////////////////////////////////////////////TICKET #844 let the resourcecollector do it, no need when returning the thread + LUMIERA_CONDITION_WAIT (!llist_is_empty (&threadpool.pool[kind].idle_list)); } // use an existing thread, pick the first one diff --git a/src/backend/threads.c b/src/backend/threads.c index 1261fe134..7f580f712 100644 --- a/src/backend/threads.c +++ b/src/backend/threads.c @@ -37,7 +37,7 @@ #include /** - * @file + * @file threads.c * */ @@ -93,17 +93,17 @@ thread_loop (void* thread) do { lumiera_threadpool_release_thread(t); LUMIERA_CONDITION_WAIT (t->state != LUMIERA_THREADSTATE_IDLE); - INFO (threads, "Thread awaken with state %s", lumiera_threadstate_names[t->state]); + TRACE (threads, "Thread awaken with state %s", lumiera_threadstate_names[t->state]); // NULL function means: no work to do - INFO (threads, "function %p", t->function); + TRACE (threads, "function %p", t->function); if (t->function) t->function (t->arguments); TRACE (threads, "function done"); if (t->kind & LUMIERA_THREAD_JOINABLE) { - INFO (threads, "Thread zombified"); + TRACE (threads, "Thread zombified"); /* move error state to data the other thread will it pick up from there */ t->arguments = (void*)lumiera_error (); t->state = LUMIERA_THREADSTATE_ZOMBIE; @@ -111,16 +111,16 @@ thread_loop (void* thread) LUMIERA_CONDITION_SIGNAL; LUMIERA_CONDITION_WAIT (t->state == LUMIERA_THREADSTATE_JOINED); - INFO (threads, "Thread joined"); + TRACE (threads, "Thread joined"); } } while (t->state != LUMIERA_THREADSTATE_SHUTDOWN); // SHUTDOWN state - INFO (threads, "Thread Shutdown"); + TRACE (threads, "Thread done."); } - TODO ("no error must be pending here, else do app shutdown"); + //////////////////////////////////////////////////////////////////////TICKET #844 no error must be pending here, else do app shutdown return 0; } @@ -322,7 +322,7 @@ lumiera_thread_sync (void) self->state = LUMIERA_THREADSTATE_SYNCING; lumiera_condition_signal (&self->signal, &NOBUG_FLAG(threads), NOBUG_CONTEXT); - TODO("error handing, maybe timed mutex (using the threads heartbeat timeout, shortly before timeout)"); + //////////////////////////////////////////TICKET #843 error handing, maybe timed mutex (using the threads heartbeat timeout, shortly before timeout) while (self->state == LUMIERA_THREADSTATE_SYNCING) { lumiera_condition_wait (&self->signal, &NOBUG_FLAG(threads), self->rh, NOBUG_CONTEXT); diff --git a/src/backend/threads.h b/src/backend/threads.h index 6e755be48..d539a3944 100644 --- a/src/backend/threads.h +++ b/src/backend/threads.h @@ -37,7 +37,7 @@ /** - * @file + * @file threads.h * */ diff --git a/src/common/config_lookup.c b/src/common/config-lookup.c similarity index 93% rename from src/common/config_lookup.c rename to src/common/config-lookup.c index 0c0a776a4..3daf24f10 100644 --- a/src/common/config_lookup.c +++ b/src/common/config-lookup.c @@ -23,7 +23,7 @@ #include "lib/safeclib.h" #include "lib/tmpbuf.h" -#include "common/config_lookup.h" +#include "common/config-lookup.h" #include "common/config.h" /* we only use one fatal error for now, when allocation in the config system fail, something else is pretty wrong */ @@ -76,7 +76,8 @@ lumiera_config_lookup_insert (LumieraConfigLookup self, LumieraConfigitem item) REQUIRE (item->key); REQUIRE (item->key_size); - FIXME ("implement section prefix/suffix for the key"); + ////////////////////////////////////////TICKET #839 implement section prefix/suffix for the key" + const char* key = lumiera_tmpbuf_strcat3 (NULL, 0, item->key, item->key_size, NULL, 0); LumieraConfigLookupentry entry = (LumieraConfigLookupentry)psplay_find (&self->tree, key, 100); @@ -101,7 +102,9 @@ lumiera_config_lookup_insert_default (LumieraConfigLookup self, LumieraConfigite LumieraConfigLookupentry entry = (LumieraConfigLookupentry)psplay_find (&self->tree, key, 100); if (!entry) entry = (LumieraConfigLookupentry)psplay_insert (&self->tree, &lumiera_config_lookupentry_new (key)->node, 100); - TODO ("else check that no 'default' item already exists, that is, the tail element's parent points to the 'defaults' in config"); + + ////////////////////////////////////////TICKET #839 check that no 'default' item already exists when inserting a default + ////////////////////////////////////////TICKET #839 ...that is, the tail element's parent points to the 'defaults' in config llist_insert_tail (&entry->configitems, &item->lookup); return entry; diff --git a/src/common/config_lookup.h b/src/common/config-lookup.h similarity index 98% rename from src/common/config_lookup.h rename to src/common/config-lookup.h index eadf96b39..29f5d6d18 100644 --- a/src/common/config_lookup.h +++ b/src/common/config-lookup.h @@ -1,5 +1,5 @@ /* - config_lookup.h - Lookup functions for the config subsystem + CONFIG-LOOKUP.h - Lookup functions for the config subsystem Copyright (C) Lumiera.org 2008, Christian Thaeter diff --git a/src/common/config.h b/src/common/config.h index be8b47171..48ac51a4b 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -51,7 +51,7 @@ LUMIERA_ERROR_DECLARE (CONFIG_NO_ENTRY); //TODO: Lumiera header includes// -#include "common/config_lookup.h" +#include "common/config-lookup.h" #include "common/configitem.h" //TODO: System includes// diff --git a/src/common/configentry.c b/src/common/configentry.c index ef02f5149..5ee5f35ae 100644 --- a/src/common/configentry.c +++ b/src/common/configentry.c @@ -43,7 +43,7 @@ lumiera_configentry_new (LumieraConfigitem tmp) LumieraConfigentry self = lumiera_malloc (sizeof (*self)); lumiera_configitem_move ((LumieraConfigitem)self, tmp); - TODO ("initialize other stuff here (lookup, parent, ...)"); + //////////////////////////////////////////////////////////////////TICKET #839 initialise other stuff here (lookup, parent, ...) return (LumieraConfigitem)self; } @@ -52,7 +52,7 @@ lumiera_configentry_new (LumieraConfigitem tmp) LumieraConfigitem lumiera_configentry_destroy (LumieraConfigitem self) { - TODO ("cleanup other stuff here (lookup, parent, ...)"); + //////////////////////////////////////////////////////////////////TICKET #839 cleanup other stuff here (lookup, parent, ...) return self; } diff --git a/src/common/configitem.c b/src/common/configitem.c index 1a07ee820..de290e16a 100644 --- a/src/common/configitem.c +++ b/src/common/configitem.c @@ -20,6 +20,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/** @file configitem.c + ** create a configitem out of a single line. + ** + */ + + #include "include/logging.h" #include "lib/llist.h" #include "lib/safeclib.h" @@ -43,12 +50,6 @@ static LumieraConfigitem parse_configentry (LumieraConfigitem self, char* itr); #include -/** - * @file - * create a configitem out of a single line. - * - */ - LumieraConfigitem lumiera_configitem_init (LumieraConfigitem self) @@ -176,11 +177,11 @@ lumiera_configitem_parse (LumieraConfigitem self, const char* line) lumiera_free (self->line); self->line = lumiera_strndup (line, SIZE_MAX); - FIXME ("MOCKUP START"); + /////////////////////////TODO do a real startup here char* itr = self->line; - /* skip leading whitespaces */ + /* skip leading whitespace */ while (*itr && isspace (*itr)) itr++; @@ -215,7 +216,7 @@ parse_directive (LumieraConfigitem self, char* itr) /* itr points now to @ */ self->key = itr; - /* check whether there are illegal whitespaces after @ */ + /* check whether there are illegal whitespace after @ */ itr++; if (*itr && !isspace(*itr)) { @@ -224,7 +225,7 @@ parse_directive (LumieraConfigitem self, char* itr) itr += self->key_size; - /* we need a key with a length greather than zero and */ + /* we need a key with a length greater than zero and */ /* either end of line or whitespace after key */ if ( self->key_size && ( !*itr || (*itr && isspace(*itr)) )) diff --git a/src/common/configitem.h b/src/common/configitem.h index 49ae8b62d..92d12a6c2 100644 --- a/src/common/configitem.h +++ b/src/common/configitem.h @@ -33,7 +33,7 @@ typedef lumiera_configitem* LumieraConfigitem; struct lumiera_configitem_vtable; //TODO: Lumiera header includes// -#include "common/config_lookup.h" +#include "common/config-lookup.h" //TODO: System includes// diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp index 841858883..28b535d02 100644 --- a/src/common/configrules.hpp +++ b/src/common/configrules.hpp @@ -68,7 +68,7 @@ namespace lumiera { ///////TODO: shouldn't that be namespace lib? or proc? using std::string; - using lumiera::P; + using lib::P; @@ -185,7 +185,7 @@ namespace lumiera { ///////TODO: shouldn't that be namespace lib? or proc? */ template class ConfigRules - : public typelist::InstantiateForEach + : public lib::meta::InstantiateForEach { protected: ConfigRules () {} @@ -212,13 +212,13 @@ namespace lumiera { ///////TODO: shouldn't that be namespace lib? or proc? * the list of all concrete types participating in the * rule based config query system */ - typedef lumiera::typelist::Types < mobject::session::Track - , asset::Pipe - , const asset::ProcPatt - , asset::Timeline - , asset::Sequence - > ::List - InterfaceTypes; + typedef lib::meta::Types < proc::mobject::session::Track + , proc::asset::Pipe + , const proc::asset::ProcPatt + , proc::asset::Timeline + , proc::asset::Sequence + > ::List + InterfaceTypes; /** * user-visible Interface to the ConfigRules subsystem. diff --git a/src/common/guifacade.cpp b/src/common/guifacade.cpp index aff7febb8..5e1c4599d 100644 --- a/src/common/guifacade.cpp +++ b/src/common/guifacade.cpp @@ -156,7 +156,7 @@ namespace gui { if (facade) { WARN (guifacade, "GUI subsystem terminates, but GuiFacade isn't properly closed. " - "Closing it forcedly; this indicates broken startup logic and should be fixed."); + "Closing it forcedly; this indicates broken startup logic and should be fixed."); try { facade.reset (0); } catch(...) { WARN_IF (lumiera_error_peek(), guifacade, "Ignoring error: %s", lumiera_error()); } lumiera_error(); // clear any remaining error state... diff --git a/src/common/instancehandle.hpp b/src/common/instancehandle.hpp index d6c98e546..9f8004c65 100644 --- a/src/common/instancehandle.hpp +++ b/src/common/instancehandle.hpp @@ -184,7 +184,7 @@ namespace lumiera { public: /** Set up an InstanceHandle representing a plugin. - * Should be placed at the client side. + * Should be placed at the client side. * @param iName unmangled name of the interface * @param version major version * @param minminor minimum acceptable minor version number @@ -203,7 +203,7 @@ namespace lumiera { * registration and deregistration of interface(s). * Should be placed at the service providing side. * @param a (single) interface descriptor, which can be created with - * LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF + * LUMIERA_INTERFACE_INSTANCE and referred to by LUMIERA_INTERFACE_REF */ InstanceHandle (LumieraInterface descriptor) : desc_(descriptor) @@ -222,9 +222,9 @@ namespace lumiera { - /** act as smart pointer providing access through the facade. + /** act as smart pointer providing access through the facade. * @note we don't provide operator* */ - FA * operator-> () const { return &(facadeLink_(*this)); } + FA * operator-> () const { return &(facadeLink_(*this)); } /** directly access the instance via the CL interface */ I& get () const { ENSURE(instance_); return *instance_; } diff --git a/src/common/interface-facade-link.hpp b/src/common/interface-facade-link.hpp new file mode 100644 index 000000000..27c0826c9 --- /dev/null +++ b/src/common/interface-facade-link.hpp @@ -0,0 +1,123 @@ +/* + INTERFACE-FACADE-LINK - a switchable link from interface to service implementation + + 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 interface-facade-link.hpp + ** Opening, accessing and closing the service access through a facade interface. + ** Client code is assumed to access an application level service through an facade + ** interface, while the actual implementation object remains an opaque internal detail. + ** Moreover, services may come up and shut down, so the implementation might change + ** during the Lifecycle. The facility defined here in this header provides a basic + ** implementation for this access mechanism, but without any adaptation, binding + ** or plugin access layer. It works only under the assumption that both the + ** interface and the actual service implementation coexist in the same + ** executable and are written in C++, so any invocation of an + ** interface method boils down to a language-level call. + ** + ** Usually, client code doesn't need to include this header. Clients are assumed + ** to use the facade interface of the service in question. This facade interface + ** contains a static member of type \c lumiera::facade::Accessor (where I is + ** the type of the facade interface). The Accessor baseclass is defined in + ** interfaceproxy.hpp and typically included through the facade header. + ** + ** @note there is a way more elaborate implementation of the same mechanism + ** for use with the Lumiera Interface/Plugin system. + ** + ** @see interfaceproxy.hpp description of the more general use case + ** @see PlayService example for the simple use case + */ + + +#ifndef LUMIERA_FACADE_INTERFACE_FACADE_LINK_H +#define LUMIERA_FACADE_INTERFACE_FACADE_LINK_H + + +#include "lib/error.hpp" +#include "lib/test/test-helper.hpp" +#include "include/interfaceproxy.hpp" +#include "lib/symbol.hpp" + +#include + + + +namespace lumiera { +namespace facade { + + using lib::Literal; + + + /************************************************************************ + * simple access-frontend to the implementation of a service (C++ only). + * Usually, an instance of Accessor is placed as static member right into + * the facade interface used to access the service. This implementation + * of the access mechanism handles the simple case that both the facade + * and the service implementation are written in C++ and calls happen + * within the main executable as direct language calls, without an + * binding layer and without involving the Interface/Plugin system. + * + * Typically, the InterfaceFacadeLink becomes a member of the service + * implementation class and is directly tied into the constructor of + * the latter. Being a subclass of lumiera::facade::Accessor, it is + * allowed to "open" the facade access just by setting the static + * protected pointer Accessor::implProxy_ + */ + template + class InterfaceFacadeLink + : protected Accessor + , boost::noncopyable + { + Literal displayName_; + + void + __checkLifecycle () + { + if (Accessor::implProxy_) + throw error::State("Attempt to open an already opened Facade interface." + , error::LUMIERA_ERROR_LIFECYCLE); + } + + public: + InterfaceFacadeLink(FA& serviceImpl, Literal interfaceName_for_Log=0) + : displayName_(lib::test::showType(interfaceName_for_Log)) + { + __checkLifecycle(); + Accessor::implProxy_ = &serviceImpl; + INFO (interface, "interface %s opened", displayName_.c()); + } + + ~InterfaceFacadeLink() + { + INFO (interface, "closing interface %s...", displayName_.c()); + Accessor::implProxy_ = 0; + } + }; + + + + /** storage for the static access pointer */ + template + FA* Accessor::implProxy_; + + +}} // namespace lumiera::facade + +#endif diff --git a/src/common/interfaceproxy.cpp b/src/common/interfaceproxy.cpp index e403e40a5..814f991b4 100644 --- a/src/common/interfaceproxy.cpp +++ b/src/common/interfaceproxy.cpp @@ -28,73 +28,87 @@ using util::cStr; -namespace lumiera { - namespace facade { - - - LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade interface currently not accessible"); - - - template - class Holder; - - template - class Holder > - : Accessor, - protected FA - { - protected: - typedef InstanceHandle IHandle; - typedef Holder THolder; - typedef Proxy TProxy; - typedef Accessor Access; - - I& _i_; - - Holder (IHandle const& iha) - : _i_(iha.get()) - { } - - public: - static TProxy& open(IHandle const& iha) - { - static char buff[sizeof(TProxy)]; - TProxy* p = new(buff) TProxy(iha); - Access::implProxy_ = p; - return *p; - } - - static void close() - { - if (!Access::implProxy_) return; - TProxy* p = static_cast (Access::implProxy_); - Access::implProxy_ = 0; - p->~TProxy(); - } - }; - - - template - FA* Accessor::implProxy_; - - - template - void - openProxy (IHA const& iha) - { - Proxy::open(iha); - } - - template - void - closeProxy () - { - Proxy::close(); - } - - } // namespace facade +namespace lumiera{ +namespace facade { -} // namespace lumiera + + LUMIERA_ERROR_DEFINE (FACADE_LIFECYCLE, "facade is closed; service currently not accessible"); + + + /** + * Implementation Base + * for building Facade Proxy implementations. + * Typically the purpose of such a proxy is to route + * any calls through the C-Bindings of the Lumiera Interface system. + * The actual storage for the concrete proxy object is embedded, + * inline within the #open() function. For access by the clients, + * a frontend-object of type \c Accessor may be placed into + * the facade interface; this accessor-frontend is basically + * a concealed static pointer to the proxy, and will be set, + * when the interface is opened. This opening and closing + * of the interface itself is controlled by the + * InstanceHandle, which in turn is typically + * created and managed within the context + * of the service implementation. + */ + template + class Holder; + + template + class Holder > + : Accessor + , protected FA + { + protected: + typedef InstanceHandle IHandle; + typedef Holder THolder; + typedef Proxy TProxy; + typedef Accessor Access; + + I& _i_; + + Holder (IHandle const& iha) + : _i_(iha.get()) + { } + + public: + static TProxy& open(IHandle const& iha) + { + static char buff[sizeof(TProxy)]; + TProxy* p = new(buff) TProxy(iha); + Access::implProxy_ = p; + return *p; + } + + static void close() + { + if (!Access::implProxy_) return; + TProxy* p = static_cast (Access::implProxy_); + Access::implProxy_ = 0; + p->~TProxy(); + } + }; + + + template + FA* Accessor::implProxy_; + + + template + void + openProxy (IHA const& iha) + { + Proxy::open(iha); + } + + template + void + closeProxy () + { + Proxy::close(); + } + +}} // namespace lumiera::facade diff --git a/src/common/plugin.c b/src/common/plugin.c index 373e405e7..a44592dc8 100644 --- a/src/common/plugin.c +++ b/src/common/plugin.c @@ -248,7 +248,7 @@ lumiera_plugin_discover (LumieraPlugin (*callback_load)(const char* plugin), LumieraPlugin lumiera_plugin_load (const char* plugin) { - TRACE (pluginloader_dbg); + TRACE (pluginloader_dbg, "plugin=%s", plugin); /* dispatch on ext, call the registered function */ const char* ext = strrchr (plugin, '.'); @@ -417,6 +417,7 @@ lumiera_plugin_delete_fn (PSplaynode node) LUMIERA_INTERFACE_CAST(lumieraorg__plugin, 0) self->plugin; lumiera_interfaceregistry_bulkremove_interfaces (handle->plugin_interfaces ()); } + TRACE (pluginloader_dbg, "unloading plugin/module %s", self->name); itr->lumiera_plugin_unload_fn (self); break; } diff --git a/src/common/plugin_dynlib.c b/src/common/plugin_dynlib.c index 7e8618628..5e9aa30a4 100644 --- a/src/common/plugin_dynlib.c +++ b/src/common/plugin_dynlib.c @@ -34,7 +34,7 @@ LumieraPlugin lumiera_plugin_load_DYNLIB (const char* name) { - TRACE (pluginloader_dbg); + TRACE (pluginloader_dbg, "load DYNLIB: %s", name); REQUIRE (name); LumieraPlugin self = lumiera_plugin_new (name); LumieraInterface plugin = NULL; diff --git a/src/common/query/fake-configrules.hpp b/src/common/query/fake-configrules.hpp index bbc9f91ca..80ec54f74 100644 --- a/src/common/query/fake-configrules.hpp +++ b/src/common/query/fake-configrules.hpp @@ -54,10 +54,13 @@ namespace lumiera { namespace query { + namespace asset = proc::asset; + using asset::Pipe; using asset::ProcPatt; using asset::PProcPatt; - using mobject::Session; + using proc::mobject::Session; + using lib::meta::InstantiateChained; using util::isnil; @@ -246,10 +249,10 @@ namespace lumiera { * values for some types of interest for testing and debugging. */ class MockConfigRules - : public typelist::InstantiateChained < InterfaceTypes - , LookupPreconfigured // building block used for each of the types - , MockTable // for implementing the base class (interface) - > + : public InstantiateChained < InterfaceTypes + , LookupPreconfigured // building block used for each of the types + , MockTable // for implementing the base class (interface) + > { protected: MockConfigRules (); ///< to be used only by the singleton factory diff --git a/src/common/subsys.hpp b/src/common/subsys.hpp index 363b17e51..26b080861 100644 --- a/src/common/subsys.hpp +++ b/src/common/subsys.hpp @@ -104,9 +104,12 @@ namespace lumiera { /** initiate termination of this subsystem. - * may be called repeatedly any time... - * @warning must not block nor throw. */ - virtual void triggerShutdown () throw() =0; + * This trigger may be called repeatedly any time... + * When the subsystem actually has terminated, + * the SigTerm passed to #start must be invoked. + * @note called within a locked context (barrier) + * @warning must not block nor throw. */ + virtual void triggerShutdown () throw() =0; const std::vector @@ -114,7 +117,7 @@ namespace lumiera { private: - /** weather this subsystem is actually operational. + /** whether this subsystem is actually operational. * When returning \c false here, the application may * terminate at any point without further notice * Note further, that a subsystem must not be in diff --git a/src/common/subsystem-runner.hpp b/src/common/subsystem-runner.hpp index 89a90245d..6cbd3495a 100644 --- a/src/common/subsystem-runner.hpp +++ b/src/common/subsystem-runner.hpp @@ -173,7 +173,7 @@ namespace lumiera { if (!and_all (susy->getPrerequisites(), isRunning() )) { susy->triggerShutdown(); - throw error::Logic("Unable to start all prerequisites of Subsystem "+string(*susy)); + throw error::State("Unable to start all prerequisites of Subsystem "+string(*susy)); } } void diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index 095baf82c..679c6be06 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -112,9 +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/draw-strategy.cpp \ + $(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/display-service.hpp b/src/gui/display-service.hpp index 1dec2c192..1c5cda79f 100644 --- a/src/gui/display-service.hpp +++ b/src/gui/display-service.hpp @@ -50,6 +50,7 @@ #include "common/instancehandle.hpp" #include "lib/singleton-ref.hpp" #include "lib/scoped-ptrvect.hpp" +#include "include/logging.h" #include #include diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index 65ee00e9c..315ee4f2c 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -55,11 +55,15 @@ #include "gui/window-manager.hpp" #include +#include #include namespace gui { +using std::tr1::shared_ptr; + + /* ====== The Application Class ====== */ diff --git a/src/gui/model/clip-track.cpp b/src/gui/model/clip-track.cpp index 95ddd8ac0..827aec0b9 100644 --- a/src/gui/model/clip-track.cpp +++ b/src/gui/model/clip-track.cpp @@ -24,7 +24,7 @@ #include "clip.hpp" -#include +using namespace std::tr1; namespace gui { namespace model { @@ -33,7 +33,7 @@ namespace model { { // TEST CODE: add a clip to the track - boost::shared_ptr modelClip(new model::Clip()); + shared_ptr modelClip(new model::Clip()); modelClip->setName("Clip Name"); clips.push_back(modelClip); @@ -50,7 +50,7 @@ namespace model { return os.str(); } - lumiera::observable_list< boost::shared_ptr >& + lumiera::observable_list< shared_ptr >& ClipTrack::getClipList() { return clips; diff --git a/src/gui/model/clip-track.hpp b/src/gui/model/clip-track.hpp index 288b41bf4..45a067f60 100644 --- a/src/gui/model/clip-track.hpp +++ b/src/gui/model/clip-track.hpp @@ -28,7 +28,7 @@ #ifndef CLIP_TRACK_HPP #define CLIP_TRACK_HPP -#include "track.hpp" +#include "gui/model/track.hpp" #include "lib/observable-list.hpp" namespace gui { @@ -53,12 +53,12 @@ namespace model { /** * Gets the list of clips associated with this track. */ - lumiera::observable_list< boost::shared_ptr >& + lumiera::observable_list >& getClipList(void); private: - lumiera::observable_list< boost::shared_ptr > clips; + lumiera::observable_list > clips; }; diff --git a/src/gui/model/clip.cpp b/src/gui/model/clip.cpp index 2b7dc6cd6..f78e2c2af 100644 --- a/src/gui/model/clip.cpp +++ b/src/gui/model/clip.cpp @@ -32,7 +32,7 @@ namespace gui { namespace model { Clip::Clip() - : timeCoord_(Time(FSecs(1)), FSecs(2)) + : timeCoord_(Time(FSecs(1)), FSecs(3)) { } @@ -40,14 +40,14 @@ namespace model { Clip::setBegin (Time newStartTime) { timeCoord_.accept (Mutation::changeTime (newStartTime)); - // TODO: emit signal + TODO("Emit A Signal"); } void Clip::setDuration (Duration newLength) { timeCoord_.accept (Mutation::changeDuration(newLength)); - // TODO: emit signal + TODO("Emit A Signal"); } void diff --git a/src/gui/model/parent-track.cpp b/src/gui/model/parent-track.cpp index 874c7e9d5..45c8d13ae 100644 --- a/src/gui/model/parent-track.cpp +++ b/src/gui/model/parent-track.cpp @@ -23,7 +23,7 @@ #include "parent-track.hpp" #include -using namespace boost; +using std::tr1::shared_ptr; namespace gui { namespace model { @@ -32,13 +32,13 @@ ParentTrack::ParentTrack() { } -const std::list< boost::shared_ptr >& +const std::list >& ParentTrack::get_child_tracks() const { return tracks.get_list(); } -lumiera::observable_list< boost::shared_ptr >& +lumiera::observable_list >& ParentTrack::get_child_track_list() { return tracks; @@ -52,11 +52,11 @@ ParentTrack::can_host_children() const bool ParentTrack::remove_descendant_track( - const boost::shared_ptr track) + const shared_ptr track) { REQUIRE(track); - boost::shared_ptr parent = + shared_ptr parent = find_descendant_track_parent(track); if(parent) { @@ -67,23 +67,24 @@ ParentTrack::remove_descendant_track( return false; } -boost::shared_ptr -ParentTrack::find_descendant_track_parent( - boost::shared_ptr child) +shared_ptr +ParentTrack::find_descendant_track_parent(shared_ptr child) { + using namespace boost; + REQUIRE(child != NULL); - BOOST_FOREACH(shared_ptr track, tracks) + BOOST_FOREACH(std::tr1::shared_ptr track, tracks) { if(track == child) return shared_from_this(); - - shared_ptr result = + + std::tr1::shared_ptr result = track->find_descendant_track_parent(child); if(result) return result; } - return shared_ptr(); + return std::tr1::shared_ptr(); } } // namespace model diff --git a/src/gui/model/parent-track.hpp b/src/gui/model/parent-track.hpp index 78451972e..a1c709713 100644 --- a/src/gui/model/parent-track.hpp +++ b/src/gui/model/parent-track.hpp @@ -28,13 +28,9 @@ #ifndef PARENT_TRACK_HPP #define PARENT_TRACK_HPP -#include "track.hpp" +#include "gui/model/track.hpp" #include "lib/observable-list.hpp" -#include -#include - - namespace gui { namespace model { @@ -44,7 +40,7 @@ namespace model { */ class ParentTrack : public Track, - public boost::enable_shared_from_this + public std::tr1::enable_shared_from_this { protected: /** @@ -57,13 +53,13 @@ public: /** * Gets a read-only reference to the the list of child tracks. */ - const std::list< boost::shared_ptr >& + const std::list >& get_child_tracks() const; /** * Gets read-write access to the list of child tracks. */ - lumiera::observable_list< boost::shared_ptr >& + lumiera::observable_list >& get_child_track_list(); /** @@ -79,7 +75,7 @@ public: * @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); + bool remove_descendant_track (const shared_ptr track); /** * A utility function that attempts to find the parent of a track by @@ -88,14 +84,14 @@ public: * @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); + shared_ptr + find_descendant_track_parent (shared_ptr child); protected: /** * The internal list of child tracks of this parent. */ - lumiera::observable_list< boost::shared_ptr > tracks; + lumiera::observable_list > tracks; }; } // namespace model diff --git a/src/gui/model/project.cpp b/src/gui/model/project.cpp index 3b37020cd..7f8054b53 100644 --- a/src/gui/model/project.cpp +++ b/src/gui/model/project.cpp @@ -26,7 +26,7 @@ #include "sequence.hpp" using namespace std; -using namespace boost; +using namespace std::tr1; namespace gui { namespace model { @@ -43,7 +43,7 @@ Project::~Project() } -lumiera::observable_list< boost::shared_ptr >& +lumiera::observable_list< shared_ptr >& Project::get_sequences() { return sequences; diff --git a/src/gui/model/project.hpp b/src/gui/model/project.hpp index e6e6bbc9e..f2806abca 100644 --- a/src/gui/model/project.hpp +++ b/src/gui/model/project.hpp @@ -27,7 +27,7 @@ #ifndef PROJECT_HPP #define PROJECT_HPP -#include "sequence.hpp" +#include "gui/model/sequence.hpp" #include "lib/observable-list.hpp" namespace gui { @@ -40,14 +40,14 @@ public: ~Project(); - lumiera::observable_list< boost::shared_ptr >& + lumiera::observable_list >& get_sequences(); void add_new_sequence(uString name); private: - lumiera::observable_list< boost::shared_ptr > sequences; + lumiera::observable_list > sequences; }; } // namespace model diff --git a/src/gui/model/sequence.cpp b/src/gui/model/sequence.cpp index 318ee4a4f..107396515 100644 --- a/src/gui/model/sequence.cpp +++ b/src/gui/model/sequence.cpp @@ -27,7 +27,7 @@ #include "group-track.hpp" #include "clip-track.hpp" -using namespace boost; +using namespace std::tr1; namespace gui { namespace model { diff --git a/src/gui/model/sequence.hpp b/src/gui/model/sequence.hpp index 45b9a43bf..db856d29e 100644 --- a/src/gui/model/sequence.hpp +++ b/src/gui/model/sequence.hpp @@ -27,7 +27,7 @@ #ifndef SEQUENCE_HPP #define SEQUENCE_HPP -#include "parent-track.hpp" +#include "gui/model/parent-track.hpp" namespace gui { namespace model { diff --git a/src/gui/model/track.cpp b/src/gui/model/track.cpp index b8588ed49..c81cc9739 100644 --- a/src/gui/model/track.cpp +++ b/src/gui/model/track.cpp @@ -24,7 +24,7 @@ #include "parent-track.hpp" #include -using namespace boost; +using namespace std::tr1; using namespace std; namespace gui { @@ -35,11 +35,12 @@ const list< shared_ptr > Track::NoChildren; Track::Track() : enabled(true), locked(false) -{ +{ } -} +Track::~Track() { } -const std::list< boost::shared_ptr >& + +const std::list< shared_ptr >& Track::get_child_tracks() const { return Track::NoChildren; @@ -96,9 +97,9 @@ Track::print_branch() return print_branch_recursive(0); } -boost::shared_ptr +shared_ptr Track::find_descendant_track_parent( - boost::shared_ptr /*child*/) + shared_ptr /*child*/) { return shared_ptr(); } diff --git a/src/gui/model/track.hpp b/src/gui/model/track.hpp index 7e6b9085a..c7472b03f 100644 --- a/src/gui/model/track.hpp +++ b/src/gui/model/track.hpp @@ -47,6 +47,7 @@ protected: Track(); public: + virtual ~Track(); /// this is an interface /** * Returns true if this track can own any child tracks. @@ -57,7 +58,7 @@ public: /** * Gets the list of child tracks. */ - virtual const std::list< boost::shared_ptr >& + virtual const std::list >& get_child_tracks () const; /** @@ -106,8 +107,8 @@ public: * @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); + virtual shared_ptr + find_descendant_track_parent (shared_ptr child); /** * A signal which fires when the enabled status changes. @@ -154,7 +155,7 @@ protected: * An object used internally as a return value for when there's no * children. */ - static const std::list< boost::shared_ptr > NoChildren; + static const std::list< shared_ptr > NoChildren; /** * The internal implementation of print_branch. diff --git a/src/gui/notification-interface-proxy.hpp b/src/gui/notification-interface-proxy.hpp index 79500e8df..2269513e5 100644 --- a/src/gui/notification-interface-proxy.hpp +++ b/src/gui/notification-interface-proxy.hpp @@ -1,5 +1,5 @@ /* - Notification(Proxy) - public service allowing to push informations into the GUI + Notification(Proxy) - public service allowing to push information into the GUI Copyright (C) Lumiera.org 2008, Hermann Vosseler diff --git a/src/gui/notification-service.cpp b/src/gui/notification-service.cpp index 0cfd3c826..a27d5a641 100644 --- a/src/gui/notification-service.cpp +++ b/src/gui/notification-service.cpp @@ -1,5 +1,5 @@ /* - NotificationService - public service allowing to push informations into the GUI + NotificationService - public service allowing to push information into the GUI Copyright (C) Lumiera.org 2008, Hermann Vosseler diff --git a/src/gui/notification-service.hpp b/src/gui/notification-service.hpp index d7e2e218b..427ec045d 100644 --- a/src/gui/notification-service.hpp +++ b/src/gui/notification-service.hpp @@ -1,5 +1,5 @@ /* - NOTIFICATION-SERVICE.hpp - public service allowing to push informations into the GUI + NOTIFICATION-SERVICE.hpp - public service allowing to push information into the GUI Copyright (C) Lumiera.org 2008, Hermann Vosseler diff --git a/src/gui/panels/panel.hpp b/src/gui/panels/panel.hpp index 1a9801250..a6f508403 100644 --- a/src/gui/panels/panel.hpp +++ b/src/gui/panels/panel.hpp @@ -27,11 +27,12 @@ #ifndef PANEL_HPP #define PANEL_HPP -#include #include "gui/gtk-lumiera.hpp" #include "gui/widgets/panel-bar.hpp" +#include + namespace gui { namespace workspace { diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 07dce7b1d..827a2bee9 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -23,10 +23,12 @@ #include "gui/gtk-lumiera.hpp" #include "gui/panels/timeline-panel.hpp" +#include "gui/widgets/timeline/timeline-zoom-scale.hpp" #include "gui/workspace/workspace-window.hpp" #include "gui/model/project.hpp" #include "gui/controller/controller.hpp" + #include "lib/util.hpp" #include @@ -34,10 +36,11 @@ using namespace Gtk; using namespace sigc; using namespace gui::widgets; +using namespace gui::widgets::timeline; using namespace gui::model; -using boost::shared_ptr; ///////////////////////////////TICKET #796 -using boost::weak_ptr; ///////////////////////////////TICKET #796 +using std::tr1::shared_ptr; +using std::tr1::weak_ptr; using util::contains; namespace gui { @@ -60,6 +63,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, , iBeamTool(Gtk::StockID("tool_i_beam")) , zoomIn(Stock::ZOOM_IN) , zoomOut(Stock::ZOOM_OUT) + , zoomScale() , updatingToolbar(false) , currentTool(timeline::Arrow) { @@ -101,14 +105,15 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, toolbar.append(separator2); - toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in)); - toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out)); - + toolbar.append(zoomScale); + zoomScale.signal_zoom(). + connect(mem_fun(this,&TimelinePanel::on_zoom)); + toolbar.show_all(); panelBar.pack_start(toolbar, PACK_SHRINK); // Setup tooltips - sequenceChooser .set_tooltip_text(_("Change sequence")); + sequenceChooser .set_tooltip_text(_("Change sequence")); previousButton .set_tooltip_text(_("To beginning")); rewindButton .set_tooltip_text(_("Rewind")); @@ -122,13 +127,18 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, zoomIn .set_tooltip_text(_("Zoom in")); zoomOut .set_tooltip_text(_("Zoom out")); + zoomScale .set_tooltip_text(_("Adjust timeline zoom scale")); // Setup the timeline widget - shared_ptr sequence ///////////////////////////////TICKET #796 - = *get_project().get_sequences().begin(); + shared_ptr sequence = *(get_project().get_sequences().begin()); timelineWidget.reset(new TimelineWidget(load_state(sequence))); pack_start(*timelineWidget, PACK_EXPAND_WIDGET); - + + // since TimelineWidget is now initialised, + // wire the zoom slider to react on timeline state changes + zoomScale.wireTimelineState (timelineWidget->get_state(), + timelineWidget->state_changed_signal()); + // Set the initial UI state update_sequence_chooser(); update_tool_buttons(); @@ -136,7 +146,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, show_time (Time::ZERO); } - const char* TimelinePanel::get_title() { @@ -183,6 +192,13 @@ TimelinePanel::on_ibeam_tool() set_tool(timeline::IBeam); } +void +TimelinePanel::on_zoom(double time_scale_ratio) +{ + REQUIRE(timelineWidget); + timelineWidget->zoom_view(time_scale_ratio); +} + void TimelinePanel::on_zoom_in() { @@ -236,18 +252,19 @@ TimelinePanel::on_sequence_chosen() { weak_ptr sequence_ptr = (*iter)[sequenceChooserColumns.sequenceColumn]; + shared_ptr sequence(sequence_ptr.lock()); + if(sequence) { shared_ptr old_state( timelineWidget->get_state()); REQUIRE(old_state); - if(sequence != old_state->get_sequence()) timelineWidget->set_state(load_state(sequence)); } } - + update_zoom_buttons(); } @@ -316,19 +333,9 @@ TimelinePanel::update_tool_buttons() void TimelinePanel::update_zoom_buttons() { - REQUIRE(timelineWidget); - - const shared_ptr state = - timelineWidget->get_state(); - if(state) - { - timeline::TimelineViewWindow &viewWindow = - state->get_view_window(); - - zoomIn.set_sensitive(viewWindow.get_time_scale() != 1); - zoomOut.set_sensitive(viewWindow.get_time_scale() - != TimelineWidget::MaxScale); - } +/* This function is no longer needed + * TODO: Let the ZoomScaleWidget perform + * the update on its own */ } void @@ -379,9 +386,10 @@ TimelinePanel::on_frame() shared_ptr TimelinePanel::load_state(weak_ptr sequence) { + /* state exists */ if(contains(timelineStates, sequence)) - return timelineStates[sequence]; - + return timelineStates[sequence]; + shared_ptr shared_sequence = sequence.lock(); if(shared_sequence) { diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index b65469bc9..febcb857c 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -32,15 +32,19 @@ #include "gui/panels/panel.hpp" #include "gui/widgets/timecode-widget.hpp" #include "gui/widgets/timeline-widget.hpp" +#include "gui/widgets/timeline/timeline-zoom-scale.hpp" + #include "lib/time/timevalue.hpp" -#include -#include +#include using namespace gui::widgets; - namespace gui { + +using std::tr1::shared_ptr; +using std::tr1::weak_ptr; + namespace model { class Sequence; @@ -51,6 +55,7 @@ namespace panels { using lib::time::Time; + /** * The definition of the timeline panel class, which holds timeline * widgets. @@ -87,6 +92,7 @@ private: void on_arrow_tool(); void on_ibeam_tool(); + void on_zoom(double time_scale_ratio); void on_zoom_in(); void on_zoom_out(); @@ -124,8 +130,8 @@ private: void show_time (Time); - boost::shared_ptr ///////////////////////////////TICKET #796 - load_state (boost::weak_ptr sequence); + shared_ptr + load_state (weak_ptr sequence); private: @@ -145,7 +151,7 @@ private: * An invisible column which will be used to identify the sequence * of a row. */ - Gtk::TreeModelColumn< boost::weak_ptr > + Gtk::TreeModelColumn< weak_ptr > sequenceColumn; /** @@ -169,9 +175,10 @@ private: // Body Widgets boost::scoped_ptr timelineWidget; - - std::map< boost::weak_ptr, - boost::shared_ptr > ///////////////////////////////TICKET #796 + + std::map< weak_ptr + , shared_ptr + > timelineStates; // Toolbar Widgets @@ -191,6 +198,7 @@ private: MiniButton zoomIn; MiniButton zoomOut; + gui::widgets::timeline::TimelineZoomScale zoomScale; Gtk::SeparatorToolItem separator2; diff --git a/src/gui/panels/viewer-panel.cpp b/src/gui/panels/viewer-panel.cpp index 692f82308..955b92e3e 100644 --- a/src/gui/panels/viewer-panel.cpp +++ b/src/gui/panels/viewer-panel.cpp @@ -21,13 +21,13 @@ * *****************************************************/ #include "gui/gtk-lumiera.hpp" +#include "gui/panels/viewer-panel.hpp" +#include "gui/workspace/workspace-window.hpp" #include "gui/controller/controller.hpp" #include "gui/controller/playback-controller.hpp" #include "gui/display-service.hpp" -#include "gui/workspace/workspace-window.hpp" -#include "viewer-panel.hpp" using namespace Gtk; using namespace gui::widgets; diff --git a/src/gui/panels/viewer-panel.hpp b/src/gui/panels/viewer-panel.hpp index ae41516c1..7fe5cace3 100644 --- a/src/gui/panels/viewer-panel.hpp +++ b/src/gui/panels/viewer-panel.hpp @@ -26,11 +26,12 @@ #ifndef VIEWER_PANEL_HPP #define VIEWER_PANEL_HPP -#include -#include "panel.hpp" +#include "gui/panels/panel.hpp" #include "gui/widgets/video-display-widget.hpp" +#include + namespace gui { namespace panels { diff --git a/src/gui/widgets/timecode-widget.cpp b/src/gui/widgets/timecode-widget.cpp index b46907e76..4fdd9180f 100644 --- a/src/gui/widgets/timecode-widget.cpp +++ b/src/gui/widgets/timecode-widget.cpp @@ -189,8 +189,7 @@ TimeCode::set_widget_name(string name) void TimeCode::setup_events() { - // FIXME: change to set_can_focus(true) once Debian squeeze is released - clock_base.set_flags(CAN_FOCUS); + clock_base.set_can_focus(true); const Gdk::EventMask eventMask = Gdk::BUTTON_PRESS_MASK| @@ -210,15 +209,14 @@ TimeCode::setup_events() ms_seconds_ebox.add_events(eventMask); audio_frames_ebox.add_events(eventMask); - // FIXME: change to set_can_focus(true) once Debian squeeze is released - hours_ebox.set_flags(CAN_FOCUS); - minutes_ebox.set_flags(CAN_FOCUS); - seconds_ebox.set_flags(CAN_FOCUS); - frames_ebox.set_flags(CAN_FOCUS); - audio_frames_ebox.set_flags(CAN_FOCUS); - ms_hours_ebox.set_flags(CAN_FOCUS); - ms_minutes_ebox.set_flags(CAN_FOCUS); - ms_seconds_ebox.set_flags(CAN_FOCUS); + hours_ebox.set_can_focus(true); + minutes_ebox.set_can_focus(true); + seconds_ebox.set_can_focus(true); + frames_ebox.set_can_focus(true); + audio_frames_ebox.set_can_focus(true); + ms_hours_ebox.set_can_focus(true); + ms_minutes_ebox.set_can_focus(true); + ms_seconds_ebox.set_can_focus(true); hours_ebox.signal_motion_notify_event().connect(bind(mem_fun( *this, &TimeCode::field_motion_notify_event), SMPTE_Hours)); diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index a00882b7e..fac09a6f5 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -27,7 +27,8 @@ using namespace Gtk; using namespace std; -using namespace boost; +using namespace std::tr1; + using namespace util; using namespace gui::widgets::timeline; @@ -40,21 +41,19 @@ namespace widgets { const int TimelineWidget::TrackPadding = 1; const int TimelineWidget::HeaderWidth = 150; const int TimelineWidget::HeaderIndentWidth = 10; -const double TimelineWidget::ZoomIncrement = 1.25; -const int64_t TimelineWidget::MaxScale = 30000000; -TimelineWidget::TimelineWidget( - boost::shared_ptr source_state) : - Table(2, 2), - layoutHelper(*this), - headerContainer(NULL), - body(NULL), - ruler(NULL), - horizontalAdjustment(0, 0, 0), - verticalAdjustment(0, 0, 0), - horizontalScroll(horizontalAdjustment), - verticalScroll(verticalAdjustment), - update_tracks_frozen(false) + +TimelineWidget::TimelineWidget(shared_ptr source_state) + : Table(2, 2) + , layoutHelper(*this) + , headerContainer(NULL) + , body(NULL) + , ruler(NULL) + , horizontalAdjustment(0, 0, 0) + , verticalAdjustment(0, 0, 0) + , horizontalScroll(horizontalAdjustment) + , verticalScroll(verticalAdjustment) + , update_tracks_frozen(false) { body = manage(new TimelineBody(*this)); ENSURE(body != NULL); @@ -62,7 +61,7 @@ TimelineWidget::TimelineWidget( ENSURE(headerContainer != NULL); ruler = manage(new TimelineRuler(*this)); ENSURE(ruler != NULL); - + horizontalAdjustment.signal_value_changed().connect( sigc::mem_fun( this, &TimelineWidget::on_scroll) ); verticalAdjustment.signal_value_changed().connect( sigc::mem_fun( @@ -71,7 +70,7 @@ TimelineWidget::TimelineWidget( this, &TimelineWidget::on_motion_in_body_notify_event) ); update_tracks(); - + attach(*body, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND); attach(*ruler, 1, 2, 0, 1, FILL|EXPAND, SHRINK); attach(*headerContainer, 0, 1, 1, 2, SHRINK, FILL|EXPAND); @@ -93,7 +92,7 @@ TimelineWidget::~TimelineWidget() /* ===== Data Access ===== */ -boost::shared_ptr +shared_ptr TimelineWidget::get_state() { return state; @@ -102,6 +101,7 @@ TimelineWidget::get_state() void TimelineWidget::set_state(shared_ptr new_state) { + state = new_state; // Clear the track tree @@ -125,17 +125,17 @@ TimelineWidget::set_state(shared_ptr new_state) update_tracks(); // Send the state changed signal - stateChangedSignal.emit(); + stateChangedSignal.emit (state); } void -TimelineWidget::zoom_view(int zoom_size) +TimelineWidget::zoom_view(double timescale_ratio) { if(state) - { - const int view_width = body->get_allocation().get_width(); - state->get_view_window().zoom_view(view_width / 2, zoom_size); - } + { + const int view_width = body->get_allocation().get_width(); + state->get_view_window().zoom_view(view_width / 2, timescale_ratio); + } } ToolType @@ -178,7 +178,7 @@ TimelineWidget::hovering_track_changed_signal() const return hoveringTrackChangedSignal; } -sigc::signal +TimelineWidget::TimelineStateChangeSignal TimelineWidget::state_changed_signal() const { return stateChangedSignal; @@ -212,10 +212,12 @@ TimelineWidget::on_view_window_changed() if(state) { timeline::TimelineViewWindow &window = state->get_view_window(); + const int view_width = body->get_allocation().get_width(); - + horizontalAdjustment.set_page_size( window.get_time_scale() * view_width); + horizontalAdjustment.set_value(_raw(window.get_time_offset())); } } @@ -331,8 +333,8 @@ TimelineWidget::create_timeline_track_from_modelTrack( void TimelineWidget::remove_orphaned_tracks() { - std::map, - boost::shared_ptr > + std::map, + shared_ptr > orphan_track_map(trackMap); // Remove all tracks which are still present in the sequence @@ -353,9 +355,9 @@ TimelineWidget::remove_orphaned_tracks() void TimelineWidget::search_orphaned_tracks_in_branch( - boost::shared_ptr modelTrack, - std::map, - boost::shared_ptr > &orphan_track_map) + shared_ptr modelTrack, + std::map, + shared_ptr > &orphan_track_map) { REQUIRE(modelTrack); @@ -411,12 +413,13 @@ TimelineWidget::update_scroll() if(state) { + ///////////////////////////////////////////////TICKET #861 shoudln't that be performed by TimelineViewWindow, instead of manipulating values from the outside? timeline::TimelineViewWindow &window = state->get_view_window(); //----- Horizontal Scroll ------// // TEST CODE - horizontalAdjustment.set_upper(1000 * GAVL_TIME_SCALE / 200); + horizontalAdjustment.set_upper( 1000 * GAVL_TIME_SCALE / 200); horizontalAdjustment.set_lower(-1000 * GAVL_TIME_SCALE / 200); // Set the page size @@ -477,7 +480,7 @@ TimelineWidget::on_motion_in_body_notify_event(GdkEventMotion *event) return true; } -boost::shared_ptr +shared_ptr TimelineWidget::sequence() const { if(!state) diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index ca213c15b..ffff15dda 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -64,17 +64,13 @@ class TimelineWidget : public Gtk::Table { public: /** - * Constructor * @param source_state The state that will be used as the data source * for this timeline widget. */ - TimelineWidget( - boost::shared_ptr source_state); + TimelineWidget (shared_ptr source_state); - /** - * Destructor - */ - ~TimelineWidget(); + virtual ~TimelineWidget(); + /* ===== Data Access ===== */ public: @@ -84,13 +80,13 @@ public: * @return The state object that the timeline widget is currently * working with. */ - boost::shared_ptr get_state(); + 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); + void set_state(shared_ptr new_state); /** * Zooms the view in or out as by a number of steps while keeping a @@ -98,7 +94,7 @@ public: * @param zoom_size The number of steps to zoom by. The scale factor * is 1.25^(-zoom_size). */ - void zoom_view(int zoom_size); + void zoom_view(double timescale_ratio); /** * Gets the type of the tool currently active. @@ -110,19 +106,21 @@ public: */ void set_tool(timeline::ToolType tool_type); - boost::shared_ptr + shared_ptr get_hovering_track() const; public: /* ===== Signals ===== */ + typedef sigc::signal > TimelineStateChangeSignal; + typedef sigc::signal > HoveringTrackChangedSignal; + sigc::signal mouse_hover_signal() const; sigc::signal playback_period_drag_released_signal() const; - sigc::signal > - hovering_track_changed_signal() const; + HoveringTrackChangedSignal hovering_track_changed_signal() const; - sigc::signal state_changed_signal() const; + TimelineStateChangeSignal state_changed_signal() const; /* ===== Events ===== */ protected: @@ -167,8 +165,8 @@ private: * already exist in trackMap. * @param list The parent track of the branch. */ - void create_timeline_tracks_from_branch( - boost::shared_ptr modelTrack); + void + create_timeline_tracks_from_branch (shared_ptr modelTrack); /** * Creates a timeline UI track to correspond to a model track. @@ -176,9 +174,8 @@ private: * @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); + shared_ptr + create_timeline_track_from_modelTrack(shared_ptr modelTrack); /** * Removes any UI tracks which no longer have corresponding model @@ -186,10 +183,10 @@ private: */ void remove_orphaned_tracks(); - void search_orphaned_tracks_in_branch( - boost::shared_ptr modelTrack, - std::map, - boost::shared_ptr > &orphan_track_map); + void + search_orphaned_tracks_in_branch (shared_ptr modelTrack, + std::map, + shared_ptr > &orphan_track_map); /** * Looks up a timeline UI track in trackMap that corresponds to a @@ -199,8 +196,8 @@ private: * modelTrack has no corresponding timeline UI track (this is an * error condition). */ - boost::shared_ptr lookup_timeline_track( - boost::shared_ptr modelTrack) const; + shared_ptr + lookup_timeline_track (shared_ptr modelTrack) const; // ----- Layout Functions ----- // @@ -230,12 +227,12 @@ private: * Helper to get the sequence object from the state. * @return Returns a shared pointer to the sequence. */ - boost::shared_ptr sequence() const; + shared_ptr sequence() const; // ----- Other Functions ----- // - void set_hovering_track( - boost::shared_ptr hovering_track); + void + set_hovering_track (shared_ptr hovering_track); protected: @@ -243,7 +240,7 @@ protected: * The state that will be used as the data source for this timeline * widget. */ - boost::shared_ptr state; + shared_ptr state; // Model Data @@ -254,11 +251,11 @@ protected: * 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 > + std::map + ,shared_ptr > trackMap; - boost::shared_ptr hoveringTrack; + shared_ptr hoveringTrack; // Helper Classes timeline::TimelineLayoutHelper layoutHelper; @@ -275,26 +272,16 @@ protected: // Signals sigc::signal mouseHoverSignal; sigc::signal playbackPeriodDragReleasedSignal; - sigc::signal > - hoveringTrackChangedSignal; - sigc::signal stateChangedSignal; + HoveringTrackChangedSignal hoveringTrackChangedSignal; + TimelineStateChangeSignal stateChangedSignal; bool update_tracks_frozen; /* ===== Constants ===== */ -public: - /** - * The maximum scale for timeline display. - * @remarks At MaxScale, every pixel on the timeline is equivalent - * to 30000000 lumiera::Time increments. - */ - static const int64_t MaxScale; - protected: static const int TrackPadding; static const int HeaderWidth; static const int HeaderIndentWidth; - static const double ZoomIncrement; friend class timeline::TimelineBody; friend class timeline::TimelineHeaderContainer; diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.cpp b/src/gui/widgets/timeline/timeline-arrow-tool.cpp index dab5539e4..8b2e0ad37 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.cpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.cpp @@ -55,17 +55,17 @@ namespace timeline { Tool::on_button_press_event(event); // Convert the mouse click position to a Time - boost::shared_ptr state = timelineBody.getTimelineWidget().get_state(); + shared_ptr state = timelineBody.getTimelineWidget().get_state(); REQUIRE(state); TimelineViewWindow const& window = state->get_view_window(); Time tpoint = window.x_to_time(mousePoint.get_x()); // Get the clip, if any - boost::shared_ptr track = getHoveringTrack(); - boost::shared_ptr clip = track->getClipAt(tpoint); + shared_ptr track = getHoveringTrack(); + shared_ptr clip = track->getClipAt(tpoint); // Nothing to do if there is no clip - if (clip == boost::shared_ptr()) + if (!clip) return; clip->setSelected(true); @@ -77,8 +77,7 @@ namespace timeline { REQUIRE (event != NULL); Tool::on_button_release_event(event); - boost::shared_ptr track = - getHoveringTrack(); + shared_ptr track = getHoveringTrack(); } void @@ -92,10 +91,10 @@ namespace timeline { return; } - boost::shared_ptr + shared_ptr ArrowTool::getHoveringTrack () { - boost::shared_ptr track( + shared_ptr track( timelineBody.getTimelineWidget().get_hovering_track()); return track; } diff --git a/src/gui/widgets/timeline/timeline-arrow-tool.hpp b/src/gui/widgets/timeline/timeline-arrow-tool.hpp index 83abd77e7..b390a25d5 100644 --- a/src/gui/widgets/timeline/timeline-arrow-tool.hpp +++ b/src/gui/widgets/timeline/timeline-arrow-tool.hpp @@ -81,7 +81,7 @@ namespace timeline { private: - boost::shared_ptr + shared_ptr getHoveringTrack (); bool selectionRectangleActive; diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 7f78d0929..1b580c262 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -34,10 +34,10 @@ using namespace Gtk; using namespace std; -using namespace boost; using namespace lumiera; using gui::util::CairoUtil; +using std::tr1::shared_ptr; namespace gui { namespace widgets { @@ -57,11 +57,14 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget) timelineWidget.state_changed_signal().connect( sigc::mem_fun(this, &TimelineBody::on_state_changed) ); + // Set a default Tool + this->set_tool(Arrow); + // Install style properties register_styles(); - + // Reset the state - on_state_changed(); + propagateStateChange(); } TimelineBody::~TimelineBody() @@ -69,6 +72,13 @@ TimelineBody::~TimelineBody() WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here"); } +TimelineViewWindow& +TimelineBody::viewWindow() const +{ + REQUIRE(timelineState); + return timelineState->get_view_window(); +} + TimelineWidget& TimelineBody::getTimelineWidget () const { @@ -83,10 +93,10 @@ TimelineBody::get_tool() const } void -TimelineBody::set_tool(timeline::ToolType tool_type) +TimelineBody::set_tool(timeline::ToolType tool_type, bool force) { // Tidy up old tool - if(tool) + if(tool && !force) { // Do we need to change tools? if(tool->get_type() == tool_type) @@ -147,7 +157,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event) // Makes sure the widget styles have been loaded read_styles(); - if(timelineWidget.get_state()) + if (timelineState) { // Prepare to render via cairo const Allocation allocation = get_allocation(); @@ -169,9 +179,9 @@ TimelineBody::on_scroll_event (GdkEventScroll* event) { REQUIRE(event != NULL); - if(timelineWidget.get_state()) + if (timelineState) { - TimelineViewWindow &window = view_window(); + TimelineViewWindow &window = viewWindow(); const Allocation allocation = get_allocation(); if(event->state & GDK_CONTROL_MASK) @@ -255,17 +265,17 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) { REQUIRE(event != NULL); - if(timelineWidget.get_state()) + if (timelineState) { - // Handle a middle-mouse drag if one is occuring + // Handle a middle-mouse drag if one is occurring + /////////////////////////////TICKET #861 : shoudln't all of that be performed by TimelineViewWindow, instead of manipulating values from the outside? switch(dragType) { case Shift: { - TimelineViewWindow &window = view_window(); - - /////////////////////////////TICKET# 795 : don't reach in from outside and manipulate internals of the timeline view! + /////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view! ///////////////////////////// : either encapsulate this entirely here, or leave it to the timeline view! + TimelineViewWindow &window = viewWindow(); const int64_t scale = window.get_time_scale(); window.set_time_offset(beginShiftTimeOffset + TimeValue(scale * (mouseDownX - event->x))); @@ -294,15 +304,26 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) } void -TimelineBody::on_state_changed() +TimelineBody::on_state_changed (shared_ptr newState) { - if(timelineWidget.get_state()) + REQUIRE (newState); + timelineState = newState; + propagateStateChange(); +} + +void +TimelineBody::propagateStateChange() +{ + if (timelineState) { // Connect up some events - view_window().changed_signal().connect( + viewWindow().changed_signal().connect( sigc::mem_fun(this, &TimelineBody::on_update_view) ); } - + + // Need to reload the current tool... + set_tool (get_tool(), true); + // Redraw queue_draw(); } @@ -335,7 +356,7 @@ TimelineBody::draw_tracks(Cairo::RefPtr cr) const shared_ptr timeline_track = timelineWidget.lookup_timeline_track(*iterator); - optional rect = + boost::optional rect = layout_helper.get_track_header_rect(timeline_track); // Is this track visible? @@ -373,7 +394,7 @@ TimelineBody::draw_track(Cairo::RefPtr cr, // Render the track cr->save(); - TimelineViewWindow &window = view_window(); + TimelineViewWindow &window = viewWindow(); timeline_track->draw_track(cr, &window); cr->restore(); } @@ -386,12 +407,11 @@ TimelineBody::draw_selection(Cairo::RefPtr cr) // Prepare const Allocation allocation = get_allocation(); - shared_ptr state = timelineWidget.get_state(); - REQUIRE(state); + REQUIRE(timelineState); - TimelineViewWindow const& window = state->get_view_window(); - const int start_x = window.time_to_x(state->getSelectionStart()); - const int end_x = window.time_to_x(state->getSelectionEnd()); + TimelineViewWindow const& window = timelineState->get_view_window(); + const int start_x = window.time_to_x(timelineState->getSelectionStart()); + const int end_x = window.time_to_x(timelineState->getSelectionEnd()); // Draw the cover if(end_x > 0 && start_x < allocation.get_width()) @@ -427,16 +447,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr cr) { REQUIRE(cr); - // Prepare - - shared_ptr state = timelineWidget.get_state(); - if(state) + if (timelineState) { - if (!state->isPlaying()) return; + if (!timelineState->isPlaying()) return; const Allocation allocation = get_allocation(); - Time point = state->getPlaybackPoint(); - const int x = view_window().time_to_x(point); + Time point = timelineState->getPlaybackPoint(); + const int x = viewWindow().time_to_x(point); // Set source cr->set_source(playbackPointColour); @@ -455,10 +472,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr cr) void TimelineBody::begin_shift_drag() { - if(timelineWidget.get_state()) + if (timelineState) { dragType = Shift; - beginShiftTimeOffset = view_window().get_time_offset(); + beginShiftTimeOffset = viewWindow().get_time_offset(); beginShiftVerticalOffset = get_vertical_offset(); } } @@ -475,14 +492,6 @@ TimelineBody::set_vertical_offset(int offset) timelineWidget.verticalAdjustment.set_value(offset); } -TimelineViewWindow& -TimelineBody::view_window() const -{ - shared_ptr state = timelineWidget.get_state(); - REQUIRE(state); - return state->get_view_window(); -} - void TimelineBody::register_styles() const { diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 74b057738..c248ca061 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -46,6 +46,7 @@ class TimelineWidget; namespace timeline { using lib::time::TimeVar; +using lib::time::TimeSpan; class Track; @@ -68,10 +69,7 @@ public: */ TimelineBody(gui::widgets::TimelineWidget &timeline_widget); - /** - * Destructor - */ - ~TimelineBody(); + virtual ~TimelineBody(); TimelineWidget& getTimelineWidget () const; @@ -87,7 +85,7 @@ public: * @param tool_type The type of tool to set. */ void - set_tool(ToolType tool_type); + set_tool(ToolType tool_type, bool force=false); /* ===== Events ===== */ protected: @@ -125,13 +123,18 @@ protected: bool on_motion_notify_event(GdkEventMotion *event); /** - * The event handler for when the TimelineWidget's state object is - * replaced. + * The event handler for when the TimelineWidget's state is switched. */ - void on_state_changed(); + void on_state_changed (shared_ptr newState); /* ===== Internals ===== */ private: + /** + * Access the current timeline view window + * @warning must not be called unless the TimlineWidget + * has a valid state. + */ + TimelineViewWindow& viewWindow() const; /** * Draws the timeline tracks. @@ -140,8 +143,8 @@ private: void draw_tracks(Cairo::RefPtr cr); void draw_track(Cairo::RefPtr cr, - boost::shared_ptr timeline_track, - const int view_width) const; + shared_ptr timeline_track, + const int view_width) const; /** * Draws the selected timeline period. @@ -161,13 +164,9 @@ private: void set_vertical_offset(int offset); - /** - * 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; - + /** adjust to the new timeline state */ + void propagateStateChange(); + /** * Registers all the styles that this class will respond to. */ @@ -202,6 +201,8 @@ private: Cairo::RefPtr playbackPointColour; gui::widgets::TimelineWidget &timelineWidget; + shared_ptr timelineState; + friend class Tool; friend class ArrowTool; diff --git a/src/gui/widgets/timeline/timeline-clip-track.cpp b/src/gui/widgets/timeline/timeline-clip-track.cpp index 39d254a48..f659aa1f8 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.cpp +++ b/src/gui/widgets/timeline/timeline-clip-track.cpp @@ -22,13 +22,13 @@ #include -#include "timeline-clip.hpp" -#include "timeline-clip-track.hpp" -#include "timeline-view-window.hpp" +#include "gui/widgets/timeline/timeline-clip.hpp" +#include "gui/widgets/timeline/timeline-clip-track.hpp" +#include "gui/widgets/timeline/timeline-view-window.hpp" using namespace Gtk; -using boost::dynamic_pointer_cast; -using boost::shared_ptr; + +using std::tr1::shared_ptr; using util::contains; namespace gui { @@ -76,7 +76,7 @@ namespace timeline { } } - boost::shared_ptr + shared_ptr ClipTrack::getClipAt(Time position) const { std::pair, shared_ptr > @@ -88,7 +88,7 @@ namespace timeline { } // Nothing found - return boost::shared_ptr(); + return shared_ptr(); } //// private methods @@ -97,8 +97,8 @@ namespace timeline { ClipTrack::createTimelineClips() { // Share the draw strategy between all objects - // TODO: use factory/builder here - static boost::shared_ptr drawStrategy(new BasicDrawStrategy()); + TODO("Use factory/builder to create Timline Clips"); + static shared_ptr drawStrategy(new BasicDrawStrategy()); BOOST_FOREACH (shared_ptr modelClip, getModelTrack()->getClipList()) { // Is a timeline UI clip present in the map already? @@ -115,7 +115,7 @@ namespace timeline { shared_ptr ClipTrack::getModelTrack () { - return dynamic_pointer_cast(modelTrack); + return std::tr1::dynamic_pointer_cast(modelTrack); } void diff --git a/src/gui/widgets/timeline/timeline-clip-track.hpp b/src/gui/widgets/timeline/timeline-clip-track.hpp index 1265c913b..32b93b321 100644 --- a/src/gui/widgets/timeline/timeline-clip-track.hpp +++ b/src/gui/widgets/timeline/timeline-clip-track.hpp @@ -52,7 +52,7 @@ namespace timeline { * Constructor. */ ClipTrack(TimelineWidget &timelineWidget, - boost::shared_ptr track); + shared_ptr track); /** * Draw the track in the timeline. @@ -66,7 +66,7 @@ namespace timeline { * pointer. * @param the given time */ - boost::shared_ptr + shared_ptr getClipAt(Time position) const; private: @@ -80,7 +80,7 @@ namespace timeline { /** * Gets the modelTrack as a ClipTrack. */ - boost::shared_ptr + shared_ptr getModelTrack (); /** @@ -107,8 +107,8 @@ namespace timeline { * The clipMap maps model clips to timeline widget clips which are responsible for the * UI representation of a clip. */ - std::map, - boost::shared_ptr > + std::map, + shared_ptr > clipMap; }; diff --git a/src/gui/widgets/timeline/timeline-clip.cpp b/src/gui/widgets/timeline/timeline-clip.cpp index f2328bdc5..1b4e04d72 100644 --- a/src/gui/widgets/timeline/timeline-clip.cpp +++ b/src/gui/widgets/timeline/timeline-clip.cpp @@ -27,11 +27,11 @@ namespace gui { namespace widgets { namespace timeline { - using boost::shared_ptr; //////////////////////TICKET #796 + using std::tr1::shared_ptr; - Clip::Clip (boost::shared_ptr clip, - boost::shared_ptr drawStrategy) + Clip::Clip (shared_ptr clip, + shared_ptr drawStrategy) : Entity(drawStrategy) , modelClip(clip) , selected(false) diff --git a/src/gui/widgets/timeline/timeline-clip.hpp b/src/gui/widgets/timeline/timeline-clip.hpp index 2d6d83139..c7e6bd64b 100644 --- a/src/gui/widgets/timeline/timeline-clip.hpp +++ b/src/gui/widgets/timeline/timeline-clip.hpp @@ -49,8 +49,8 @@ namespace timeline { class Clip : public Entity { public: - Clip (boost::shared_ptr clip, - boost::shared_ptr drawStrategy); + Clip (shared_ptr clip, + shared_ptr drawStrategy); Time getBegin() const; @@ -62,7 +62,7 @@ namespace timeline { private: - boost::shared_ptr modelClip; ////////////////////////TICKET #796 : should use std::tr1 + shared_ptr modelClip; /** * True when this clip is selected in the GUI. diff --git a/src/gui/widgets/timeline/timeline-entity.cpp b/src/gui/widgets/timeline/timeline-entity.cpp index 13dc398aa..3f7582375 100644 --- a/src/gui/widgets/timeline/timeline-entity.cpp +++ b/src/gui/widgets/timeline/timeline-entity.cpp @@ -29,7 +29,7 @@ namespace gui { namespace widgets { namespace timeline { - Entity::Entity(boost::shared_ptr drawStrategy) + Entity::Entity (shared_ptr drawStrategy) : enabled(true), drawStrategy(drawStrategy) { } diff --git a/src/gui/widgets/timeline/timeline-entity.hpp b/src/gui/widgets/timeline/timeline-entity.hpp index a6a03b5da..f30a804a1 100644 --- a/src/gui/widgets/timeline/timeline-entity.hpp +++ b/src/gui/widgets/timeline/timeline-entity.hpp @@ -31,14 +31,16 @@ #include "lib/time/timevalue.hpp" #include +#include #include -#include //////////////////////TICKET #796 + namespace gui { namespace widgets { namespace timeline { using lib::time::Time; + using std::tr1::shared_ptr; class DrawStrategy; @@ -51,7 +53,7 @@ namespace timeline { class Entity { protected: - Entity(boost::shared_ptr drawStrategy); + Entity (shared_ptr drawStrategy); virtual ~Entity(); @@ -81,7 +83,7 @@ namespace timeline { bool enabled; - boost::shared_ptr drawStrategy; + shared_ptr drawStrategy; }; } // namespace timeline diff --git a/src/gui/widgets/timeline/timeline-group-track.cpp b/src/gui/widgets/timeline/timeline-group-track.cpp index aa43e1970..4ed072453 100644 --- a/src/gui/widgets/timeline/timeline-group-track.cpp +++ b/src/gui/widgets/timeline/timeline-group-track.cpp @@ -24,7 +24,7 @@ #include "gui/widgets/timeline-widget.hpp" using namespace Gtk; -using namespace boost; +using namespace std::tr1; using namespace sigc; namespace gui { diff --git a/src/gui/widgets/timeline/timeline-group-track.hpp b/src/gui/widgets/timeline/timeline-group-track.hpp index 3cf079b47..6787c4184 100644 --- a/src/gui/widgets/timeline/timeline-group-track.hpp +++ b/src/gui/widgets/timeline/timeline-group-track.hpp @@ -26,7 +26,7 @@ #ifndef TIMELINE_GROUP_TRACK_HPP #define TIMELINE_GROUP_TRACK_HPP -#include "timeline-track.hpp" +#include "gui/widgets/timeline/timeline-track.hpp" #include "gui/model/group-track.hpp" namespace gui { @@ -36,19 +36,16 @@ namespace timeline { class GroupTrack : public timeline::Track { public: - GroupTrack(TimelineWidget &timeline_widget, - boost::shared_ptr track); + GroupTrack (TimelineWidget &timeline_widget, + shared_ptr track); - void draw_track(Cairo::RefPtr cairo, - TimelineViewWindow* constwindow) - const; + void draw_track (Cairo::RefPtr cairo, + TimelineViewWindow* constwindow) const; protected: void on_child_list_changed(); }; -} // namespace timeline -} // namespace widgets -} // namespace gui +}}} // namespace gui::widgets::timeline #endif // TIMELINE_GROUP_TRACK_HPP diff --git a/src/gui/widgets/timeline/timeline-header-container.cpp b/src/gui/widgets/timeline/timeline-header-container.cpp index 3ecfba2d0..1d1af9537 100644 --- a/src/gui/widgets/timeline/timeline-header-container.cpp +++ b/src/gui/widgets/timeline/timeline-header-container.cpp @@ -30,7 +30,7 @@ using namespace Gtk; using namespace std; -using namespace boost; +using namespace std::tr1; using namespace util; namespace gui { @@ -342,12 +342,9 @@ TimelineHeaderContainer::on_scroll() } void -TimelineHeaderContainer::on_hovering_track_changed( - boost::shared_ptr hovering_track) +TimelineHeaderContainer::on_hovering_track_changed( shared_ptr) { - (void)hovering_track; - - + /* do nothing */ } bool @@ -392,7 +389,7 @@ TimelineHeaderContainer::layout_headers() Widget &widget = timeline_track->get_header_widget(); - optional header_rect = + boost::optional header_rect = layout_helper.get_track_header_rect(timeline_track); if(header_rect) diff --git a/src/gui/widgets/timeline/timeline-header-container.hpp b/src/gui/widgets/timeline/timeline-header-container.hpp index 094619ecc..6c32d6ce4 100644 --- a/src/gui/widgets/timeline/timeline-header-container.hpp +++ b/src/gui/widgets/timeline/timeline-header-container.hpp @@ -139,8 +139,7 @@ private: */ void on_scroll(); - void on_hovering_track_changed( - boost::shared_ptr hovering_track); + void on_hovering_track_changed(shared_ptr hovering_track); private: /* ===== Internal Event Handlers ===== */ @@ -168,9 +167,8 @@ private: * 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); + void draw_header_decoration(shared_ptr modelTrack, + const Gdk::Rectangle &clip_rect); /** * A helper function which calls lookup_timeline_track within the @@ -182,8 +180,8 @@ private: * @remarks If the return value is going to be NULL, an ENSURE will * fail. */ - boost::shared_ptr lookup_timeline_track( - boost::shared_ptr modelTrack); + shared_ptr + lookup_timeline_track (shared_ptr modelTrack); void begin_drag(); @@ -248,7 +246,7 @@ private: int scrollSlideRate; //----- User Interaction State -----// - boost::shared_ptr hoveringTrack; + shared_ptr hoveringTrack; Gdk::Point mousePoint; diff --git a/src/gui/widgets/timeline/timeline-header-widget.cpp b/src/gui/widgets/timeline/timeline-header-widget.cpp index 734e42dfd..e9588fa82 100644 --- a/src/gui/widgets/timeline/timeline-header-widget.cpp +++ b/src/gui/widgets/timeline/timeline-header-widget.cpp @@ -28,9 +28,10 @@ using namespace Gtk; using namespace std; -using namespace boost; using namespace util; +using std::tr1::shared_ptr; + namespace gui { namespace widgets { namespace timeline { diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp index 6a858aa18..67da78ecd 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.cpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.cpp @@ -24,12 +24,10 @@ #include "gui/widgets/timeline-widget.hpp" #include "lib/time/mutation.hpp" -#include - using namespace gui::widgets; using lib::time::Mutation; -using boost::shared_ptr; /////////////////////////TICKET #796 +using std::tr1::shared_ptr; namespace gui { namespace widgets { @@ -45,15 +43,18 @@ const int IBeamTool::ScrollSlideEventInterval = 40; IBeamTool::IBeamTool(TimelineBody &timeline_body) : Tool(timeline_body), + selectionControl(), dragType(None), pinnedDragTime(), scrollSlideRate(0) { - + // Connect the timlinebody selection to the selectionControl + get_state()->set_selection_control (selectionControl); } IBeamTool::~IBeamTool() { + selectionControl.disconnect(); end_scroll_slide(); } @@ -120,9 +121,7 @@ IBeamTool::on_button_press_event(GdkEventButton* event) // User began the drag in clear space, begin a Select drag dragType = Selection; pinnedDragTime = time; - state->setSelection (Mutation::changeTime(time)); - state->setSelection (Mutation::changeDuration(Duration::NIL)); - //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all + selectionControl (TimeSpan(time, Duration::NIL)); } } } @@ -181,8 +180,8 @@ IBeamTool::on_motion_notify_event(GdkEventMotion *event) bool IBeamTool::on_scroll_slide_timer() { - const Gdk::Rectangle body_rect(get_body_rectangle()); - view_window().shift_view(body_rect.get_width(), scrollSlideRate); + const Gdk::Rectangle body_rect (get_body_rectangle()); + view_window().shift_view (body_rect.get_width(), scrollSlideRate); // Return true to keep the timer going return true; @@ -193,16 +192,17 @@ IBeamTool::set_leading_x(const int x) { shared_ptr state = get_state(); - const bool set_playback_period = dragType == Selection; + // The line below needs handled differently now; + // + //const bool set_playback_period = dragType == Selection; + TimeVar newStartPoint (state->get_view_window().x_to_time(x)); Offset selectionLength (pinnedDragTime, newStartPoint); - + if (newStartPoint > pinnedDragTime) newStartPoint=pinnedDragTime; // use the smaller one as selection start - - state->setSelection (Mutation::changeTime(newStartPoint) , set_playback_period); - state->setSelection (Mutation::changeDuration(selectionLength), set_playback_period); - //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once + + selectionControl (TimeSpan (newStartPoint, Duration(selectionLength))); } void diff --git a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp index 291305cde..ecf5b517d 100644 --- a/src/gui/widgets/timeline/timeline-ibeam-tool.hpp +++ b/src/gui/widgets/timeline/timeline-ibeam-tool.hpp @@ -30,14 +30,17 @@ #include "gui/widgets/timeline/timeline-tool.hpp" #include "lib/time/timevalue.hpp" +#include "lib/time/timequant.hpp" +#include "lib/time/control.hpp" -#include namespace gui { namespace widgets { namespace timeline { using lib::time::TimeVar; +using lib::time::TimeSpan; +using lib::time::Control; /** * A helper class to implement the timeline i-beam tool @@ -135,12 +138,12 @@ private: enum DragType { /** - * No drag is occuring + * No drag is occurring */ None, /** - * A selection drag is occuring. + * A selection drag is occurring. * @remarks The position of one end of the selection was set at * mouse-down of the drag, and the other end is set by * drag-release. @@ -159,6 +162,8 @@ private: }; /* ==== Internals ===== */ + Control selectionControl; + /** * Specifies the type of drag currently taking place. */ diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index 0e311cd7d..df00dd131 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -30,7 +30,7 @@ using namespace Gtk; using namespace std; -using namespace boost; +using namespace std::tr1; using namespace lumiera; using namespace util; using namespace gui::util; @@ -80,17 +80,17 @@ TimelineLayoutHelper::add_branch( } } -optional +boost::optional TimelineLayoutHelper::get_track_header_rect( - boost::weak_ptr track) + std::tr1::weak_ptr track) { if(contains(headerBoxes, track)) { Gdk::Rectangle rect(headerBoxes[track]); rect.set_y(rect.get_y() - timelineWidget.get_y_scroll_offset()); - return optional(rect); + return boost::optional(rect); } - return optional(); + return boost::optional(); } shared_ptr @@ -112,8 +112,8 @@ TimelineLayoutHelper::header_from_point(Gdk::Point point) return shared_ptr(); } -boost::shared_ptr -TimelineLayoutHelper::track_from_y(int y) +shared_ptr +TimelineLayoutHelper::track_from_y (int y) { // Apply the scroll offset y += timelineWidget.get_y_scroll_offset(); @@ -299,8 +299,7 @@ TimelineLayoutHelper::is_animating() const } TimelineLayoutHelper::TrackTree::pre_order_iterator -TimelineLayoutHelper::iterator_from_track( - boost::shared_ptr modelTrack) +TimelineLayoutHelper::iterator_from_track(shared_ptr modelTrack) { REQUIRE(modelTrack); @@ -666,7 +665,7 @@ TimelineLayoutHelper::apply_drop_to_modelTree( timelineWidget.thaw_update_tracks(); } -boost::shared_ptr +shared_ptr TimelineLayoutHelper::get_sequence() const { REQUIRE(timelineWidget.state); diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index bc0f1c39d..2c2cca44a 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -59,7 +59,7 @@ public: /** * Definition of the layout track tree type. */ - typedef lumiera::tree< boost::shared_ptr > TrackTree; + typedef lumiera::tree< std::tr1::shared_ptr > TrackTree; public: /** @@ -101,7 +101,7 @@ public: * @see update_layout() */ boost::optional get_track_header_rect( - boost::weak_ptr track); + std::tr1::weak_ptr track); /** * Searches for a header which has the specified point inside of it. @@ -114,8 +114,7 @@ public: * tracks. * @see update_layout() */ - boost::shared_ptr header_from_point( - Gdk::Point point); + shared_ptr header_from_point (Gdk::Point point); /** * Searches for a tack which has the specified y-offset inside of it. @@ -128,15 +127,15 @@ public: * tracks. * @see update_layout() */ - boost::shared_ptr track_from_y(int y); + 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); + shared_ptr + begin_dragging_track (Gdk::Point const& mouse_point); /** * Drops the dragging track. @@ -185,8 +184,8 @@ public: * @return Returns the model iterator of layoutTree.end() if no * iterator was found. */ - TrackTree::pre_order_iterator iterator_from_track( - boost::shared_ptr modelTrack); + TrackTree::pre_order_iterator + iterator_from_track (shared_ptr modelTrack); /** * A function that recursively calculates the visible height of a @@ -262,7 +261,7 @@ protected: * @see clone_tree_from_sequence() */ void add_branch(TrackTree::iterator_base parent_iterator, - boost::shared_ptr parent); + shared_ptr parent); /** * Recursively calculates the boxes for a given branch in the timeline @@ -298,8 +297,8 @@ protected: * @remarks If the return value is going to be NULL, an ENSURE will * fail. */ - boost::shared_ptr lookup_timeline_track( - boost::shared_ptr modelTrack); + shared_ptr + lookup_timeline_track(shared_ptr modelTrack); /** * A helper function which kicks off the animation timer. @@ -343,7 +342,8 @@ protected: * Helper to get the sequence object from the state. * @return Returns a shared pointer to the sequence. */ - boost::shared_ptr get_sequence() const; + shared_ptr + get_sequence() const; protected: /** @@ -363,7 +363,7 @@ protected: * the update_layout method. * @see update_layout() */ - std::map, Gdk::Rectangle> + std::map, Gdk::Rectangle> headerBoxes; /** diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index afc395710..532e461f7 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -36,7 +36,7 @@ using namespace gui; using namespace gui::widgets; using namespace gui::widgets::timeline; -using boost::shared_ptr; ////////////////////TICKET #796 +using std::tr1::shared_ptr; using gui::util::CairoUtil; using lib::time::Time; using lib::time::TimeVar; @@ -75,6 +75,14 @@ TimelineRuler::TimelineRuler (TimelineWidget &timeline_widget) register_styles(); } +TimelineViewWindow& +TimelineRuler::viewWindow() const +{ + REQUIRE(timelineState); + return timelineState->get_view_window(); +} + + void TimelineRuler::set_mouse_chevron_offset(int offset) { @@ -114,7 +122,7 @@ TimelineRuler::on_expose_event(GdkEventExpose* event) if(!window) return false; - if(timelineWidget.get_state()) + if (timelineState) { // Prepare to render via cairo const Allocation allocation = get_allocation(); @@ -160,11 +168,11 @@ TimelineRuler::on_button_press_event(GdkEventButton* event) { REQUIRE(event != NULL); - if(timelineWidget.get_state()) + if (timelineState) { if(event->button == 1) { - pinnedDragTime = view_window().x_to_time(event->x); + pinnedDragTime = viewWindow().x_to_time(event->x); isDragging = true; } } @@ -219,34 +227,40 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation) } void -TimelineRuler::on_state_changed() +TimelineRuler::on_state_changed (shared_ptr newState) { - if(timelineWidget.get_state()) - { - // Connect up some events - view_window().changed_signal().connect( - sigc::mem_fun(this, &TimelineRuler::on_update_view) ); - } - + REQUIRE (newState); + timelineState = newState; + + propagateStateChange(); +} + +void +TimelineRuler::propagateStateChange() +{ + // Connect up some events + viewWindow().changed_signal().connect( + sigc::mem_fun(this, &TimelineRuler::on_update_view) ); + // Redraw on_update_view(); } + + void TimelineRuler::set_leading_x(const int x) { - shared_ptr state = timelineWidget.get_state(); - - if(state) + if (timelineState) { - TimeVar newStartPoint (view_window().x_to_time(x)); + TimeVar newStartPoint (viewWindow().x_to_time(x)); Offset selectionLength (pinnedDragTime, newStartPoint); if (newStartPoint > pinnedDragTime) newStartPoint=pinnedDragTime; // use the smaller one as selection start - state->setPlaybackPeriod (Mutation::changeTime(newStartPoint) ); - state->setPlaybackPeriod (Mutation::changeDuration(selectionLength)); + timelineState->setPlaybackPeriod (Mutation::changeTime(newStartPoint) ); + timelineState->setPlaybackPeriod (Mutation::changeDuration(selectionLength)); //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once ////////////////////TODO : code duplication with timeline-ibeam-tool 205 } @@ -260,7 +274,8 @@ TimelineRuler::draw_ruler(Cairo::RefPtr cr, REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); - const TimelineViewWindow &window = view_window(); + /////////////////////////////TICKET #861 : what part of these calculation could be centralised within TimelineViewWindow? + const TimelineViewWindow &window = viewWindow(); const gavl_time_t left_offset = _raw(window.get_time_offset()); const int64_t time_scale = window.get_time_scale(); @@ -361,16 +376,15 @@ TimelineRuler::draw_selection(Cairo::RefPtr cr, REQUIRE(cr); REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); + REQUIRE(timelineState); - shared_ptr state = timelineWidget.get_state(); - REQUIRE(state); - const TimelineViewWindow &window = state->get_view_window(); + const TimelineViewWindow &window = timelineState->get_view_window(); Glib::RefPtr
My TiddlyWiki is loading ...

Requires Javascript.
- - Data Backend - design draft - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #8cf
-PrimaryLight: #18f
-PrimaryMid: #04b
-PrimaryDark: #014
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
/*{{{*/
-body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-
-a {color:[[ColorPalette::PrimaryMid]];}
-a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
-a img {border:0;}
-
-h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
-h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
-h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
-
-.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
-.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
-.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
-
-.header {background:[[ColorPalette::PrimaryMid]];}
-.headerShadow {color:[[ColorPalette::Foreground]];}
-.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
-.headerForeground {color:[[ColorPalette::Background]];}
-.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
-
-.tabSelected{color:[[ColorPalette::PrimaryDark]];
-	background:[[ColorPalette::TertiaryPale]];
-	border-left:1px solid [[ColorPalette::TertiaryLight]];
-	border-top:1px solid [[ColorPalette::TertiaryLight]];
-	border-right:1px solid [[ColorPalette::TertiaryLight]];
-}
-.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
-.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
-.tabContents .button {border:0;}
-
-#sidebar {}
-#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
-#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
-
-.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
-.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
-.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
-	border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
-.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
-.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
-.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
-	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
-.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
-.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
-	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
-
-#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
-#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
-
-.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
-
-.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
-.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
-.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
-.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
-
-.tiddler .defaultCommand {font-weight:bold;}
-
-.shadow .title {color:[[ColorPalette::TertiaryDark]];}
-
-.title {color:[[ColorPalette::SecondaryDark]];}
-.subtitle {color:[[ColorPalette::TertiaryDark]];}
-
-.toolbar {color:[[ColorPalette::PrimaryMid]];}
-.toolbar a {color:[[ColorPalette::TertiaryLight]];}
-.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
-.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
-
-.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
-.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
-.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
-.tagging .button, .tagged .button {border:none;}
-
-.footer {color:[[ColorPalette::TertiaryLight]];}
-.selected .footer {color:[[ColorPalette::TertiaryMid]];}
-
-.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
-.sparktick {background:[[ColorPalette::PrimaryDark]];}
-
-.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
-.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
-.lowlight {background:[[ColorPalette::TertiaryLight]];}
-
-.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
-
-.imageLink, #displayArea .imageLink {background:transparent;}
-
-.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
-
-.viewer .listTitle {list-style-type:none; margin-left:-2em;}
-.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
-.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
-.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
-.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
-.viewer code {color:[[ColorPalette::SecondaryDark]];}
-.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
-
-.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
-
-.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
-.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
-.editorFooter {color:[[ColorPalette::TertiaryMid]];}
-
-#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
-#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
-#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
-#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
-.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
-.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
-#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
-/*}}}*/
-
-
-
/*{{{*/
-* html .tiddler {height:1%;}
-
-body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
-
-h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
-h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
-h4,h5,h6 {margin-top:1em;}
-h1 {font-size:1.35em;}
-h2 {font-size:1.25em;}
-h3 {font-size:1.1em;}
-h4 {font-size:1em;}
-h5 {font-size:.9em;}
-
-hr {height:1px;}
-
-a {text-decoration:none;}
-
-dt {font-weight:bold;}
-
-ol {list-style-type:decimal;}
-ol ol {list-style-type:lower-alpha;}
-ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol {list-style-type:decimal;}
-ol ol ol ol ol {list-style-type:lower-alpha;}
-ol ol ol ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol ol ol ol {list-style-type:decimal;}
-
-.txtOptionInput {width:11em;}
-
-#contentWrapper .chkOptionInput {border:0;}
-
-.externalLink {text-decoration:underline;}
-
-.indent {margin-left:3em;}
-.outdent {margin-left:3em; text-indent:-3em;}
-code.escaped {white-space:nowrap;}
-
-.tiddlyLinkExisting {font-weight:bold;}
-.tiddlyLinkNonExisting {font-style:italic;}
-
-/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
-a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
-
-#mainMenu .tiddlyLinkExisting,
-	#mainMenu .tiddlyLinkNonExisting,
-	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
-#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
-
-.header {position:relative;}
-.header a:hover {background:transparent;}
-.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
-.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
-
-.siteTitle {font-size:3em;}
-.siteSubtitle {font-size:1.2em;}
-
-#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
-
-#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
-#sidebarOptions {padding-top:0.3em;}
-#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
-#sidebarOptions input {margin:0.4em 0.5em;}
-#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
-#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
-#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
-#sidebarTabs .tabContents {width:15em; overflow:hidden;}
-
-.wizard {padding:0.1em 1em 0em 2em;}
-.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizardStep {padding:1em 1em 1em 1em;}
-.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
-.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
-.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
-.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
-
-#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
-.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
-#messageArea a {text-decoration:underline;}
-
-.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
-.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
-
-.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
-.popup .popupMessage {padding:0.4em;}
-.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
-.popup li.disabled {padding:0.4em;}
-.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
-.listBreak {font-size:1px; line-height:1px;}
-.listBreak div {margin:2px 0;}
-
-.tabset {padding:1em 0em 0em 0.5em;}
-.tab {margin:0em 0em 0em 0.25em; padding:2px;}
-.tabContents {padding:0.5em;}
-.tabContents ul, .tabContents ol {margin:0; padding:0;}
-.txtMainTab .tabContents li {list-style:none;}
-.tabContents li.listLink { margin-left:.75em;}
-
-#contentWrapper {display:block;}
-#splashScreen {display:none;}
-
-#displayArea {margin:1em 17em 0em 14em;}
-
-.toolbar {text-align:right; font-size:.9em;}
-
-.tiddler {padding:1em 1em 0em 1em;}
-
-.missing .viewer,.missing .title {font-style:italic;}
-
-.title {font-size:1.6em; font-weight:bold;}
-
-.missing .subtitle {display:none;}
-.subtitle {font-size:1.1em;}
-
-.tiddler .button {padding:0.2em 0.4em;}
-
-.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
-.isTag .tagging {display:block;}
-.tagged {margin:0.5em; float:right;}
-.tagging, .tagged {font-size:0.9em; padding:0.25em;}
-.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
-.tagClear {clear:both;}
-
-.footer {font-size:.9em;}
-.footer li {display:inline;}
-
-.annotation {padding:0.5em; margin:0.5em;}
-
-* html .viewer pre {width:99%; padding:0 0 1em 0;}
-.viewer {line-height:1.4em; padding-top:0.5em;}
-.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
-.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
-.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
-
-.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
-.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
-table.listView {font-size:0.85em; margin:0.8em 1.0em;}
-table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
-
-.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
-.viewer code {font-size:1.2em; line-height:1.4em;}
-
-.editor {font-size:1.1em;}
-.editor input, .editor textarea {display:block; width:100%; font:inherit;}
-.editorFooter {padding:0.25em 0em; font-size:.9em;}
-.editorFooter .button {padding-top:0px; padding-bottom:0px;}
-
-.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
-
-.sparkline {line-height:1em;}
-.sparktick {outline:0;}
-
-.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
-.zoomer div {padding:1em;}
-
-* html #backstage {width:99%;}
-* html #backstageArea {width:99%;}
-#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageToolbar {position:relative;}
-#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
-#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
-#backstage {position:relative; width:100%; z-index:50;}
-#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
-.backstagePanelFooter {padding-top:0.2em; float:right;}
-.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
-#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
-
-.whenBackstage {display:none;}
-.backstageVisible .whenBackstage {display:block;}
-/*}}}*/
-
-
-
/***
-StyleSheet for use when a translation requires any css style changes.
-This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
-***/
-
-/*{{{*/
-body {font-size:0.8em;}
-
-#sidebarOptions {font-size:1.05em;}
-#sidebarOptions a {font-style:normal;}
-#sidebarOptions .sliderPanel {font-size:0.95em;}
-
-.subtitle {font-size:0.8em;}
-
-.viewer table.listView {font-size:0.95em;}
-
-.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
-/*}}}*/
-
-
-
/*{{{*/
-@media print {
-#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
-#displayArea {margin: 1em 1em 0em 1em;}
-/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
-noscript {display:none;}
-}
-/*}}}*/
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-<div class='headerShadow'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-<div class='headerForeground'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-</div>
-<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
-<div id='sidebar'>
-<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-<div id='messageArea'></div>
-<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
-<div class='title' macro='view title'></div>
-<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
-<div class='tagging' macro='tagging'></div>
-<div class='tagged' macro='tags'></div>
-<div class='viewer' macro='view text wikified'></div>
-<div class='tagClear'></div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
-<div class='title' macro='view title'></div>
-<div class='editor' macro='edit title'></div>
-<div macro='annotations'></div>
-<div class='editor' macro='edit text'></div>
-<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
-<!--}}}-->
-
-
-
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
-* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
-* MainMenu: The menu (usually on the left)
-* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
-You'll also need to enter your username for signing your edits: <<option txtUserName>>
-
-
-
These InterfaceOptions for customising TiddlyWiki are saved in your browser
-
-Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
-
-<<option txtUserName>>
-<<option chkSaveBackups>> SaveBackups
-<<option chkAutoSave>> AutoSave
-<<option chkRegExpSearch>> RegExpSearch
-<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
-<<option chkAnimate>> EnableAnimations
-
-----
-Also see AdvancedOptions
-
-
- -
-
-
A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far.  Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:
-<<<
-<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.
-
-Try modifying the time spent.  Suppose you've just spent one more hour and want to record it.  Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window.  Watch the time remaining go down to 1 hour.
-
-In reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours.  The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:
-<<<
-<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-You can adjust the current estimate in the same way as you adjusted the time spent.  Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".
-
-You can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases).  Click on the time remaining and add an hour by typing "+1".
-
-When the time remaining goes to zero, the task is considered complete:
-<<<
-<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you haven't already done so, try double-clicking the description.  Yes, it really does open up the editor and select just the text of the description.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 0.5>> Put tasksum on the ViewTemplate.
-<<<
-You can specify just the description of a task, and leave it unestimated.  Click the question mark to enter the estimate:
-<<<
-<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-As this task implies, you can enter two values in the popup when you click on any of the time cells.  Separate them with spaces and/or a comma.  Experiment:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-Finally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there.  That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.
-
-And setting up a taskadder is simplicity itself.  Just add {{{<<taskadder>>}}} to your tiddler.  You will see this:
-<<<
-<<taskadder>>
-<<<
-Just type a task description into the first field, and your initial estimate for how long it will take into the second field.  Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler.  Notice that you can just start typing a new task as soon as you're done entering the first one.
-
-You can have as many taskadders as you like in any tiddler.  The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse.  Try adding some tasks here and in the above adder:
-<<<
-<<taskadder>>
-<<<
-Notice that the one you just used takes focus when this tiddler is redisplayed.
-
-A taskadder by default adds tasks above itself.  You can make it add them below by adding a {{{below}}} argument to the macro call:
-<<<
-<<taskadder below>>
-<<<
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
In this tutorial, we've been looking mostly at individual tasks.  In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler.  In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.
-
-To get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them.  Here's an example:
-<<<
-<<tasksum start>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end>>
-<<<
-If you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.
-<<<
-<<tasksum start here>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<tasksum end>>
-<<<
-You can nest these things if you like, just be sure to match starts and ends:
-<<<
-<<tasksum start here>>
-* Time cell manipulation:<<tasksum start>>
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<tasksum end "Cell manipulation:">>
-<<br>>
-* Double-click handling:<<tasksum start>>
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end "Double-clicks:">>
-
-<<tasksum end>>
-<<<
-Finally, the simplest way to use tasksum is to add it to your view template.  See TaskSummaryViewTemplate for an example template.  Note that if no tasks are present between the start and end, nothing is displayed.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort.  However, there are two issues that may affect you.  (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)
-# The task macros don't play nicely with the default TiddlyWiki display of tags.  In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler.  However, this little box may interfere with the tables used by the task macros.  In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible.  In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box.  If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative.  Simply import it into your wiki and rename it to ViewTemplate.  This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>
-# Most view templates also add a minus sign ("-") before the "close" command.  TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out.  But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it.  It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed.  So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.
-
-----
-This ends the tutorial.  To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
-
-
-
PageTemplate
-|>|SiteTitle - SiteSubtitle|
-|>|MainMenu|
-|DefaultTiddlers<<br>><<br>><<br>>ViewTemplate<<br>><<br>>EditTemplate|SideBarOptions|
-|~|OptionsPanel|
-|~|SideBarTabs|
-|~|AdvancedOptions|
-|~|<<tiddler Configuration.SideBarTabs>>|
-
-''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
-
-ColorPalette
-
-SiteUrl
-
-
-
/***
-|Name|BetterTimelineMacro|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#BetterTimelineMacro|
-|Version|0.5 beta|
-|Requires|~TW2.x|
-!!!Description:
-A replacement for the core timeline macro that offers more features:
-*list tiddlers with only specfic tag
-*exclude tiddlers with a particular tag
-*limit entries to any number of days, for example one week
-*specify a start date for the timeline, only tiddlers after that date will be listed.
-
-!!!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!!!Syntax:
-{{{<<timeline better:true>>}}}
-''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
-
-additonal params:
-(use only the ones you want)
-{{{<<timeline better:true  onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30>>}}}
-
-''explanation of syntax:''
-onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
-excludeTag: tiddlers with this tag will not be listed.
-sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
-firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
-maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
-maxEntries: limit the total number of entries in the timeline.
-
-
-!!!History:
-*28-07-06: ver 0.5 beta, first release
-
-!!!Code
-***/
-//{{{
-// Return the tiddlers as a sorted array
-TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
-{
-          var results = [];
-          this.forEachTiddler(function(title,tiddler)
-          {
-          if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
-                        if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
-                                      results.push(tiddler);
-          });
-          if(field)
-                   results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
-          return results;
-}
-
-
-
-//this function by Udo
-function getParam(params, name, defaultValue)
-{
-          if (!params)
-          return defaultValue;
-          var p = params[0][name];
-          return p ? p[0] : defaultValue;
-}
-
-window.old_timeline_handler= config.macros.timeline.handler;
-config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
-{
-          var args = paramString.parseParams("list",null,true);
-          var betterMode = getParam(args, "better", "false");
-          if (betterMode == 'true')
-          {
-          var sortBy = getParam(args,"sortBy","modified");
-          var excludeTag = getParam(args,"excludeTag",undefined);
-          var includeTag = getParam(args,"onlyTag",undefined);
-          var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
-          var firstDayParam = getParam(args,"firstDay",undefined);
-          var firstDay = (firstDayParam!=undefined)? firstDayParam: "00010101";
-          var lastDay = "";
-          var field= sortBy;
-          var maxDaysParam = getParam(args,"maxDays",undefined);
-          var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
-          var maxEntries = getParam(args,"maxEntries",undefined);
-          var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
-          for(var t=tiddlers.length-1; t>=last; t--)
-                  {
-                  var tiddler = tiddlers[t];
-                  var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
-                  if ((theDay>=firstDay)&& (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
-                     {
-                     if(theDay != lastDay)
-                               {
-                               var theDateList = document.createElement("ul");
-                               place.appendChild(theDateList);
-                               createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
-                               lastDay = theDay;
-                               }
-                  var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
-                  theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
-                  }
-                  }
-          }
-
-          else
-              {
-              window.old_timeline_handler.apply(this,arguments);
-              }
-}
-//}}}
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #f8a
-PrimaryLight: #f48
-PrimaryMid: #824
-PrimaryDark: #412
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
This just starts as braindump, I will refine it soon:
-* handle all files Lumiera uses at runtime (media, edl, temp data)
-* manage filehandles, Lumiera might use more more files than available filehandles
-* manage temporary data
-* do caching
-* io will be blocked where the backend tells the core where it can expect the data (not read()/write() like)
-* kind-of garbage collector
-* do prefetching
-* no/low latency for the core the prefetcher and other things ensure that data is available in time
-* translate any input into a format which the Lumiera core understands (demux, decode)
-* same for encoding to output formats
-* offer a plugin API for encoders/decoders
-* maybe network backend for serving data to distributed render nodes
-* can do some load control or management (trigger adaptive rendering if system is idle etc)
-* pull based arch
-* Serialize persistent data (Project / EDL's)
-
-Look at [[Overview]] for the current design proposal
-
-
-
-
DataBackend
-
-
-
-
[[File]] associates a filename with the underlying FileDescriptor. This allows [[File]]s which have serveral names (hardlinks) to share a underlying backend.
-
-
-
'FileDescriptor' is the superclass of all possible filetypes, it has a weak reference to a FileHandle which is managed in FilehandleCache, on creation only the existence (when reading) or access for write for new files are checked. 'FileDescriptor' stores some generic metadata about the underlying file and intended use. But actual opening is done on demand.
-
-The content is memory mapped into the process address space, this is managed by FileMap objects and a FileMapCache.
-
-
-
-
'FileHandle's are managed by the FileHandleCache, they are just storing the underlying OS file handles and managed in a lazy/weak way, (re)opened when needed and aging in the cache when not needed, since the amount of open file handles is limited aged ones will be closed and reused when the system needs to open another file.
-
-
-
-
Each 'FileMap' object contains many [[Frame]]s. The actual layout depends on the type of the [[File]]. Mappings need to be page aligned while [[Frame]]s can be anywhere within a file and dynamically sized.
-
-All established [[FileMap]]s are managed in a FileMapCache. This is similar to the FileHandleCache, but mappings which are in use are checked out of the aging list and thus become locked from aging/purging.
-
-FileMap objects are transparent to the application. It will only requests [[Frame]]s as in position and size (and some other parameters).
-
-
-
-
The 'FileMapCache' keeps a list of FileMaps which are currently not in use and subject of aging.
-Whenever a FileMap is in use, it is checked out into an in-use list where it is not subject to aging.
-
-
-
-
'FilehandleCache' storing a finite maximum number of [[FileHandle]]s as a list. As long the configured maximum of open files is not reached new file handles are stored at the begin of the list. Whenever a filehandle is accessed it is moved to the begin of the list too. Unused filehandles propagate towards the end of the list. When the maximum of open filehandles is reached, aged filehandles are closed and taken from the end.
-
-
-
-
'Frames' are the smallest datablocks handled by the Backend. The application tells the Backend to make [[File]]s available and then only requests Frames from the Backend. All other datastructures of the backend are private.
-
-Actually Frames are (references to) blocks of continuous memory. They can be anything depending on the usage of the [[File]] (Video frames, encoder frames, blocks of sound samples).
-
-Each [[Frame]] points to a [[FrameDescriptor]] which describes the shared properties of [[Frame]]s of the same kind. For video frames this [[FrameDescriptor]] will define the policies of the used color model, resolution, aspect ratio and so on, for example.
-
-Frames are referenced by a smart-pointer like object which manages the lifetime and caching behavior. There are 3 states such a frame reference can be in:
-# readonly: the backing FileMap is checked out from the aging list, frames can be read
-# readwrite: the backing FileMap is checked out from the aging list, frames can be read and written (depends on the filemode as well)
-# weak: the FileMap object is checked back into the aging list, the frame can't be accessed but we can try to transform a weak reference into a readonly or readwrite reference
-
-Frames can be addressed uniquely (needs to be worked out) whenever a frame is not available. The backend can initiate a (probably recursive) render for it.
-
-Accessing [[Frame]]s may add further renderjobs for related frames to the [[Prefetch]] task.
-
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
-
/***
-|''Name:''|InlineJavascriptPlugin|
-|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
-|''Author:''|Eric Shulman - ELS Design Studios|
-|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
-|''~CoreVersion:''|2.0.10|
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Display script source in tiddler output''
-By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-><script show>
- alert('InlineJavascriptPlugin: this is a demonstration message');
-</script>
-dynamic output:
-><script show>
- return (new Date()).toString();
-</script>
-wikified dynamic output:
-><script show>
- return "link to current user: [["+config.options.txtUserName+"]]";
-</script>
-dynamic output using 'place' to get size information for current tiddler:
-><script show>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-creating an 'onclick' button/link that runs a script:
-><script label="click here" show>
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-loading a script from a source url:
->http://www.TiddlyTools.com/demo.js contains:
->>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
->>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
-><script src="demo.js" show>
- return "loading demo.js..."
-</script>
-><script label="click to execute demo() function" show>
- demo()
-</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
-''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
-''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]'' initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[4]) { // there is script code
- if (lookaheadMatch[3]) // show inline script code in tiddler output
- wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
- if (lookaheadMatch[2]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[4]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else { // run inline script code
- var code="function _out(place){"+lookaheadMatch[4]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
- }
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
''[[Lumiera|index.html]]''
-DataBackend
-[[Overview]]
-<<fullscreen>>
-
-
-
-
<!--{{{-->
-<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
-<!--}}}-->
-
-<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>My TiddlyWiki</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>
-
-
-
how is FileMetadata kept
-
-copying semantics of smart pointers
-
-explain opening/closing files (use() forget()?)
-
-difference between actual files and temporary. does it make sense to have temporary storage on diffent speed disks?
-
-statistics hit/fail max/min/avg timings, hard / soft fails, timing constraints, when is rerendering cheaper than caching?..
-
-adaptive rendering
-
-background rendering
-
-renderfarm
-
-[[FrameDescriptor]]s and [[Frame]] details, Policies composing frames
-
-Storage and logging of EDL's, unlimited undo, database,...
-
-When to Cache and when not to cache, aka instant [[Frame]] reuse
-
-
-
-
Whenever Lumiera needs to access data this is done through the DataBackend described here. 
-
-There are two main kinds how data is handled:
-* Project Description and EDL's are handled in a InMemoryDatabase which uses a [[Serializer]] for storing and logging modifications.
-* Media (audio, video, ...) is mapped as described below.
-
-The backend uses memory mapping to make data available to the program. This is little different to more common open/read/write/close file access while giving superior performance and much better memory utilization.
-
-The data backend must be capable to handle more data than will fit into the memory or even address space on 32 bit architectures.  Moreover a project may access more files than the OS can handle at a time, thus the for [[File]]s used by the Backend it needs a FilehandleCache to manage filehandles dynamically.
-
-Which parts of a File are actually mapped to physical RAM is managed by the kernel, it keeps a FileMapCache to manage the [[FileMap]]s we've set up.
-
-The application itself only requests [[Frame]]s from the backend.
-
-To minimize latency and optimize CPU utilization we have a [[Prefetch]] thread which operates a [[Scheduler]] to render and cache frames which are expected to be consumed soon. This prefetcher keeps [[Statistics]] for optimizing performance.
-
-
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-	<div class='headerShadow'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-	<div class='headerForeground'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-</div>
-<!-- horizontal MainMenu -->
-<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
-<!-- original MainMenu menu -->
-<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
-<div id='sidebar'>
-	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-	<div id='messageArea'></div>
-	<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
-
/***
-|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
-|''Version:''|1.0.6 (2006-11-07)|
-|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
-|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
-|''Licence:''|[[BSD open source license]]|
-|''TiddlyWiki:''|2.0|
-|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
-!Table of Content<html><a name="TOC"/></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
-!Description<html><a name="Description"/></html>
-With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
-Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts, use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
-
-''Syntax:'' 
-|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
-|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//.|
-|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
-|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
-|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Applications<html><a name="Applications"/></html>
-!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
-Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
-
-Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Citation Index<html><a name="Citation"/></html>
-Create a tiddler "Citations" that contains your "citations". 
-Wrap every citation with a part and a proper name. 
-
-''Example''
-{{{
-<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
-in //Proc. ICSM//, 1998.</part>
-
-<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
-Thesis, Uni Stuttgart, 2002.</part>
-
-<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
-in //Proc. ICSM//, 1999.</part>
-}}}
-
-You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
-You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
-{{{
-* Item 1
-* Item 2
-* Item 3
-}}}
-into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
-
-Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
-
-''Example''
-{{{
-|!Subject|!Items|
-|subject1|<<tiddler ./Cell1>>|
-|subject2|<<tiddler ./Cell2>>|
-
-<part Cell1 hidden>
-* Item 1
-* Item 2
-* Item 3
-</part>
-...
-}}}
-
-Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
-
-BTW: The same approach can be used to create bullet lists with items that contain more than one line.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating Tabs<html><a name="Tabs"/></html>
-The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
-
-With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
-
-''Example''
-The standard tabs at the sidebar are defined by the following eight tiddlers:
-* SideBarTabs
-* TabAll
-* TabMore
-* TabMoreMissing
-* TabMoreOrphans
-* TabMoreShadowed
-* TabTags
-* TabTimeline
-
-Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
-{{{
-<<tabs txtMainTab 
- Timeline Timeline SideBarTabs/Timeline 
- All 'All tiddlers' SideBarTabs/All 
- Tags 'All tags' SideBarTabs/Tags 
- More 'More lists' SideBarTabs/More>>
-<part Timeline hidden><<timeline>></part>
-<part All hidden><<list all>></part>
-<part Tags hidden><<allTags>></part>
-<part More hidden><<tabs txtMoreTab 
- Missing 'Missing tiddlers' SideBarTabs/Missing 
- Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
- Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
-<part Missing hidden><<list missing>></part>
-<part Orphans hidden><<list orphans>></part>
-<part Shadowed hidden><<list shadowed>></part>
-}}}
-
-Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
-
-E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
-{{{
-<<forEachTiddler 
- sortBy 'tiddler.modified' descending 
- write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
-}}}
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Using Sliders<html><a name="Sliders"/></html>
-Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
-
-''Example''
-In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
-{{{
-...
-<<slider chkAboutDetails About/Details details "Click here to see more details">>
-<part Details hidden>
-To give you a better overview ...
-</part>
-...
-}}}
-
-Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Revision history<html><a name="Revisions"/></html>
-* v1.0.6 (2006-11-07)
-** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
-* v1.0.5 (2006-03-02)
-** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
-* v1.0.4 (2006-02-28)
-** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
-* v1.0.3 (2006-02-26)
-** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
-* v1.0.2 (2006-02-05)
-** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
-* v1.0.1 (2006-01-27)
-** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
-** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
-* v1.0.0 (2006-01-25)
-** initial version
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Code<html><a name="Code"/></html>
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-//{{{
-//============================================================================
-// PartTiddlerPlugin
-
-// Ensure that the PartTiddler Plugin is only installed once.
-//
-if (!version.extensions.PartTiddlerPlugin) {
-
-
-
-version.extensions.PartTiddlerPlugin = {
- major: 1, minor: 0, revision: 6,
- date: new Date(2006, 10, 7), 
- type: 'plugin',
- source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
-};
-
-if (!window.abego) window.abego = {};
-if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
-
-//============================================================================
-// Common Helpers
-
-// Looks for the next newline, starting at the index-th char of text. 
-//
-// If there are only whitespaces between index and the newline 
-// the index behind the newline is returned, 
-// otherwise (or when no newline is found) index is returned.
-//
-var skipEmptyEndOfLine = function(text, index) {
- var re = /(\n|[^\s])/g;
- re.lastIndex = index;
- var result = re.exec(text);
- return (result && text.charAt(result.index) == '\n') 
- ? result.index+1
- : index;
-}
-
-
-//============================================================================
-// Constants
-
-var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
-var partEndTagREString = "<\\/part>";
-var partEndTagString = "</part>";
-
-//============================================================================
-// Plugin Specific Helpers
-
-// Parse the parameters inside a <part ...> tag and return the result.
-//
-// @return [may be null] {partName: ..., isHidden: ...}
-//
-var parseStartTagParams = function(paramText) {
- var params = paramText.readMacroParams();
- if (params.length == 0 || params[0].length == 0) return null;
- 
- var name = params[0];
- var paramsIndex = 1;
- var hidden = false;
- if (paramsIndex < params.length) {
- hidden = params[paramsIndex] == "hidden";
- paramsIndex++;
- }
- 
- return {
- partName: name, 
- isHidden: hidden
- };
-}
-
-// Returns the match to the next (end or start) part tag in the text, 
-// starting the search at startIndex.
-// 
-// When no such tag is found null is returned, otherwise a "Match" is returned:
-// [0]: full match
-// [1]: matched "end" tag (or null when no end tag match)
-// [2]: matched "start" tag (or null when no start tag match)
-// [3]: content of start tag (or null if no start tag match)
-//
-var findNextPartEndOrStartTagMatch = function(text, startIndex) {
- var re = new RegExp(partEndOrStartTagRE);
- re.lastIndex = startIndex;
- var match = re.exec(text);
- return match;
-}
-
-//============================================================================
-// Formatter
-
-// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
-//
-// @return true if a complete part section (including the end tag) could be processed, false otherwise.
-//
-var handlePartSection = function(w) {
- var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
- if (!tagMatch) return false;
- if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
-
- // Parse the start tag parameters
- var arguments = parseStartTagParams(tagMatch[3]);
- if (!arguments) return false;
- 
- // Continue processing
- var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
- var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
- if (endMatch && endMatch[1]) {
- if (!arguments.isHidden) {
- w.nextMatch = startTagEndIndex;
- w.subWikify(w.output,partEndTagREString);
- }
- w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
- 
- return true;
- }
- return false;
-}
-
-config.formatters.push( {
- name: "part",
- match: "<part\\s+[^>]+>",
- 
- handler: function(w) {
- if (!handlePartSection(w)) {
- w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
- }
- }
-} )
-
-//============================================================================
-// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
-// as tiddlers.
-
-var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
-
-// Return the match to the first <part ...> tag of the text that has the
-// requrest partName.
-//
-// @return [may be null]
-//
-var findPartStartTagByName = function(text, partName) {
- var i = 0;
- 
- while (true) {
- var tagMatch = findNextPartEndOrStartTagMatch(text, i);
- if (!tagMatch) return null;
-
- if (tagMatch[2]) {
- // Is start tag
- 
- // Check the name
- var arguments = parseStartTagParams(tagMatch[3]);
- if (arguments && arguments.partName == partName) {
- return tagMatch;
- }
- }
- i += tagMatch[0].length;
- }
-}
-
-// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
-// object, using fullName as the Tiddler's title. 
-//
-// All remaining properties of the new Tiddler (tags etc.) are inherited from 
-// the parentTiddler.
-// 
-// @return [may be null]
-//
-var getPart = function(parentTiddler, partName, fullName) {
- var text = parentTiddler.text;
- var startTag = findPartStartTagByName(text, partName);
- if (!startTag) return null;
- 
- var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
- var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
-
- if (indexOfEndTag >= 0) {
- var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
- var partTiddler = new Tiddler();
- partTiddler.set(
- fullName,
- partTiddlerText,
- parentTiddler.modifier,
- parentTiddler.modified,
- parentTiddler.tags,
- parentTiddler.created);
- partTiddler.abegoIsPartTiddler = true;
- return partTiddler;
- }
- 
- return null;
-}
-
-// Hijack the store.fetchTiddler to recognize the "part" addresses.
-//
-
-var oldFetchTiddler = store.fetchTiddler ;
-store.fetchTiddler = function(title) {
- var result = oldFetchTiddler.apply(this, arguments);
- if (!result && title) {
- var i = title.lastIndexOf('/');
- if (i > 0) {
- var parentName = title.substring(0, i);
- var partName = title.substring(i+1);
- var parent = (parentName == ".") 
- ? currentParent 
- : oldFetchTiddler.apply(this, [parentName]);
- if (parent) {
- return getPart(parent, partName, parent.title+"/"+partName);
- }
- }
- }
- return result; 
-};
-
-
-// The user must not edit a readOnly/partTiddler
-//
-
-config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
-
-Tiddler.prototype.isReadOnly = function() {
- // Tiddler.isReadOnly was introduced with TW 2.0.6.
- // For older version we explicitly check the global readOnly flag
- if (config.commands.editTiddler.oldIsReadOnlyFunction) {
- if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
- } else {
- if (readOnly) return true;
- }
-
- return this.abegoIsPartTiddler;
-}
-
-config.commands.editTiddler.handler = function(event,src,title)
-{
- var t = store.getTiddler(title);
- // Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
- // or the tiddler is not readOnly
- if(!t || !t.abegoIsPartTiddler)
- {
- clearMessage();
- story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
- story.focusTiddler(title,"text");
- return false;
- }
-}
-
-// To allow the "./partName" syntax in macros we need to hijack 
-// the invokeMacro to define the "currentParent" while it is running.
-// 
-var oldInvokeMacro = window.invokeMacro;
-function myInvokeMacro(place,macro,params,wikifier,tiddler) {
- var oldCurrentParent = currentParent;
- if (tiddler) currentParent = tiddler;
- try {
- oldInvokeMacro.apply(this, arguments);
- } finally {
- currentParent = oldCurrentParent;
- }
-}
-window.invokeMacro = myInvokeMacro;
-
-// Scroll the anchor anchorName in the viewer of the given tiddler visible.
-// When no tiddler is defined use the tiddler of the target given event is used.
-window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
- var tiddlerElem = null;
- if (tiddler) {
- tiddlerElem = document.getElementById(story.idPrefix + tiddler);
- }
- if (!tiddlerElem && evt) {
- var target = resolveTarget(evt);
- tiddlerElem = story.findContainingTiddler(target);
- }
- if (!tiddlerElem) return;
-
- var children = tiddlerElem.getElementsByTagName("a");
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var name = child.getAttribute("name");
- if (name == anchorName) {
- var y = findPosY(child);
- window.scrollTo(0,y);
- return;
- }
- }
-}
-
-} // of "install only once"
-//}}}
-
-/***
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Licence and Copyright
-Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-Neither the name of abego Software nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-
-
-
There are 2 important points when we want to access data with low latency:
-# Since we handle much more data than it will fit into most computers RAM. The data which is backed in files has to be paged in and available when needed. The [[Prefetch]] Thread manages page hinting to the kernel (posix_madvise()..)
-# Intermediate [[Frame]]s must eventually be rendered to the cache. The Backend will send Renderjobs to the Controller.
-
-Both of these actions are managed by a [[Scheduler]].
-
-Whenever something queries a [[Frame]] from the backend it provides hints about what it is doing.
-These hints contain:
-
-* Timing constraints
-** When will the [[Frame]] be needed
-** could we drop the request if it won't be available (rendered) in-time
-* Priority of this job (as soon as possible, or just in time?)
-* action (Playing forward, playing backward, tweaking, playback speed, recursive rendering of dependent frames)
-
-Notes:
-* The Backend will try to render related frames in groups.
-** This means that following frames are scheduled with lower priority. Whenever the program really requests them the priority will be adjusted.
-
-
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
//last update: RSSReaderPlugin v 1.1.1//
-
-!Description
-This plugin provides a RSSReader for TiddlyWiki
-* It accesses asynchronously an RSSFeed
-*Depending on the chanel item format, each item could be written as :
-**simple text wikified
-**html
-
-!Usage
-{{{
-<<rssReader noDesc|asHtml|asText rssUrl ['filtering string']>>
-	noDesc: only title of item is printed
-
-	asHtml: if you know that description contain html (links, img ...), 
-		the text is enclosed with <html> </html> tags
-
- 	asText: if the description should not be interpreted as html the 
-		description is wikified
-
-	rssUrl: the rssFeed url that could be accessed. 
-	
-	'filtering string': if present, the rssfeed item title must contained 
-		this string to be displayed. 
-		If 'filering string' contained space characters only, the tiddler 
-		title is used for filtering.
-
-}}}
-
-For security reasons, if the TiddlyWiki is accessed from http, a ProxyService should be used to access an rssFeed from an other site.
-
-!examples
-| !reader | !RSSFeed type | !working from |
-| BidiXTWRSS | Description asHtml | file: or tiddlywiki.bidix.info |
-| [[Le Monde]] | Description asText | file: or tiddlywiki.bidix.info using proxy |
-| YahooNewsSport | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| TiddlyWikiRSS | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| [[Libération]] | noDesc | file: or tiddlywiki.bidix.info using proxy |
-| [[TestComment]] | asText and filters | file: or tiddlywiki.bidix.info using proxy |
-see : <<tag RSSFeed>> for the full list.
-
-!Revision history
-* V1.1.0 (2207/04/13)
-**No more import functions
-* V1.0.0 (2006/11/11)
-**refactoring using core loadRemoteFile function
-**import using new tiddlywiki:tiddler element
-**import and presentation preserved without EricShulman's NestedSliderPlugin
-**better display of items 
-* v0.3.0 (24/08/2006)
-** Filter on RSS item title
-** Place to display redefined for asynchronous processing
-* v0.2.2 (22/08/2006)
-**Haloscan feed has no pubDate.
-* v0.2.1 (08/05/2006)
-* v0.2.0 (01/05/2006)
-**Small adapations for del.icio.us feed
-* v0.1.1 (28/04/2006)
-**Bug : Channel without title 
-* v0.1.0 (24/04/2006)
-** initial release
-
-
-
-
-
-
Scheduling is done with two priority queues, one for high priority jobs and one for low priority jobs. These priority queues are ordered by absolute time values (and a job identifier, details will be worked out at implementation time).
-
-there are following (non exhaustive) kinds of jobs:
-* start job
-* cancel job, if not finished (abort when out of time)
-* unschedule job
-
-Jobs implement a kind of future, datastructures which block a querier until data is available.
-
-The Job scheduler runs singlethreaded. Its only task is to schedule and delegate jobs to worker threads, by itself it will never do any extensive processing!
-
-Each job has an option what to do when its times expires (abort, proceed).
-
-The high priority queue is ordered by the __start__ time T, when the job has to be started (plus some hystersis H). A high priority job becomes scheduled beginning with time T but no later than T+H. 
-
-The low priority queue is ordered by __end__ time T, when the job has to be finished (minus a spawn S). Low priority jobs are started as soon as system load permits, the high priority queue is empty (for some usec in future, lets say 100) and the time is earlier than ~T-S. When a job is expired, it is removed from the queue, when it is already running it is handled as defined in its expire policy.
-
-Canceling and expireing jobs gets noted in Statistics to adjust performance and timings for optimal performance.
-
-
-
-
<<search>><<closeAll>><<permaview>><<newTiddler>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
-
-
-
design draft
-
-
-
-
Data Backend
-
-
-
/***
-
-''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
-
-|Name|SplashScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#SplashScreenPlugin|
-|Version|0.21 |
-|Requires|~TW2.08+|
-!Description:
-Provides a simple splash screen that is visible while the TW is loading.
-
-!Installation
-Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
-
-!Customizing
-Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
-
-!History
-* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
-* 26-06-06 : version 0.2, first release
-
-!Code
-***/
-//{{{
-var old_lewcid_splash_restart=restart;
-
-restart = function()
-{   if (document.getElementById("SplashScreen"))
-        document.getElementById("SplashScreen").style.display = "none";
-      if (document.getElementById("contentWrapper"))
-        document.getElementById("contentWrapper").style.display = "block";
-    
-    old_lewcid_splash_restart();
-   
-    if (splashScreenInstall)
-       {if(config.options.chkAutoSave)
-			{saveChanges();}
-        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
-        }
-}
-
-
-var oldText = store.getTiddlerText("MarkupPreHead");
-if (oldText.indexOf("SplashScreen")==-1)
-   {var siteTitle = store.getTiddlerText("SiteTitle");
-   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
-   if (! store.tiddlerExists("MarkupPreHead"))
-       {var myTiddler = store.createTiddler("MarkupPreHead");}
-   else
-      {var myTiddler = store.getTiddler("MarkupPreHead");}
-      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
-      store.setDirty(true);
-      var splashScreenInstall = true;
-}
-//}}}
-
-
-
/*{{{*/
-/* a contrasting background so I can see where one tiddler ends and the other begins */
-body {
-	background: [[ColorPalette::TertiaryLight]];
-}
-
-/* sexy colours and font for the header */
-.headerForeground {
-	color: [[ColorPalette::PrimaryPale]];
-}
-.headerShadow, .headerShadow a {
-	color: [[ColorPalette::PrimaryMid]];
-}
-.headerForeground, .headerShadow {
-	padding: 1em 1em 0;
-	font-family: 'Trebuchet MS' sans-serif;
-	font-weight:bold;
-}
-.headerForeground .siteSubtitle {
-	color: [[ColorPalette::PrimaryLight]];
-}
-.headerShadow .siteSubtitle {
-	color: [[ColorPalette::PrimaryMid]];
-}
-
-/* make shadow go and down right instead of up and left */
-.headerShadow {
-	left: 2px;
-	top: 3px;
-}
-
-/* prefer monospace for editing */
-.editor textarea {
-	font-family: 'Consolas' monospace;
-}
-
-/* sexy tiddler titles */
-.title {
-	font-size: 250%;
-	color: [[ColorPalette::PrimaryLight]];
-	font-family: 'Trebuchet MS' sans-serif;
-}
-
-/* more subtle tiddler subtitle */
-.subtitle {
-	padding:0px;
-	margin:0px;
-	padding-left:0.5em;
-	font-size: 90%;
-	color: [[ColorPalette::TertiaryMid]];
-}
-.subtitle .tiddlyLink {
-	color: [[ColorPalette::TertiaryMid]];
-}
-
-/* a little bit of extra whitespace */
-.viewer {
-	padding-bottom:3px;
-}
-
-/* don't want any background color for headings */
-h1,h2,h3,h4,h5,h6 {
-	background: [[ColorPalette::Background]];
-	color: [[ColorPalette::Foreground]];
-}
-
-/* give tiddlers 3d style border and explicit background */
-.tiddler {
-	background: [[ColorPalette::Background]];
-	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
-	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
-	margin-bottom: 1em;
-	padding-bottom: 2em;
-}
-
-/* make options slider look nicer */
-#sidebarOptions .sliderPanel {
-	border:solid 1px [[ColorPalette::PrimaryLight]];
-}
-
-
-/* the borders look wrong with the body background */
-#sidebar .button {
-	border-style: none;
-}
-
-/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
-.tagglyTagged li.listTitle {
-	display:none
-}
-.tagglyTagged li {
-	display: inline; font-size:90%;
-}
-.tagglyTagged ul {
-	margin:0px; padding:0px;
-}
-
-/* this means you can put line breaks in SidebarOptions for readability */
-#sidebarOptions br {
-	display:none;
-}
-/* undo the above in OptionsPanel */
-#sidebarOptions .sliderPanel br {
-	display:inline;
-}
-
-/* horizontal main menu stuff */
-#displayArea {
-	margin: 1em 15.7em 0em 1em; /* use the freed up space */
-}
-#topMenu br {
-	display: none;
-}
-#topMenu {
-	background: [[ColorPalette::PrimaryMid]];
-	color:[[ColorPalette::PrimaryPale]];
-}
-#topMenu {
-	padding:2px;
-}
-#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
-	margin-left: 0.5em;
-	margin-right: 0.5em;
-	padding-left: 3px;
-	padding-right: 3px;
-	color: [[ColorPalette::PrimaryPale]];
-	font-size: 115%;
-}
-#topMenu .button:hover, #topMenu .tiddlyLink:hover {
-	background: [[ColorPalette::PrimaryDark]];
-}
-
-/* make it print a little cleaner */
-@media print {
-	#topMenu {
-		display: none ! important;
-	}
-	/* not sure if we need all the importants */
-	.tiddler {
-		border-style: none ! important;
-		margin:0px ! important;
-		padding:0px ! important;
-		padding-bottom:2em ! important;
-	}
-	.tagglyTagging .button, .tagglyTagging .hidebutton {
-		display: none ! important;
-	}
-	.headerShadow {
-		visibility: hidden ! important;
-	}
-	.tagglyTagged .quickopentag, .tagged .quickopentag {
-		border-style: none ! important;
-	}
-	.quickopentag a.button, .miniTag {
-		display: none ! important;
-	}
-}
-/*}}}*/
-
-
-
-
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
-
- - - - - - - - - - diff --git a/wiki/compatibility.html b/wiki/compatibility.html deleted file mode 100644 index 13a2392a4..000000000 --- a/wiki/compatibility.html +++ /dev/null @@ -1,11655 +0,0 @@ - - - - - - - - - - - -
My TiddlyWiki is loading ...

Requires Javascript.
- - Compatibility - Dependencies and Style - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #8cf
-PrimaryLight: #18f
-PrimaryMid: #04b
-PrimaryDark: #014
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
/*{{{*/
-body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-
-a {color:[[ColorPalette::PrimaryMid]];}
-a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
-a img {border:0;}
-
-h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
-h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
-h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
-
-.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
-.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
-.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
-
-.header {background:[[ColorPalette::PrimaryMid]];}
-.headerShadow {color:[[ColorPalette::Foreground]];}
-.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
-.headerForeground {color:[[ColorPalette::Background]];}
-.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
-
-.tabSelected{color:[[ColorPalette::PrimaryDark]];
-	background:[[ColorPalette::TertiaryPale]];
-	border-left:1px solid [[ColorPalette::TertiaryLight]];
-	border-top:1px solid [[ColorPalette::TertiaryLight]];
-	border-right:1px solid [[ColorPalette::TertiaryLight]];
-}
-.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
-.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
-.tabContents .button {border:0;}
-
-#sidebar {}
-#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
-#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
-
-.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
-.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
-.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
-	border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
-.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
-.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
-.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
-	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
-.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
-.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
-	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
-
-#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
-#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
-
-.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
-
-.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
-.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
-.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
-.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
-
-.tiddler .defaultCommand {font-weight:bold;}
-
-.shadow .title {color:[[ColorPalette::TertiaryDark]];}
-
-.title {color:[[ColorPalette::SecondaryDark]];}
-.subtitle {color:[[ColorPalette::TertiaryDark]];}
-
-.toolbar {color:[[ColorPalette::PrimaryMid]];}
-.toolbar a {color:[[ColorPalette::TertiaryLight]];}
-.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
-.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
-
-.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
-.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
-.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
-.tagging .button, .tagged .button {border:none;}
-
-.footer {color:[[ColorPalette::TertiaryLight]];}
-.selected .footer {color:[[ColorPalette::TertiaryMid]];}
-
-.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
-.sparktick {background:[[ColorPalette::PrimaryDark]];}
-
-.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
-.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
-.lowlight {background:[[ColorPalette::TertiaryLight]];}
-
-.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
-
-.imageLink, #displayArea .imageLink {background:transparent;}
-
-.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
-
-.viewer .listTitle {list-style-type:none; margin-left:-2em;}
-.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
-.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
-.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
-.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
-.viewer code {color:[[ColorPalette::SecondaryDark]];}
-.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
-
-.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
-
-.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
-.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
-.editorFooter {color:[[ColorPalette::TertiaryMid]];}
-
-#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
-#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
-#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
-#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
-.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
-.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
-#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
-/*}}}*/
-
-
-
/*{{{*/
-* html .tiddler {height:1%;}
-
-body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
-
-h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
-h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
-h4,h5,h6 {margin-top:1em;}
-h1 {font-size:1.35em;}
-h2 {font-size:1.25em;}
-h3 {font-size:1.1em;}
-h4 {font-size:1em;}
-h5 {font-size:.9em;}
-
-hr {height:1px;}
-
-a {text-decoration:none;}
-
-dt {font-weight:bold;}
-
-ol {list-style-type:decimal;}
-ol ol {list-style-type:lower-alpha;}
-ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol {list-style-type:decimal;}
-ol ol ol ol ol {list-style-type:lower-alpha;}
-ol ol ol ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol ol ol ol {list-style-type:decimal;}
-
-.txtOptionInput {width:11em;}
-
-#contentWrapper .chkOptionInput {border:0;}
-
-.externalLink {text-decoration:underline;}
-
-.indent {margin-left:3em;}
-.outdent {margin-left:3em; text-indent:-3em;}
-code.escaped {white-space:nowrap;}
-
-.tiddlyLinkExisting {font-weight:bold;}
-.tiddlyLinkNonExisting {font-style:italic;}
-
-/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
-a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
-
-#mainMenu .tiddlyLinkExisting,
-	#mainMenu .tiddlyLinkNonExisting,
-	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
-#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
-
-.header {position:relative;}
-.header a:hover {background:transparent;}
-.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
-.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
-
-.siteTitle {font-size:3em;}
-.siteSubtitle {font-size:1.2em;}
-
-#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
-
-#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
-#sidebarOptions {padding-top:0.3em;}
-#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
-#sidebarOptions input {margin:0.4em 0.5em;}
-#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
-#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
-#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
-#sidebarTabs .tabContents {width:15em; overflow:hidden;}
-
-.wizard {padding:0.1em 1em 0em 2em;}
-.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizardStep {padding:1em 1em 1em 1em;}
-.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
-.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
-.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
-.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
-
-#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
-.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
-#messageArea a {text-decoration:underline;}
-
-.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
-.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
-
-.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
-.popup .popupMessage {padding:0.4em;}
-.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
-.popup li.disabled {padding:0.4em;}
-.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
-.listBreak {font-size:1px; line-height:1px;}
-.listBreak div {margin:2px 0;}
-
-.tabset {padding:1em 0em 0em 0.5em;}
-.tab {margin:0em 0em 0em 0.25em; padding:2px;}
-.tabContents {padding:0.5em;}
-.tabContents ul, .tabContents ol {margin:0; padding:0;}
-.txtMainTab .tabContents li {list-style:none;}
-.tabContents li.listLink { margin-left:.75em;}
-
-#contentWrapper {display:block;}
-#splashScreen {display:none;}
-
-#displayArea {margin:1em 17em 0em 14em;}
-
-.toolbar {text-align:right; font-size:.9em;}
-
-.tiddler {padding:1em 1em 0em 1em;}
-
-.missing .viewer,.missing .title {font-style:italic;}
-
-.title {font-size:1.6em; font-weight:bold;}
-
-.missing .subtitle {display:none;}
-.subtitle {font-size:1.1em;}
-
-.tiddler .button {padding:0.2em 0.4em;}
-
-.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
-.isTag .tagging {display:block;}
-.tagged {margin:0.5em; float:right;}
-.tagging, .tagged {font-size:0.9em; padding:0.25em;}
-.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
-.tagClear {clear:both;}
-
-.footer {font-size:.9em;}
-.footer li {display:inline;}
-
-.annotation {padding:0.5em; margin:0.5em;}
-
-* html .viewer pre {width:99%; padding:0 0 1em 0;}
-.viewer {line-height:1.4em; padding-top:0.5em;}
-.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
-.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
-.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
-
-.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
-.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
-table.listView {font-size:0.85em; margin:0.8em 1.0em;}
-table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
-
-.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
-.viewer code {font-size:1.2em; line-height:1.4em;}
-
-.editor {font-size:1.1em;}
-.editor input, .editor textarea {display:block; width:100%; font:inherit;}
-.editorFooter {padding:0.25em 0em; font-size:.9em;}
-.editorFooter .button {padding-top:0px; padding-bottom:0px;}
-
-.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
-
-.sparkline {line-height:1em;}
-.sparktick {outline:0;}
-
-.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
-.zoomer div {padding:1em;}
-
-* html #backstage {width:99%;}
-* html #backstageArea {width:99%;}
-#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageToolbar {position:relative;}
-#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
-#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
-#backstage {position:relative; width:100%; z-index:50;}
-#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
-.backstagePanelFooter {padding-top:0.2em; float:right;}
-.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
-#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
-
-.whenBackstage {display:none;}
-.backstageVisible .whenBackstage {display:block;}
-/*}}}*/
-
-
-
/***
-StyleSheet for use when a translation requires any css style changes.
-This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
-***/
-
-/*{{{*/
-body {font-size:0.8em;}
-
-#sidebarOptions {font-size:1.05em;}
-#sidebarOptions a {font-style:normal;}
-#sidebarOptions .sliderPanel {font-size:0.95em;}
-
-.subtitle {font-size:0.8em;}
-
-.viewer table.listView {font-size:0.95em;}
-
-.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
-/*}}}*/
-
-
-
/*{{{*/
-@media print {
-#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
-#displayArea {margin: 1em 1em 0em 1em;}
-/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
-noscript {display:none;}
-}
-/*}}}*/
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-<div class='headerShadow'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-<div class='headerForeground'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-</div>
-<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
-<div id='sidebar'>
-<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-<div id='messageArea'></div>
-<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
-<div class='title' macro='view title'></div>
-<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
-<div class='tagging' macro='tagging'></div>
-<div class='tagged' macro='tags'></div>
-<div class='viewer' macro='view text wikified'></div>
-<div class='tagClear'></div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
-<div class='title' macro='view title'></div>
-<div class='editor' macro='edit title'></div>
-<div macro='annotations'></div>
-<div class='editor' macro='edit text'></div>
-<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
-<!--}}}-->
-
-
-
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
-* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
-* MainMenu: The menu (usually on the left)
-* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
-You'll also need to enter your username for signing your edits: <<option txtUserName>>
-
-
-
These InterfaceOptions for customising TiddlyWiki are saved in your browser
-
-Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
-
-<<option txtUserName>>
-<<option chkSaveBackups>> SaveBackups
-<<option chkAutoSave>> AutoSave
-<<option chkRegExpSearch>> RegExpSearch
-<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
-<<option chkAnimate>> EnableAnimations
-
-----
-Also see AdvancedOptions
-
-
- -
-
-
A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far.  Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:
-<<<
-<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.
-
-Try modifying the time spent.  Suppose you've just spent one more hour and want to record it.  Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window.  Watch the time remaining go down to 1 hour.
-
-In reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours.  The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:
-<<<
-<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-You can adjust the current estimate in the same way as you adjusted the time spent.  Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".
-
-You can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases).  Click on the time remaining and add an hour by typing "+1".
-
-When the time remaining goes to zero, the task is considered complete:
-<<<
-<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you haven't already done so, try double-clicking the description.  Yes, it really does open up the editor and select just the text of the description.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 0.5>> Put tasksum on the ViewTemplate.
-<<<
-You can specify just the description of a task, and leave it unestimated.  Click the question mark to enter the estimate:
-<<<
-<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-As this task implies, you can enter two values in the popup when you click on any of the time cells.  Separate them with spaces and/or a comma.  Experiment:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-Finally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there.  That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.
-
-And setting up a taskadder is simplicity itself.  Just add {{{<<taskadder>>}}} to your tiddler.  You will see this:
-<<<
-<<taskadder>>
-<<<
-Just type a task description into the first field, and your initial estimate for how long it will take into the second field.  Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler.  Notice that you can just start typing a new task as soon as you're done entering the first one.
-
-You can have as many taskadders as you like in any tiddler.  The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse.  Try adding some tasks here and in the above adder:
-<<<
-<<taskadder>>
-<<<
-Notice that the one you just used takes focus when this tiddler is redisplayed.
-
-A taskadder by default adds tasks above itself.  You can make it add them below by adding a {{{below}}} argument to the macro call:
-<<<
-<<taskadder below>>
-<<<
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
In this tutorial, we've been looking mostly at individual tasks.  In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler.  In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.
-
-To get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them.  Here's an example:
-<<<
-<<tasksum start>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end>>
-<<<
-If you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.
-<<<
-<<tasksum start here>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<tasksum end>>
-<<<
-You can nest these things if you like, just be sure to match starts and ends:
-<<<
-<<tasksum start here>>
-* Time cell manipulation:<<tasksum start>>
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<tasksum end "Cell manipulation:">>
-<<br>>
-* Double-click handling:<<tasksum start>>
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end "Double-clicks:">>
-
-<<tasksum end>>
-<<<
-Finally, the simplest way to use tasksum is to add it to your view template.  See TaskSummaryViewTemplate for an example template.  Note that if no tasks are present between the start and end, nothing is displayed.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort.  However, there are two issues that may affect you.  (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)
-# The task macros don't play nicely with the default TiddlyWiki display of tags.  In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler.  However, this little box may interfere with the tables used by the task macros.  In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible.  In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box.  If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative.  Simply import it into your wiki and rename it to ViewTemplate.  This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>
-# Most view templates also add a minus sign ("-") before the "close" command.  TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out.  But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it.  It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed.  So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.
-
-----
-This ends the tutorial.  To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
-
-
-
PageTemplate
-|>|SiteTitle - SiteSubtitle|
-|>|MainMenu|
-|DefaultTiddlers<<br>><<br>><<br>>ViewTemplate<<br>><<br>>EditTemplate|SideBarOptions|
-|~|OptionsPanel|
-|~|SideBarTabs|
-|~|AdvancedOptions|
-|~|<<tiddler Configuration.SideBarTabs>>|
-
-''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
-
-ColorPalette
-
-SiteUrl
-
-
-
/***
-|Name|BetterTimelineMacro|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#BetterTimelineMacro|
-|Version|0.5 beta|
-|Requires|~TW2.x|
-!!!Description:
-A replacement for the core timeline macro that offers more features:
-*list tiddlers with only specfic tag
-*exclude tiddlers with a particular tag
-*limit entries to any number of days, for example one week
-*specify a start date for the timeline, only tiddlers after that date will be listed.
-
-!!!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!!!Syntax:
-{{{<<timeline better:true>>}}}
-''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
-
-additonal params:
-(use only the ones you want)
-{{{<<timeline better:true  onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30>>}}}
-
-''explanation of syntax:''
-onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
-excludeTag: tiddlers with this tag will not be listed.
-sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
-firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
-maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
-maxEntries: limit the total number of entries in the timeline.
-
-
-!!!History:
-*28-07-06: ver 0.5 beta, first release
-
-!!!Code
-***/
-//{{{
-// Return the tiddlers as a sorted array
-TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
-{
-          var results = [];
-          this.forEachTiddler(function(title,tiddler)
-          {
-          if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
-                        if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
-                                      results.push(tiddler);
-          });
-          if(field)
-                   results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
-          return results;
-}
-
-
-
-//this function by Udo
-function getParam(params, name, defaultValue)
-{
-          if (!params)
-          return defaultValue;
-          var p = params[0][name];
-          return p ? p[0] : defaultValue;
-}
-
-window.old_timeline_handler= config.macros.timeline.handler;
-config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
-{
-          var args = paramString.parseParams("list",null,true);
-          var betterMode = getParam(args, "better", "false");
-          if (betterMode == 'true')
-          {
-          var sortBy = getParam(args,"sortBy","modified");
-          var excludeTag = getParam(args,"excludeTag",undefined);
-          var includeTag = getParam(args,"onlyTag",undefined);
-          var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
-          var firstDayParam = getParam(args,"firstDay",undefined);
-          var firstDay = (firstDayParam!=undefined)? firstDayParam: "00010101";
-          var lastDay = "";
-          var field= sortBy;
-          var maxDaysParam = getParam(args,"maxDays",undefined);
-          var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
-          var maxEntries = getParam(args,"maxEntries",undefined);
-          var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
-          for(var t=tiddlers.length-1; t>=last; t--)
-                  {
-                  var tiddler = tiddlers[t];
-                  var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
-                  if ((theDay>=firstDay)&& (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
-                     {
-                     if(theDay != lastDay)
-                               {
-                               var theDateList = document.createElement("ul");
-                               place.appendChild(theDateList);
-                               createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
-                               lastDay = theDay;
-                               }
-                  var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
-                  theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
-                  }
-                  }
-          }
-
-          else
-              {
-              window.old_timeline_handler.apply(this,arguments);
-              }
-}
-//}}}
-
-
-
! Programming Languages
-* C
-** a C99 compatible compiler, some GCC extensions are used, most are optional.
-* C++
-** C++98
-** std::tr1 (for <std::tr1::memory>)
-* BOOST ~~(listed below are the DEBIAN package names)~~
-** libboost-dev (>=1.34.1-2)
-** libboost-program-options-dev (>=1.34.1-2)
-** libboost-program-options1.34.1 (>=1.34.1-2) ''NOTE: binary dependency''
-** libboost-regex-dev (>=1.34.1-2)
-** libboost-regex1.34.1 (>=1.34.1-2) ''binary..''
-** //usually, newer versions are OK//
-
-* bash
-  some build scripts (test.sh, ..) use bash specific extensions
-
-! Build Tools
-* autotools
-* SCons
-** //need either autotools or scons//
-** SCons (0.96), Python (2.4)
-** pkg-config
-* Doxygen
-* test.sh (included)
-
-! Extra tools
-* git
-* bouml
-
-! Libraries
-* boost (see above, version 1.35 works too)
-* NoBug
-* [[GAVL|http://gmerlin.sourceforge.net/gavl.html]] (1.0.0) 
-* for the GUI: gtkmm-2.4 gdl-1.0 libglibmm-2.4 cairomm-1.0 xv
-** libgtkmm-2.4-dev (>=2.8)
-** libcairomm-1.0-dev (>=0.6.0)
-** libgdl-lum-dev or libgdl-1-dev (>=2.27.1)
-*** libxml2-dev (>=2.6)
-** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16) and gthread-2.0 (>=2.12.4)
-** libxv-dev ~~(1.0.2 is known to work)~~
-* for rendering the icons: librsvg-2.0 
-** librsvg2-dev (>= 2.18)
-
-//usually, newer versions are OK//
-
-! Special Library requirements
-We use the GNOME Docking Library (GDL) within the Lumiera GTK GUI. As we actively participate in GDL development, we depend on a much more recent version, than most distos provide. We maintain a custom debian package (and source tree of GDL) which builds a {{{libgdl-lum.so}}}, in order to avoid side effects on other software. You may grab the tree including the Debian source package structure from [[our git|http://git.lumiera.org/gitweb?p=gdl-package;a=shortlog;h=refs/heads/debLumiera]] and create a binary Debian (or Ubuntu) package. For non-Debian systems, you may use the same build tree just in the standard way (configure, make, make install) to install into {{{/usr/local/lib/libgdl-lum.so}}}. Alternatively you may of course always just use a recent snapshot of GDL and build and install it as usual &mdash; but doing so may cause other software on your system to use this bleeding edge version of GDL too.
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #aaf
-PrimaryLight: #88f
-PrimaryMid: #00d
-PrimaryDark: #004
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
Overview
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
! Platform
-We work and test on PC hardware, 32 and 64 bit. It is intended that Lumiera runs on other platforms which run GNU/Linux.
-
-! Graphics
-There are no special requirements for the graphic system, using of OpenGL will likely be defined by the single plugins. Anyways it will be strictly optional.
-
-! Disks
-Video editing requires decent disk speed, it is suggested to use a fast/big array of disks configured as raid.
-
-! Special Hardware
-There are currently no plans to add support for propietary hardware accelerator boards. We don't have such hardware and we don't want to tie a free software program to closed hardware. This might change if the benefits are worth it, someone dontates such hardware to the developers, the specs of the hardware and the programming API are open.
-
-
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
-
/***
-|''Name:''|InlineJavascriptPlugin|
-|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
-|''Author:''|Eric Shulman - ELS Design Studios|
-|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
-|''~CoreVersion:''|2.0.10|
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Display script source in tiddler output''
-By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-><script show>
- alert('InlineJavascriptPlugin: this is a demonstration message');
-</script>
-dynamic output:
-><script show>
- return (new Date()).toString();
-</script>
-wikified dynamic output:
-><script show>
- return "link to current user: [["+config.options.txtUserName+"]]";
-</script>
-dynamic output using 'place' to get size information for current tiddler:
-><script show>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-creating an 'onclick' button/link that runs a script:
-><script label="click here" show>
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-loading a script from a source url:
->http://www.TiddlyTools.com/demo.js contains:
->>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
->>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
-><script src="demo.js" show>
- return "loading demo.js..."
-</script>
-><script label="click to execute demo() function" show>
- demo()
-</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
-''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
-''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]'' initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[4]) { // there is script code
- if (lookaheadMatch[3]) // show inline script code in tiddler output
- wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
- if (lookaheadMatch[2]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[4]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else { // run inline script code
- var code="function _out(place){"+lookaheadMatch[4]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
- }
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
''[[Lumiera|index.html]]''
-OperatingSystem
-[[Hardware]]
-[[Software]]
-BuildDependencies
-RuntimeDependencies
-StyleGuide
-[[Admin]]
-<<fullscreen>>
-
-
-
<!--{{{-->
-<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
-<!--}}}-->
-
-<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>My TiddlyWiki</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>
-
-
-
Lumiera is written for GNU/Linux. We try to make the best out of modern system programming techniques to reach the best possible performance. It is suggested to use a 64bit OS. Lumiera shall scale with the provided Hardware, the more RAM and the more/faster CPU's you have the better. Nevertheless lower end 32bit machines are supported too.
-
-Secondary targets will be other free operating systems which offer a decent Posix API. By coincidence it will be possible to port Lumiera to Mac OSX if anyone helps with that.
-
-Windows platforms are not on our target list, if someone wants to do port it, we will merge his efforts but unless that happens we will not care for windows compatibility.
-
-
-
-
 * OperatingSystem
- * [[Hardware]]
- * [[Software]]
- * BuildDependencies
- * RuntimeDependencies
- * StyleGuide
-
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-	<div class='headerShadow'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-	<div class='headerForeground'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-</div>
-<!-- horizontal MainMenu -->
-<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
-<!-- original MainMenu menu -->
-<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
-<div id='sidebar'>
-	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-	<div id='messageArea'></div>
-	<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
-
/***
-|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
-|''Version:''|1.0.6 (2006-11-07)|
-|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
-|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
-|''Licence:''|[[BSD open source license]]|
-|''TiddlyWiki:''|2.0|
-|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
-!Table of Content<html><a name="TOC"/></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
-!Description<html><a name="Description"/></html>
-With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
-Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts, use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
-
-''Syntax:'' 
-|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
-|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//.|
-|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
-|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
-|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Applications<html><a name="Applications"/></html>
-!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
-Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
-
-Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Citation Index<html><a name="Citation"/></html>
-Create a tiddler "Citations" that contains your "citations". 
-Wrap every citation with a part and a proper name. 
-
-''Example''
-{{{
-<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
-in //Proc. ICSM//, 1998.</part>
-
-<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
-Thesis, Uni Stuttgart, 2002.</part>
-
-<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
-in //Proc. ICSM//, 1999.</part>
-}}}
-
-You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
-You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
-{{{
-* Item 1
-* Item 2
-* Item 3
-}}}
-into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
-
-Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
-
-''Example''
-{{{
-|!Subject|!Items|
-|subject1|<<tiddler ./Cell1>>|
-|subject2|<<tiddler ./Cell2>>|
-
-<part Cell1 hidden>
-* Item 1
-* Item 2
-* Item 3
-</part>
-...
-}}}
-
-Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
-
-BTW: The same approach can be used to create bullet lists with items that contain more than one line.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating Tabs<html><a name="Tabs"/></html>
-The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
-
-With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
-
-''Example''
-The standard tabs at the sidebar are defined by the following eight tiddlers:
-* SideBarTabs
-* TabAll
-* TabMore
-* TabMoreMissing
-* TabMoreOrphans
-* TabMoreShadowed
-* TabTags
-* TabTimeline
-
-Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
-{{{
-<<tabs txtMainTab 
- Timeline Timeline SideBarTabs/Timeline 
- All 'All tiddlers' SideBarTabs/All 
- Tags 'All tags' SideBarTabs/Tags 
- More 'More lists' SideBarTabs/More>>
-<part Timeline hidden><<timeline>></part>
-<part All hidden><<list all>></part>
-<part Tags hidden><<allTags>></part>
-<part More hidden><<tabs txtMoreTab 
- Missing 'Missing tiddlers' SideBarTabs/Missing 
- Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
- Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
-<part Missing hidden><<list missing>></part>
-<part Orphans hidden><<list orphans>></part>
-<part Shadowed hidden><<list shadowed>></part>
-}}}
-
-Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
-
-E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
-{{{
-<<forEachTiddler 
- sortBy 'tiddler.modified' descending 
- write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
-}}}
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Using Sliders<html><a name="Sliders"/></html>
-Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
-
-''Example''
-In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
-{{{
-...
-<<slider chkAboutDetails About/Details details "Click here to see more details">>
-<part Details hidden>
-To give you a better overview ...
-</part>
-...
-}}}
-
-Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Revision history<html><a name="Revisions"/></html>
-* v1.0.6 (2006-11-07)
-** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
-* v1.0.5 (2006-03-02)
-** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
-* v1.0.4 (2006-02-28)
-** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
-* v1.0.3 (2006-02-26)
-** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
-* v1.0.2 (2006-02-05)
-** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
-* v1.0.1 (2006-01-27)
-** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
-** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
-* v1.0.0 (2006-01-25)
-** initial version
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Code<html><a name="Code"/></html>
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-//{{{
-//============================================================================
-// PartTiddlerPlugin
-
-// Ensure that the PartTiddler Plugin is only installed once.
-//
-if (!version.extensions.PartTiddlerPlugin) {
-
-
-
-version.extensions.PartTiddlerPlugin = {
- major: 1, minor: 0, revision: 6,
- date: new Date(2006, 10, 7), 
- type: 'plugin',
- source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
-};
-
-if (!window.abego) window.abego = {};
-if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
-
-//============================================================================
-// Common Helpers
-
-// Looks for the next newline, starting at the index-th char of text. 
-//
-// If there are only whitespaces between index and the newline 
-// the index behind the newline is returned, 
-// otherwise (or when no newline is found) index is returned.
-//
-var skipEmptyEndOfLine = function(text, index) {
- var re = /(\n|[^\s])/g;
- re.lastIndex = index;
- var result = re.exec(text);
- return (result && text.charAt(result.index) == '\n') 
- ? result.index+1
- : index;
-}
-
-
-//============================================================================
-// Constants
-
-var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
-var partEndTagREString = "<\\/part>";
-var partEndTagString = "</part>";
-
-//============================================================================
-// Plugin Specific Helpers
-
-// Parse the parameters inside a <part ...> tag and return the result.
-//
-// @return [may be null] {partName: ..., isHidden: ...}
-//
-var parseStartTagParams = function(paramText) {
- var params = paramText.readMacroParams();
- if (params.length == 0 || params[0].length == 0) return null;
- 
- var name = params[0];
- var paramsIndex = 1;
- var hidden = false;
- if (paramsIndex < params.length) {
- hidden = params[paramsIndex] == "hidden";
- paramsIndex++;
- }
- 
- return {
- partName: name, 
- isHidden: hidden
- };
-}
-
-// Returns the match to the next (end or start) part tag in the text, 
-// starting the search at startIndex.
-// 
-// When no such tag is found null is returned, otherwise a "Match" is returned:
-// [0]: full match
-// [1]: matched "end" tag (or null when no end tag match)
-// [2]: matched "start" tag (or null when no start tag match)
-// [3]: content of start tag (or null if no start tag match)
-//
-var findNextPartEndOrStartTagMatch = function(text, startIndex) {
- var re = new RegExp(partEndOrStartTagRE);
- re.lastIndex = startIndex;
- var match = re.exec(text);
- return match;
-}
-
-//============================================================================
-// Formatter
-
-// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
-//
-// @return true if a complete part section (including the end tag) could be processed, false otherwise.
-//
-var handlePartSection = function(w) {
- var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
- if (!tagMatch) return false;
- if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
-
- // Parse the start tag parameters
- var arguments = parseStartTagParams(tagMatch[3]);
- if (!arguments) return false;
- 
- // Continue processing
- var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
- var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
- if (endMatch && endMatch[1]) {
- if (!arguments.isHidden) {
- w.nextMatch = startTagEndIndex;
- w.subWikify(w.output,partEndTagREString);
- }
- w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
- 
- return true;
- }
- return false;
-}
-
-config.formatters.push( {
- name: "part",
- match: "<part\\s+[^>]+>",
- 
- handler: function(w) {
- if (!handlePartSection(w)) {
- w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
- }
- }
-} )
-
-//============================================================================
-// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
-// as tiddlers.
-
-var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
-
-// Return the match to the first <part ...> tag of the text that has the
-// requrest partName.
-//
-// @return [may be null]
-//
-var findPartStartTagByName = function(text, partName) {
- var i = 0;
- 
- while (true) {
- var tagMatch = findNextPartEndOrStartTagMatch(text, i);
- if (!tagMatch) return null;
-
- if (tagMatch[2]) {
- // Is start tag
- 
- // Check the name
- var arguments = parseStartTagParams(tagMatch[3]);
- if (arguments && arguments.partName == partName) {
- return tagMatch;
- }
- }
- i += tagMatch[0].length;
- }
-}
-
-// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
-// object, using fullName as the Tiddler's title. 
-//
-// All remaining properties of the new Tiddler (tags etc.) are inherited from 
-// the parentTiddler.
-// 
-// @return [may be null]
-//
-var getPart = function(parentTiddler, partName, fullName) {
- var text = parentTiddler.text;
- var startTag = findPartStartTagByName(text, partName);
- if (!startTag) return null;
- 
- var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
- var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
-
- if (indexOfEndTag >= 0) {
- var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
- var partTiddler = new Tiddler();
- partTiddler.set(
- fullName,
- partTiddlerText,
- parentTiddler.modifier,
- parentTiddler.modified,
- parentTiddler.tags,
- parentTiddler.created);
- partTiddler.abegoIsPartTiddler = true;
- return partTiddler;
- }
- 
- return null;
-}
-
-// Hijack the store.fetchTiddler to recognize the "part" addresses.
-//
-
-var oldFetchTiddler = store.fetchTiddler ;
-store.fetchTiddler = function(title) {
- var result = oldFetchTiddler.apply(this, arguments);
- if (!result && title) {
- var i = title.lastIndexOf('/');
- if (i > 0) {
- var parentName = title.substring(0, i);
- var partName = title.substring(i+1);
- var parent = (parentName == ".") 
- ? currentParent 
- : oldFetchTiddler.apply(this, [parentName]);
- if (parent) {
- return getPart(parent, partName, parent.title+"/"+partName);
- }
- }
- }
- return result; 
-};
-
-
-// The user must not edit a readOnly/partTiddler
-//
-
-config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
-
-Tiddler.prototype.isReadOnly = function() {
- // Tiddler.isReadOnly was introduced with TW 2.0.6.
- // For older version we explicitly check the global readOnly flag
- if (config.commands.editTiddler.oldIsReadOnlyFunction) {
- if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
- } else {
- if (readOnly) return true;
- }
-
- return this.abegoIsPartTiddler;
-}
-
-config.commands.editTiddler.handler = function(event,src,title)
-{
- var t = store.getTiddler(title);
- // Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
- // or the tiddler is not readOnly
- if(!t || !t.abegoIsPartTiddler)
- {
- clearMessage();
- story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
- story.focusTiddler(title,"text");
- return false;
- }
-}
-
-// To allow the "./partName" syntax in macros we need to hijack 
-// the invokeMacro to define the "currentParent" while it is running.
-// 
-var oldInvokeMacro = window.invokeMacro;
-function myInvokeMacro(place,macro,params,wikifier,tiddler) {
- var oldCurrentParent = currentParent;
- if (tiddler) currentParent = tiddler;
- try {
- oldInvokeMacro.apply(this, arguments);
- } finally {
- currentParent = oldCurrentParent;
- }
-}
-window.invokeMacro = myInvokeMacro;
-
-// Scroll the anchor anchorName in the viewer of the given tiddler visible.
-// When no tiddler is defined use the tiddler of the target given event is used.
-window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
- var tiddlerElem = null;
- if (tiddler) {
- tiddlerElem = document.getElementById(story.idPrefix + tiddler);
- }
- if (!tiddlerElem && evt) {
- var target = resolveTarget(evt);
- tiddlerElem = story.findContainingTiddler(target);
- }
- if (!tiddlerElem) return;
-
- var children = tiddlerElem.getElementsByTagName("a");
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var name = child.getAttribute("name");
- if (name == anchorName) {
- var y = findPosY(child);
- window.scrollTo(0,y);
- return;
- }
- }
-}
-
-} // of "install only once"
-//}}}
-
-/***
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Licence and Copyright
-Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-Neither the name of abego Software nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
//last update: RSSReaderPlugin v 1.1.1//
-
-!Description
-This plugin provides a RSSReader for TiddlyWiki
-* It accesses asynchronously an RSSFeed
-*Depending on the chanel item format, each item could be written as :
-**simple text wikified
-**html
-
-!Usage
-{{{
-<<rssReader noDesc|asHtml|asText rssUrl ['filtering string']>>
-	noDesc: only title of item is printed
-
-	asHtml: if you know that description contain html (links, img ...), 
-		the text is enclosed with <html> </html> tags
-
- 	asText: if the description should not be interpreted as html the 
-		description is wikified
-
-	rssUrl: the rssFeed url that could be accessed. 
-	
-	'filtering string': if present, the rssfeed item title must contained 
-		this string to be displayed. 
-		If 'filering string' contained space characters only, the tiddler 
-		title is used for filtering.
-
-}}}
-
-For security reasons, if the TiddlyWiki is accessed from http, a ProxyService should be used to access an rssFeed from an other site.
-
-!examples
-| !reader | !RSSFeed type | !working from |
-| BidiXTWRSS | Description asHtml | file: or tiddlywiki.bidix.info |
-| [[Le Monde]] | Description asText | file: or tiddlywiki.bidix.info using proxy |
-| YahooNewsSport | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| TiddlyWikiRSS | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| [[Libération]] | noDesc | file: or tiddlywiki.bidix.info using proxy |
-| [[TestComment]] | asText and filters | file: or tiddlywiki.bidix.info using proxy |
-see : <<tag RSSFeed>> for the full list.
-
-!Revision history
-* V1.1.0 (2207/04/13)
-**No more import functions
-* V1.0.0 (2006/11/11)
-**refactoring using core loadRemoteFile function
-**import using new tiddlywiki:tiddler element
-**import and presentation preserved without EricShulman's NestedSliderPlugin
-**better display of items 
-* v0.3.0 (24/08/2006)
-** Filter on RSS item title
-** Place to display redefined for asynchronous processing
-* v0.2.2 (22/08/2006)
-**Haloscan feed has no pubDate.
-* v0.2.1 (08/05/2006)
-* v0.2.0 (01/05/2006)
-**Small adapations for del.icio.us feed
-* v0.1.1 (28/04/2006)
-**Bug : Channel without title 
-* v0.1.0 (24/04/2006)
-** initial release
-
-
-
-
-
-
Dependencies and Style
-
-
-
Compatibility
-
-
-
Lumiera expects a 'standard' desktop installation running a Xserver.
-
-
-
-
/***
-
-''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
-
-|Name|SplashScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#SplashScreenPlugin|
-|Version|0.21 |
-|Requires|~TW2.08+|
-!Description:
-Provides a simple splash screen that is visible while the TW is loading.
-
-!Installation
-Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
-
-!Customizing
-Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
-
-!History
-* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
-* 26-06-06 : version 0.2, first release
-
-!Code
-***/
-//{{{
-var old_lewcid_splash_restart=restart;
-
-restart = function()
-{   if (document.getElementById("SplashScreen"))
-        document.getElementById("SplashScreen").style.display = "none";
-      if (document.getElementById("contentWrapper"))
-        document.getElementById("contentWrapper").style.display = "block";
-    
-    old_lewcid_splash_restart();
-   
-    if (splashScreenInstall)
-       {if(config.options.chkAutoSave)
-			{saveChanges();}
-        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
-        }
-}
-
-
-var oldText = store.getTiddlerText("MarkupPreHead");
-if (oldText.indexOf("SplashScreen")==-1)
-   {var siteTitle = store.getTiddlerText("SiteTitle");
-   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
-   if (! store.tiddlerExists("MarkupPreHead"))
-       {var myTiddler = store.createTiddler("MarkupPreHead");}
-   else
-      {var myTiddler = store.getTiddler("MarkupPreHead");}
-      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
-      store.setDirty(true);
-      var splashScreenInstall = true;
-}
-//}}}
-
-
-
! Source code formatting
-We decided to use the 'gnu-style' for indenting (using spaces and never tabs!):
- http://www.gnu.org/prep/standards/html_node/Formatting.html#Formatting
-
-It is reasonable to be relaxed about soem formatting rules:
- * line length might be longer when required
- * inter expession spacing can be changed to the actual needed ex: (2*x + 2)
-
-Things we are pedantic about:
- * use never ever tab characters in C/C++ sources
- * be consistent
- * source files should end with a newline
- * no trailing or bogus whitespaces
-
-! Coding Practices
-
-! Writing Tests
-
-! Contibuting
-
-
-
-
/*{{{*/
-/* a contrasting background so I can see where one tiddler ends and the other begins */
-body {
-	background: [[ColorPalette::TertiaryLight]];
-}
-
-/* sexy colours and font for the header */
-.headerForeground {
-	color: [[ColorPalette::PrimaryPale]];
-}
-.headerShadow, .headerShadow a {
-	color: [[ColorPalette::PrimaryMid]];
-}
-.headerForeground, .headerShadow {
-	padding: 1em 1em 0;
-	font-family: 'Trebuchet MS' sans-serif;
-	font-weight:bold;
-}
-.headerForeground .siteSubtitle {
-	color: [[ColorPalette::PrimaryLight]];
-}
-.headerShadow .siteSubtitle {
-	color: [[ColorPalette::PrimaryMid]];
-}
-
-/* make shadow go and down right instead of up and left */
-.headerShadow {
-	left: 2px;
-	top: 3px;
-}
-
-/* prefer monospace for editing */
-.editor textarea {
-	font-family: 'Consolas' monospace;
-}
-
-/* sexy tiddler titles */
-.title {
-	font-size: 250%;
-	color: [[ColorPalette::PrimaryLight]];
-	font-family: 'Trebuchet MS' sans-serif;
-}
-
-/* more subtle tiddler subtitle */
-.subtitle {
-	padding:0px;
-	margin:0px;
-	padding-left:0.5em;
-	font-size: 90%;
-	color: [[ColorPalette::TertiaryMid]];
-}
-.subtitle .tiddlyLink {
-	color: [[ColorPalette::TertiaryMid]];
-}
-
-/* a little bit of extra whitespace */
-.viewer {
-	padding-bottom:3px;
-}
-
-/* don't want any background color for headings */
-h1,h2,h3,h4,h5,h6 {
-	background: [[ColorPalette::Background]];
-	color: [[ColorPalette::Foreground]];
-}
-
-/* give tiddlers 3d style border and explicit background */
-.tiddler {
-	background: [[ColorPalette::Background]];
-	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
-	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
-	margin-bottom: 1em;
-	padding-bottom: 2em;
-}
-
-/* make options slider look nicer */
-#sidebarOptions .sliderPanel {
-	border:solid 1px [[ColorPalette::PrimaryLight]];
-}
-
-
-/* the borders look wrong with the body background */
-#sidebar .button {
-	border-style: none;
-}
-
-/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
-.tagglyTagged li.listTitle {
-	display:none
-}
-.tagglyTagged li {
-	display: inline; font-size:90%;
-}
-.tagglyTagged ul {
-	margin:0px; padding:0px;
-}
-
-/* this means you can put line breaks in SidebarOptions for readability */
-#sidebarOptions br {
-	display:none;
-}
-/* undo the above in OptionsPanel */
-#sidebarOptions .sliderPanel br {
-	display:inline;
-}
-
-/* horizontal main menu stuff */
-#displayArea {
-	margin: 1em 15.7em 0em 1em; /* use the freed up space */
-}
-#topMenu br {
-	display: none;
-}
-#topMenu {
-	background: [[ColorPalette::PrimaryMid]];
-	color:[[ColorPalette::PrimaryPale]];
-}
-#topMenu {
-	padding:2px;
-}
-#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
-	margin-left: 0.5em;
-	margin-right: 0.5em;
-	padding-left: 3px;
-	padding-right: 3px;
-	color: [[ColorPalette::PrimaryPale]];
-	font-size: 115%;
-}
-#topMenu .button:hover, #topMenu .tiddlyLink:hover {
-	background: [[ColorPalette::PrimaryDark]];
-}
-
-/* make it print a little cleaner */
-@media print {
-	#topMenu {
-		display: none ! important;
-	}
-	/* not sure if we need all the importants */
-	.tiddler {
-		border-style: none ! important;
-		margin:0px ! important;
-		padding:0px ! important;
-		padding-bottom:2em ! important;
-	}
-	.tagglyTagging .button, .tagglyTagging .hidebutton {
-		display: none ! important;
-	}
-	.headerShadow {
-		visibility: hidden ! important;
-	}
-	.tagglyTagged .quickopentag, .tagged .quickopentag {
-		border-style: none ! important;
-	}
-	.quickopentag a.button, .miniTag {
-		display: none ! important;
-	}
-}
-/*}}}*/
-
-
-
-
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
-
- - - - - - - - - - diff --git a/wiki/index.html b/wiki/index.html index 516cf549d..874368d26 100644 --- a/wiki/index.html +++ b/wiki/index.html @@ -498,127 +498,6 @@ Also see AdvancedOptions
-
-
A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far.  Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:
-<<<
-<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.
-
-Try modifying the time spent.  Suppose you've just spent one more hour and want to record it.  Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window.  Watch the time remaining go down to 1 hour.
-
-In reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours.  The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:
-<<<
-<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-You can adjust the current estimate in the same way as you adjusted the time spent.  Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".
-
-You can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases).  Click on the time remaining and add an hour by typing "+1".
-
-When the time remaining goes to zero, the task is considered complete:
-<<<
-<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you haven't already done so, try double-clicking the description.  Yes, it really does open up the editor and select just the text of the description.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 0.5>> Put tasksum on the ViewTemplate.
-<<<
-You can specify just the description of a task, and leave it unestimated.  Click the question mark to enter the estimate:
-<<<
-<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-As this task implies, you can enter two values in the popup when you click on any of the time cells.  Separate them with spaces and/or a comma.  Experiment:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-Finally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there.  That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.
-
-And setting up a taskadder is simplicity itself.  Just add {{{<<taskadder>>}}} to your tiddler.  You will see this:
-<<<
-<<taskadder>>
-<<<
-Just type a task description into the first field, and your initial estimate for how long it will take into the second field.  Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler.  Notice that you can just start typing a new task as soon as you're done entering the first one.
-
-You can have as many taskadders as you like in any tiddler.  The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse.  Try adding some tasks here and in the above adder:
-<<<
-<<taskadder>>
-<<<
-Notice that the one you just used takes focus when this tiddler is redisplayed.
-
-A taskadder by default adds tasks above itself.  You can make it add them below by adding a {{{below}}} argument to the macro call:
-<<<
-<<taskadder below>>
-<<<
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
In this tutorial, we've been looking mostly at individual tasks.  In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler.  In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.
-
-To get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them.  Here's an example:
-<<<
-<<tasksum start>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end>>
-<<<
-If you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.
-<<<
-<<tasksum start here>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<tasksum end>>
-<<<
-You can nest these things if you like, just be sure to match starts and ends:
-<<<
-<<tasksum start here>>
-* Time cell manipulation:<<tasksum start>>
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<tasksum end "Cell manipulation:">>
-<<br>>
-* Double-click handling:<<tasksum start>>
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end "Double-clicks:">>
-
-<<tasksum end>>
-<<<
-Finally, the simplest way to use tasksum is to add it to your view template.  See TaskSummaryViewTemplate for an example template.  Note that if no tasks are present between the start and end, nothing is displayed.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort.  However, there are two issues that may affect you.  (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)
-# The task macros don't play nicely with the default TiddlyWiki display of tags.  In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler.  However, this little box may interfere with the tables used by the task macros.  In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible.  In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box.  If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative.  Simply import it into your wiki and rename it to ViewTemplate.  This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>
-# Most view templates also add a minus sign ("-") before the "close" command.  TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out.  But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it.  It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed.  So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.
-
-----
-This ends the tutorial.  To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
-
PageTemplate
 |>|SiteTitle - SiteSubtitle|
@@ -635,27 +514,6 @@ ColorPalette
 
 SiteUrl
-
-
This page intends to capture the lage picture regarding Lumiera's Architecture. This may be a moving target...
-
-[img[Overview of Lumiera Architecture|draw/Lumi.Architecture-1.png][http://www.lumiera.org/gitweb?p=LUMIERA;f=doc/devel/draw/Lumi.Architecture-1.svg;hb=HEAD]]
-
-//this drawing is maintained as SVG in GIT//
-~~(click on the above image...)~~
-
-!Description
-* the Application has three Layers: [[Backend|backend.html]], [[Proc-Layer|renderengine.html]] and GUI
-* the Application shall be completely functional without GUI ("headless", script-driven)
-* all IO, media data fetching, processing and bookkeeping falls within the realm of the Backend
-* all media object manipulation, deciding and configuration is the Proc Layer's job
-* extensible by plugins on all levels, highly configurable, but not totally componentized (micro kernel) architecture
-* strong separation between high-level and low-level areas of the Application
-* the user/GUI manipulates a [[high-level model|renderengine.html#HighLevelModel]] whereas rendering is based on a corresponding [[low-level model|renderengine.html#OverviewRenderEngine]]
-* stored Session (state) is comprised of high-level model, a collection of [[Assets|renderengine.html#Asset]] and accompaning configuration
-* (possibly) several storage backends, abstracted out by a common interface
-
-
-
* autotools 1.10 (as of 5/2008) &dash; Question: other versions usable too?
 * the usual procedure, in short
@@ -828,156 +686,6 @@ for __Running__
LumieraWiki
 ShortCuts
-
-
* There is a [[Manifest]] explaining the vision of the Lumiera project
-* The foundation how we work together is defined in LumieraDesignProcess
-* There is a description how the git repository is set up in RepositorySetup
-* we decided to write code in GNU style, with no tabs (use spaces)
-
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
to make the admin/git_hooks/post-commit working add following to your .gitconfig:
-{{{
-[alias]
-        sign = tag -s -f -m 'automatic generated on last commit'
-        publish = push --all public
-}}}
-
-these two commands are used by 'admin/git-hooks/post-commit'
-
-'git sign' creates a gpg-signed tag after each commit, named '$~BRANCH_signature' overriding an older tag of the same name. Thus the head revision is always gpg signed (it is not perfect, in some cases like some merges and other things the signature can become unsynced and needs to be fixed manually).
-
-'git publish' just sends the commit to some repository which has to be registered with 'git remote add public ...', in case you are working offline this will stuck and timeout, you may break it with ctrl-c, someone may fix it.
-
-
-
some ''interesting Branches''
-
-|![[pipapo.org|PipapoOrg]]                |!''mirrored''              |!|!description                          |
-| ct#master                               | ichthyo#master            | |Lumiera main development line         |
-| ichthyo#scons                           |                           | |[[SCons]]-based build system, improvements|
-
-
-
-
-
I use some GitAliases to make signing and publishing easier.
-
-the '.git' dir itself is not versioned/distributed since it usually contains site-specific things. Despite this we might want to distribute some maintenance scripts and hooks so I put the default hooks into admin/git_hooks/ and users can symlink from .git/hooks them when needed.
-
-For now I hope this approach suffices, maybe we need admin/git_hooks/$HOOKNAME.$USER at some point when it turns out that people want personal hooks.
-
-&rarr; see [[Interesting Branches|GitBranches]]
-
-
-
-
This part should explain what you have to do in order to download the sources of Lumiera from pipapo.org. It treats as well how to deliver improvements to cin3 using git. Git is a tool similar to cvs or svn.
-
-The cin3 sources are stored in a git repository. Maybe you have never heard of git before, but i can allay your fears: using git isn't that complicated. To download cin3 the very first time you simply have to clone a git-repo from pipapo.org. To do so just type the following in a shell:
-!!!Code
-//{{{
-git clone git://git.pipapo.org/lumiera/ct YOURCOPY'sNAME
-//}}}
-
-This will create a directory named YOURCOPY'sNAME which contains the whole data of the lumiera/ct branch of Lumiera. After having downloaded a cin3-git-repository you can update it by simply typing:
-!!!Code
-//{{{
-cd /your/path/YOURCOPY'sNAME
-git pull
-//}}}
-
-If everything went well your copy is now up to date. Pulling is faster than cloning it again and causes less traffic at the server. 
-
-!!!Tips: 
-* NEVER delete a git repository without a permission of a pro.
-* Check the url.
-* Install git before calling git
-
-Now you could compile the source code or improve Lumiera or the documentation. After having done so (let's say you have written a patch) you can deliver your changes by:
-* at first setting your name and email
-* committing your changes
-* pushing your local git-repo to a public server
-
-!!!Code
-//{{{
-git config --global user.name "YOUR REALNAME"
-git config --global user.email ~YOUR-E@MAIL.ADRESS
-git commit -m 'YOUR DESCRIPTION' -- FILE/TO/COMMIT
-git push git://git.pipapo.org/lumiera/mob
-//}}}
-
-lumiera/mob is an anonymous account at pipapo.org where everyone can commit changes. 
-
We keep a protocol or short summary of each important discussion. The summaries of the monthly developer meetings are posted to the Mailinglist and can be found on pipapo.org too
 
@@ -2096,171 +1804,6 @@ __cehteh__ and __ichthyo__
 [2008-06-05 19:58:10] <ichthyo> :-o
 }}}
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
/***
 |''Name:''|InlineJavascriptPlugin|
@@ -2407,44 +1950,7 @@ config.formatters.push( {
 } )
 //}}}
-
-
! Lumiera design process
-A lightweight formalized process how people can add proposals for the Lumiera development.
-
-
-!! Description
-Use the Wiki at http://www.pipapo.org/pipawiki/Lumiera/DesignProcess to make it easy to add proposals in a well defined manner.
-
-I'd like to introduce a slightly formalized process for the ongoing Lumiera planning:
-* Every proposal is instantiated as 'Idea', the author gives other people the opportunity to review and comment on it with extreme prejudice, while still working out details.
-* When the the 'Idea' in a proper form and worked out in most details it becomes a 'Draft'. This 'Draft' need to be carefully reviewed, commented, perhaps corrected and rated by the other Developers.
-* At some point we may decide that a 'Draft' becomes a 'Final' (I leave it open how this decision shall be done for now). 'Final' Documents will be imported into the repository (this wiki, you are reading such a Document right now!).
-
-* Sometimes proposals will become dropped for some reason, this is indicated by changing their state to 'Dropped', they still stay in the system for further reference.
-
-!!! Pros
-* simple
-* flexible
-* no much rules
-* persistent and at Final stage well documented process
-
-!!! Cons
-* could be abused/vandalized (but wiki can use ACL's)
-* depends on my server, this might be unfavorable or unreliable, ymmv.
-* will only work if all or almost all involved people agree on this process
-
-!!! Alternatives
-We could use some forum, Trac, Mailinglist or whatever instead.
-
-Just for Design documentation I would give [[Bouml|http://bouml.free.fr/]] a try. For myself, I am not very fond of UML Design tools, while Bouml looks quite promising and we could maintain the UML model in git repositories which would be more favorable than this centralized wiki. The backside is that this needs even more agreement between the developers, everyone has to install and use Bouml (and learn its usage) and design is constrained by a external tool.
-
-This distributed wiki might be used instead the pipapo.org wiki, investigate that for future.
-
-!! Rationale
-Wiki works. It is simple to use and just flexible enough to handle the task. I don't go to install any other software for such tasks on my server. While the design progresses I'd propose to move our work into git repositories and eventually phase this wiki pages out anyways. I'd rather like to start out distributed/git right away .. but git gives us only a fine storage layer, for a design process we need some good presentation layer (later when using git and starting the implementation everyones favorite editor serves for that) I have no better ideas yet to solve the presentation problem other than using this wiki (or maybe Bouml).
-
-
-
+
[<img[draw/LumiLogo.png]]
 
 
@@ -2454,19 +1960,9 @@ Wiki works. It is simple to use and just flexible enough to handle the task. I d
 
 
 This is the entry point to several [[TiddlyWiki]]-Pages containing the developer and design documentation. The documentation is split in several parts corresponding to the parts of the application. Those TiddlyWiki pages are self modifying HTML pages; we include them into the GIT source tree. For the future we plan to move the contents of these developer doc wikis into a GIT backed uWiki (still under development as of 10/2008)
-* we maintain (semi-) final design docs in DesignDocumentation
 * Things get often worked out on IRC, see IRC-Transcripts for protocols, transcripts and decisions made there
 
 ----
-!Architecture and Subsystems
-&rarr; see the ArchitectureOverview
-
-* Cehteh works on the data backend, see [[this page|backend.html]]
-* Ichthyo focuses mainly on Edit operations and Builder, [[see this separate page|renderengine.html]]
-* Joel builds the Lumiera GUI based on GTK
-* Gmerlin is in charge of [[GAVL|http://gmerlin.sourceforge.net/gavl.html]] for processing of video data
-* Some tools which don't fit somewhere else and are used everywhere are put into a [[Support Library|support_library.html]]
-
 !Coding &mdash; Building &mdash; Testing
 how to organize several aspects of the practical coding...
 * what to do in BOUML?                          &rarr; [[more|whatInBOUML]]
@@ -2478,16 +1974,11 @@ how to organize several aspects of the practical coding...
 * we embrace __Test Driven Development__.       &rarr; Description of [[Test System|TestSh]] and TestSuite
 
-
-
GettingStarted
-[[LumieraWiki]]
-[[Compatibility|compatibility.html]]
-[[ToDo|todo.html]]
-[[Backend|backend.html]]
-[[Proc-Layer|renderengine.html]]
-[[Support-Library|support_library.html]]
+
+
[[Proc-Layer|renderengine.html]]
+[[Lumiera.org website|http://Lumiera.org]]
 [[Admin]]
-<<fullscreen>>
+
! Manifest
@@ -3110,378 +2601,6 @@ DAMAGE.
 <html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
 ***/
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
//last update: RSSReaderPlugin v 1.1.1//
-
-!Description
-This plugin provides a RSSReader for TiddlyWiki
-* It accesses asynchronously an RSSFeed
-*Depending on the chanel item format, each item could be written as :
-**simple text wikified
-**html
-
-!Usage
-{{{
-<<rssReader noDesc|asHtml|asText rssUrl ['filtering string']>>
-	noDesc: only title of item is printed
-
-	asHtml: if you know that description contain html (links, img ...), 
-		the text is enclosed with <html> </html> tags
-
- 	asText: if the description should not be interpreted as html the 
-		description is wikified
-
-	rssUrl: the rssFeed url that could be accessed. 
-	
-	'filtering string': if present, the rssfeed item title must contained 
-		this string to be displayed. 
-		If 'filering string' contained space characters only, the tiddler 
-		title is used for filtering.
-
-}}}
-
-For security reasons, if the TiddlyWiki is accessed from http, a ProxyService should be used to access an rssFeed from an other site.
-
-!examples
-| !reader | !RSSFeed type | !working from |
-| BidiXTWRSS | Description asHtml | file: or tiddlywiki.bidix.info |
-| [[Le Monde]] | Description asText | file: or tiddlywiki.bidix.info using proxy |
-| YahooNewsSport | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| TiddlyWikiRSS | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| [[Libération]] | noDesc | file: or tiddlywiki.bidix.info using proxy |
-| [[TestComment]] | asText and filters | file: or tiddlywiki.bidix.info using proxy |
-see : <<tag RSSFeed>> for the full list.
-
-!Revision history
-* V1.1.0 (2207/04/13)
-**No more import functions
-* V1.0.0 (2006/11/11)
-**refactoring using core loadRemoteFile function
-**import using new tiddlywiki:tiddler element
-**import and presentation preserved without EricShulman's NestedSliderPlugin
-**better display of items 
-* v0.3.0 (24/08/2006)
-** Filter on RSS item title
-** Place to display redefined for asynchronous processing
-* v0.2.2 (22/08/2006)
-**Haloscan feed has no pubDate.
-* v0.2.1 (08/05/2006)
-* v0.2.0 (01/05/2006)
-**Small adapations for del.icio.us feed
-* v0.1.1 (28/04/2006)
-**Bug : Channel without title 
-* v0.1.0 (24/04/2006)
-** initial release
-
-
-
-
-
-
||'''State'''||''Final''||
-||'''Date'''||[[Date(2007-06-09T00:48:02Z)]]||
-||'''Proposed by'''||["ct"]||
-
-! Repository Setup
-Here we describe the Directory hierarchy and how the git repository are set up.
-
-!! Description
-Use "./admin/treeinfo.sh" to produce a annotated directory tree like this:
-{{{
-.                                       : The root dir for the Lumiera project
-./admin                                 : administrative scripts
-./admin/git_hooks                       : git hook scripts
-./build                                 : build dir
-./doc                                   : documentation
-./doc/devel                             : developer documentation, extra sources, doxygen
-./doc/devel/uml                         : Bouml generated HTML doc
-./doc/user                              : user documentation in texinfo
-./oldsrc                                : Cinelerra2 sources, added per case when needed
-./src                                   : every components source in a own subdir here
-./tests                                 : test suite
-./tests/bugs                            : tests against reported bugs
-./uml                                   : uml models, created with bouml
-./uml/lumiera                           : Lumiera UML model
-./wiki                                  : tiddlywiki for semi-persistent documentation
-}}}
-
-!! New Directories:
-
-When you need to add a new mariginally important directory please provides a file named DIR_INFO within this directory. It's first line should note the purpose of the directory in a few words (less than 40 characters). The following lines are free form description about the details. 
-
-!! Submodules:
-
-We want to use the new GIT feature of "Superprojects and Submodules" when it is ready for general use.
-Then we will transform several subtrees into separate GIT repos which will be linked to from the main
-Project (then called the "Superproject") as submodules.
-
-!!! Pros
-* because its a text-like structure, it is partially self-documenting
-* GIT is flexible and with the planned submodules it can be separated in chunks of manageable size if necessary
-
-!!! Cons
-* can get large and confusing
-* has no real "portal" or entrance point for people wanting to join
-
-!!Rationale
-Every important document, draft, text and code (including) prototypes should be checked into
-one SCM (or a set of related SCMs). This repo should be "almost everything" you need for the
-project. Because we try to use a distributed development model, every dev can/should have 
-his own copy and fed his changes back.
-
-This ''Repository approach'' avoids the problems of a central infrastructure and helps cut down
-project management time. Basically, every dev is responsible himself for getting every important
-piece of information added into "the general view of matters" in a consistent way.
-
-
-! Conclusion
-## When approbate (this proposal becomes a Final) write some conclusions about its process:
-
-
-
-! Comments
-Basically the structure is just fine.
-* maybe add a "pastebin" somewhere in the dev-documentation area?
-* i would add the source tree roots at level 2, so we can have several submodules here:
-** oldsrc
-** cin3
-** prototype
- -- ["Ichthyostega"] [[DateTime(2007-06-16T23:10:01Z)]]
-
-Draft now.
-
-Yes I left source dirs out but this sounds fine, note that with git, there is no problem to reorganize the repo (in contrast to CVS) later. We can fix things afterward when we find better ways.
- -- ["ct"] [[DateTime(2007-06-17T17:36:46Z)]]
-
-Whats prototype for? won't that be better a branch?
- -- ["ct"] [[DateTime(2007-06-17T22:04:39Z)]]
-
-I just wanted to show there could be additional things beside the main tree (later to be separate submodules). The example was meant as a classical
-throwaway prototype. But I agree, in our case we just start hacking at the new tree and make feature/tryout/prototype branches...
-
-The point I wanted to make is: every directory 2 levels deep in the source tree, e.g. /src/cinelerra3 or /src/oldsrource should be a completely 
-self-contained tree which can be built without needing anything of the rest of the repo. Thats an prerequisite for moving to Submodules IMHO.
-But you seem rather to put the sourcetree-roots 1 level deep. As we have just two trees at the moment (and can easily reorganize), I have no
-objections against this. The only point I really care is to try to keep the source tree self-contained without any dependencies to the rest
-of the "design GIT" (because of this Superprojects-Submodules thing...)
- -- ["Ichthyostega"] [[DateTime(2007-06-17T23:45:06Z)]]
-
-we could make the trees deeper than one level, I didn't intended 1-level depth. but also be careful with that not to make it too complex. While I am not sure if we want a complete oldsrc, that just adds weight and confusion for now (lets see). Neither I am fully decided about the hierarchy in /src (want /libs /plugins or /src/libs /src/plugins or /src/render/plugins? name it rather 'effects' than 'plugins'?). While I am quite sure that I want to separate /oldssrc and /src quite much (in /src should only be new stuff or stuff which is carefully reviewed, with know license and author).
- -- ["ct"] [[DateTime(2007-06-18T08:38:43Z)]]
-
-I made this proposal 'final' now further details are likely better worked out in the git repository (and we already started to define things there) see ./admin/treeinfo.sh 
- -- ["ct"] [[DateTime(2007-06-27T16:01:52Z)]]
-
-
[SCons|http://www.scons.org/] is an //alternate build system// written in Python and using specific python-scripts for defining the buildprocess. These build scripts, called {{{SConstruct}}} and {{{SConsscript}}} are indeed //definitions//, not scripts for //doing// the build. If you are new to SCons (and familiar with make), you should really read the [Introduction of the users guide|http://www.scons.org/doc/0.97/HTML/scons-user/book1.html], because SCons is quite a different beast then make and the autotools.
 
@@ -3580,10 +2699,6 @@ if (oldText.indexOf("SplashScreen")==-1)
 }
 //}}}
-
-
* add a ''~DIR_INFO'' file to each marginally important directory. The first line should give a short abstract about this dir (40 characters, not more), following lines can give more precise information. There is a 'admin/treeinfo.sh' script which generates a texual overview of the directory tree.
-
-
/*{{{*/
 /* a contrasting background so I can see where one tiddler ends and the other begins */
@@ -3748,902 +2863,6 @@ h1,h2,h3,h4,h5,h6 {
 
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
! The Test Script
 To drive the various tests, we use the script {{{tests/test.sh}}}. All tests are run under valgrind control if available unless {{{VALGRINDFLAGS=DISABLE}}} is defined. 
@@ -4766,133 +2985,6 @@ to help with automating the build, ichthyo would appreciate to have the followin
 //as of 18.8.2007, ichthyo has implemented this scheme for the SCons build//
 
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
The Name of the Software driving this Wiki. Is is written completely in ~JavaScript and contained in one single HTML page.
 Thus no server and no network connection is needed. Simply open the file in your browser and save changes locally. As the wiki HTML is located in the Lumiera source tree, all changes will be managed and distributed via [[GIT|GitNotes]]. While doing so, you sometimes will have to merge conflicing changes manually in the HTML source. There is a 'empty.html' in the same folder serving as template for generating new wikis. Please refrain from editing it.
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index b48372abb..3ce8f6dc0 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -747,10 +747,10 @@ 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. 
+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 information to the inner parts of the engine, while at the same time isolating and decoupling them from environmental details. 
 
 We can distinguish several different Kinds of Assets, each one with specific properties. While all these Kinds of Assets implement the basic Asset interface, they in turn are the __key abstractions__ of the asset management view. Mostly, their interfaces will be used directly, because they are quite different in behaviour. Thus it is common to see asset related operations being templated on the Asset Kind. 
 &rarr; see also [[Creating and registering Assets|AssetCreation]]
@@ -759,7 +759,7 @@ We can distinguish several different Kinds of Assets, each one with specific pro
 !Media Asset
 Some piece of Media Data accessible at some external Location and able to be processed by Lumiera. A Media File on Harddisk can be considered as the most basic form of Media Asset, with some important derived flavours, like a Placeholder for a currently unavailable Source, or Media available in different Resolutions or Formats.
 * __outward interface operations__ include querying properties, creating an Clip MObject, controlling processing policy (low res proxy placeholders, interlacing and other generic pre- and postprocessing)
-* __inward interface operations__ include querying filename, codec, offset and any other informations necessary for creating a source render node, getting additional processing policy decisions (handling of interlacing, aspect ratio).
+* __inward interface operations__ include querying filename, codec, offset and any other information necessary for creating a source render node, getting additional processing policy decisions (handling of interlacing, aspect ratio).
 &rarr; MediaAsset
 
 !Processing Asset
@@ -1077,6 +1077,103 @@ Please note the shortcomings and logical contradictions in the solution currentl
 * The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
 
+
+
All rendering, transformations and output of media data requires using ''data buffers'' -- but the actual layout and handling of these buffers is closely related to the actual implementation of these operations. As we're relying heavily on external libraries and plug-ins for performing these operations, there is no hope getting away with just one single {{{Buffer}}} data type definition. Thus, we need to confine ourselves to a common denominator of basic operations regarding data buffers and abstract the access to these operations through a BufferProvider entity. Beyond these basic operations, mostly we just need to assure that //a buffer exists as an distinguishable element// -- which in practice boils down to pushing around {{{void*}}} variables. 
+
+Obviously, overloading a pointer with semantic meaning isn't exactly a brilliant idea -- and the usual answer is to embed this pointer into a smart handle, which also yields the nice side-effect of explaining this design to the reader. Thus a buffer handle
+* can only be obtained from a BufferProvider
+* can be used to identify a buffer
+* can be dereferenced
+* can be copied 
+
+!design quest: buffer type information
+To perform anything useful with such a buffer handle, the client code needs some additional information, which can be generalised into a //type information:// Either, the client needs to know the size and kind of data to expect in the buffer, maybe just assume to get a specific buffer with suitably dimensions, or the client needs to know which buffer provider to contact for any management operations on that buffer (handle). And, at some point there needs to be a mechanism to verify the validity of a handle. But all of this doesn't mean that it's necessary to encode or embedd this information directly into the handle -- it might also be stored into a registration table (which has the downside of creating contention), or it might just be attached implicitly to the invocation context.
+
+Just linking this type information to the context is certainly the most elegant solution, but also by far the most difficult to achieve -- not to mention the implicit dependency on a very specific invocation situation. So for now (9/2011) it seems best to stick to the simple and explicit implementation, just keeping that structural optimisation in mind. And the link to this buffer type information should be made explicit within the definition anyway, even if we choose to employ another design tradeoff later.
+* thus the conclusion is: we introduce a ''descriptor object'', which will be stored within the handle
+* each BufferProvider exposes a ''descriptor prototype''; it can be specialised and used by to [[organise implementation details|BufferMetadata]]
+
+
+!sanity checks
+there are only limited sanity checks, and they can be expected to be optimised away for production builds.
+Basically the client is responsible for sane buffer access.
+
+
+
+
Buffers are used to hold the media data for processing and output. Within the Lumiera RenderEngine and [[Player]] subsystem, we use some common concepts to handle the access and allocation of working buffers. Yet this doesn't imply having only one central authority in charge of every buffer -- such an approach wouldn't be possible (due to collaboration with external systems) and wouldn't be desirable either. Rather, there are some common basic usage //patterns// -- and there are some core interfaces used throughout the organisation of the rendering process.
+
+Mostly, the //client code,// i.e. code in need of using buffers, can access some BufferProvider, thereby delegating the actual buffer management. This binds the client to adhere to kind of a //buffer access protocol,// comprised of the ''announcing'', ''locking'', optionally ''attaching'' and finally the ''releasing'' steps. Here, the actual buffer management within the provider is a question of implementation and will be configured during build-up of the scope in question.
+
+!usage situations
+;rendering
+:any calculations and transformations of media data typically require an input- and output buffer. To a large extent, these operations will be performed by specialised libraries, resulting in a call to some plain-C function receiving pointers to the required working buffers. Our invocation code has the liability to prepare and provide those pointers, relying on a BufferProvider in turn.
+;output
+:most any of the existing libraries for handling external output require the client to adhere to some specific protocol. Often, this involves some kind of callback invoked at the external library's discretion, thus forcing our engine to prepare data within an intermediary buffer. Alternatively, the output system might provide some mechanism to gain limited direct access to the output buffers, and such an access can again be exposed to our internal client code through the BufferProvider abstraction.
+
+!primary implementations
+;memory pool
+:in all those situations, where we just need a working buffer for some time, we can rely on our internal custom memory allocator.
+:{{red{~Not-Yet-Implemented as of 9/11}}} -- as a fallback we just rely on heap allocations through the language runtime
+;frame cache
+:whenever a calculated result may be of further interest, beyond the immediate need triggering the calculation, it might be eligible for caching.
+:The Lumiera ''frame cache'' is a special BufferProvider, maintaining a larger pool of buffers which can be pinned and kept around for some time,
+:accomodating limited resources and current demand for fresh result buffers.
+
+
+
+
+
the generic BufferProvider implementation exposes a service to attach and maintain additional metadata with individual buffers. Using this service is not mandatory -- a concrete buffer provider implementation may chose to maintain more specific metadata right on the implementation level, especially if more elaborate management is necessary within the implementation anyway (e.g. the frame index). We can expect most buffer provider implementations to utilise at least the generic buffer type id service though.
+
+!buffer types and descriptors
+Client code accesses buffer through [[smart buffer handles|BuffHandle]], including some kind of buffer type information, encoded into a type ID within the ''buffer descriptor''. These descriptors are used like prototypes, relating the type-~IDs hierarchically. Obviously, the most fundamental distinction is the BufferProvider in charge for that specific buffer. Below that, the next mandatory level of distinction is the ''buffer size''. In some cases, additional distinctions can be necessary. Each BufferProvider exposes a service to yield unique type ~IDs governed by such a hierarchical scheme.
+
+!state and metadata for individual buffers
+Beyond that, it can be necessary to associate at least a state flag with //individual buffers.// Doing so requires the buffer to be in //locked state,// otherwise it wouldn't be distinguishable as an separate entity (a client is able to access the buffer memory address only after "locking" this buffer). Especially when using a buffer provider in conjunction with an OutputSlot, these states and transitions are crucial for performing an orderly handover of generated data from the producer (render engine) to the consumer (external output sink).
+
+__Note__: while the API to access this service is uniform, conceptually there is a difference between just using the (shared) type information and associating individual metadata, like the buffer state. Type-~IDs, once allocated, will never be discarded (within the lifetime of an Lumiera application instance -- buffer associations aren't persistent). To the contrary, individual metadata //will be discarded,// when releasing the corresponding buffer. According to the ''prototype pattern'', individual metadata is treated as a one-way-off specialisation.
+
+
+
+
It turns out that --  throughout the render engine implementation -- we never need direct access to the buffers holding actual media data. Buffers are just some entity to be //managed,// i.e. "allocated", "locked" and "released"; the //actual meaning of these operations can be left to the implementation.// The code within the render engine just pushes around ''smart-prt like handles''. These [[buffer handles|BuffHandle]] act as a front-end, being created by and linked to a buffer provider implementation. There is no need to manage the lifecycle of buffers automatically, because the use of buffers is embedded into the render calculation cycle, which follows a rather strict protocol anyway. Relying on the [[capabilities of the scheduler|SchedulerRequirements]], the sequence of individual jobs in the engine ensures...
+* that the availability of a buffer was ensured prior to planning a job ("buffer allocation")
+* that a buffer handle was obtained ("locked") prior to any operation requiring a buffer
+* that buffers are marked as free ("released") after doing the actual calculations.
+
+!operations
+While BufferProvider is an interface meant to be backed by various different kinds of buffer and memory management approaches, there is a common set of operations to be supported by any of them
+;announcing
+:client code may announce beforehand that it expects to get a certain amount of buffers. Usually this causes some allocations to happen right away, or it might trigger similar mechanisms to ensure availability; the BufferProvider will then return the actual number of buffers guaranteed to be available. This announcing step is optional an can happen any time before or even after using the buffers and it can be repeated with different values to adjust to changing requirements. Thus the announced amount of buffers always denotes //additional buffers,// on top of what is actively used at the moment. This safety margin of available buffers usually is accounted separately for each distinct kind of buffer (buffer type). There is no tracking as to which specific client requested buffers, beyond the buffer type.
+;locking
+:this operation actually makes a buffer available for a specific client and returns a [[buffer handle|BuffHandle]]. The corresponding buffer is marked as used and can't be locked again unless released. If necessary, at that point the BufferProvider might allocate memory to accommodate (especially when the buffers weren't announced beforehand). The locking may fail and raise an exception. You may expect failure to be unlikely when buffers have been //announced beforehand.// To support additional sanity checks, the client may provide a token-ID with the lock-operation. This token may be retrieved later and it may be used to ensure the buffer is actually locked for //this token.//
+;attaching
+:optionally the client may attach an object to a locked buffer. This object is placement-constructed into the buffer and will be destroyed automatically when releasing the buffer. Alternatively, the client may provide a pair of constructor- / destructor-functors, to be invoked in a similar way. This allows e.g. to install descriptor structures within the buffer, as required by an external library.
+;releasing
+:buffers need to be released explicitly by the client code. This renders the corresponding BuffHandle invalid, (optionally) invokes a destructor function of an attached object and maybe reclaims the buffer memory
+
+!!type metadata service
+In addition to the basic operations, clients may associate BufferMetadata with individual buffers;
+in the basic form, this means just maintaining a type tag describing the kind of buffer, while optionally this service might be extended to e.g. associating a state flag.
+
+__see also__
+&rarr; OutputSlot relying on a buffer provider to deal with frame output buffers
+&rarr; more about BufferManagement within the RenderEngine and [[Player]] subsystem
+&rarr; RenderMechanics for details on the buffer management within the node invocation for a single render step
+
+
+
+
The invocation of individual [[render nodes|ProcNode]] uses an ''buffer table'' internal helper data structure to encapsulate technical details of the allocation, use, re-use and feeing of data buffers for the media calculations. Here, the management of the physical data buffers is delegated through a BufferProvider, which typically is implemented relying on the ''frame cache'' in the backend. Yet some partially quite involved technical details need to be settled for each invocation: We need input buffers, maybe provided as external input, while in other cases to be filled by a recursive call. We need storage to prepare the (possibly automated) parameters, and finally we need a set of output buffers. All of these buffers and parameters need to be rearranged for invoking the (external) processing function, followed by releasing the input buffers and commiting the output buffers to be used as result.
+
+Because there are several flavours of node wiring, the building blocks comprising such a node invocation will be combined depending on the circumstances. Performing all these various steps is indeed the core concern of the render node -- with the help of BufferTable to deal with the repetitive, tedious and technical details.
+
+!requirements
+The layout of the buffer table will be planned beforehand for each invocation, allongside with planning the individual invocation jobs for the scheduler. At that point, a generic JobTicket for the whole timeline segment is available, describing the necessary operations in an abstract way, as determined by the preceeding planning phase. Jobs are prepared chunk wise, some time in advance (but not all jobs of at once). Jobs will be executed concurrently. Thus, buffer tables need to be created repeatedly and placed into a memory block accessed and owned exclusively by the individual job.
+* within the buffer table, we need an working area for the output handles, the input handles and the parameter descriptors
+* actually, these can be seen as pools holding handle objects which might even be re-used, especially for a chain of effects calculated in-place.
+* each of these pools is characterised by a common //buffer type,// represented as buffer descriptor
+* we need some way to integrate with the StateProxy, because some of the buffers need to be marked especially, e.g. as result
+* there should be convenience functions to release all pending buffers, forwarding the release operation to the individual handles
+
+
//Building the fixture is actually at the core of the [[builder's operation|Builder]]//
 {{red{WIP as of 11/10}}} &rarr; see also the [[planning page|PlanningBuildFixture]]
@@ -1279,6 +1376,25 @@ at the lowest level within the builder there is the step of building a //connect
 * by default, a timeline is outfitted with one video and one sound master bus
 
+
+
Calculation stream is an organisational unit used at the interface level of the Lumiera engine.
+Representing a //stream of calculations,// delivering generated data within //timing constraints,// it is used
+*by the [[play process(es)|PlayProcess]] to define and control properties of the output generation
+*at the engine backbone to feed the [[Scheduler]] with individual [[render jobs|RenderJob]] to implement this stream of calculations
+Calculation stream objects are stateless, constant chunks of definition -- any altering of playback or rendering parameters just causes the respective descriptors to be superseeded. The presence of a CalcStream (being alive within the denoted time span) implies that using any of the associated jobs, dispatcher tables, node and wiring descriptors is safe
+
+!lifecycle
+Calculation stream descriptors can be default constructed, representing a //void calculation.// You can't do anything with these.
+Any really interesting calculation stream needs to be retrieved from the EngineFaçade. Additionally, an existing calculation stream can be chained up or superseded, yielding a new CalcStream based on the parameters of the existing one, possibly with some alterations.
+
+!purpose
+When a calculation stream is retrieved from the EngineFaçade it is already registered and attached there and represents an ongoing activity. Under the hood, several further collaborators will hold a copy of that calculation stream descriptor. While, as such, a CalcStream has no explicit state, at any time it //represents a current state.// In case the running time span of that stream is limited, it becomes superseded automatically, just by the passing of time.
+
+Each calculation stream refers a relevant [[frame dispatcher table|FrameDispatcher]]. Thus, for the engine (interface level), the calculation stream allows to produce the individual [[render jobs|RenderJob]] to enqueue with the [[Scheduler]]. This translation step is what links and relates nominal time with running wall clock time, thereby obeying the [[timing constraints|Timings]] given while initially defining the calculation stream.
+
+Additionally, each calculation stream knows how to access a //render environment closure,// allowing to re-schedule and re-adjust the setup of this stream. Basically, this closure is comprised of several functors (callbacks), which could be invoked to perform management tasks later on. Amongst others, this allows the calculation stream to redefine, supersede or "cancel itself", without the need to access a central registration table at the engine interface level.
+
+
Background: #fefefd
 Foreground: #000
@@ -1308,8 +1424,8 @@ Commands are //defined// using a [[fluent API|http://en.wikipedia.org/wiki/Fluen
 &rarr; see CommandUsage
 
-
-
Commands can be identified and accessed //by name// &mdash; consequently there needs to be an internal command registry, including a link to the actual implementing function, thus allowing to re-establish the connection between command and implementing functions when de-serialising a persisted command. To create a command, we need to provide the following informations
+
+
Commands can be identified and accessed //by name// &mdash; consequently there needs to be an internal command registry, including a link to the actual implementing function, thus allowing to re-establish the connection between command and implementing functions when de-serialising a persisted command. To create a command, we need to provide the following information
 * operation function actually implementing the command
 * function to [[undo|UndoManager]] the effect of the command
 * function to capture state to be used by UNDO.
@@ -1375,11 +1491,11 @@ To support this handling scheme, some infrastructure is in place:
 * performing the actual execution is delegated to a handling pattern object, accessed by name.
 
-
+
[<img[Structure of Commands|uml/fig135173.png]]
 While generally the command framework was designed to be flexible and allow a lot of different use cases, execution paths and to serve various goals, there is an ''intended lifecycle'' &mdash; commands are expected to go through several distinct states.
 
-The handling of a command starts out with a ''command ID'' provided by the client code. Command ~IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all these informations are available, the entity is called a ''command definition''. Conceptually, it is comparable to a //class// or //meta object.//
+The handling of a command starts out with a ''command ID'' provided by the client code. Command ~IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all of this information is available, the entity is called a ''command definition''. Conceptually, it is comparable to a //class// or //meta object.//
 
 By ''binding'' to specific operation arguments, the definition is //armed up//&nbsp; and becomes a real ''command''. This is similar to creating an instance from a class. Behind the scenes, storage is allocated to hold the argument values and any state captured to create the ability to UNDO the command's effect later on.
 
@@ -1630,6 +1746,36 @@ To achieve this, here we try to separate ''Configuration'' from ''Processing''.
 The main tool used to implement this separation is the [[Builder Pattern|http://en.wikipedia.org/wiki/Builder_pattern]]. Here especially we move all decisions and parametrization into the BuildProcess. The Nodes in the render pipeline should process Video/Audio and do nothing else. All decisions, tests and conditional operations are factored out of the render process and handled as configuration of the pipeline, which is done by the Builder. The actual color model and number of ports is configured in by a pre-built wiring descriptor. All Nodes are of equal footing with each other, able to be connected freely within the limitations of the necessary input and output. OpenGL and renderfarm support can be configured in as an alternate implementation of some operations together with an alternate signal flow (usable only if the whole Pipeline can be built up to support this changed signal flow), thus factoring out all the complexities of managing the data flow between core and hardware accelerated rendering. We introduce separate control data connections for the [[automation data|Automation]], separating the case of true multi-channel-effects from the case where one node just gets remote controlled by another node (or the case of two nodes using the same automation data).
 
 Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the Session in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
+
+
+
+
//Currently (5/2011) this page is used to collect and build up a coherent design for the player subsystem of Lumiera..//
+
+!Starting point
+The intention is to start out with the design of the PlayerDummy and to //transform//&nbsp; it into the full player subsystem.
+* the ~DisplayService in that dummy player design moves down into Proc-Layer and becomes the OutputManager
+* likewise, the ~DisplayerSlot is transformed into the interface OutputSlot, with various implementations to be registered with the OutputManager
+* the core idea of having a play controler act as the frontend and handle to an PlayProcess is retained.
+
+!!Idea
+Introduce a new kind of MObject, a GeneratorMO, to represent what the current dummy player does: generate some kind of test data. As a first step, we might retro-fit the existing dummy player to this structure, and then expand it to have a minimal render engine setup for tests and diagnostics. That is, there will be some kind of GeneratorNode to produce test data.
+
+!!Stages of transition
+Reworking the dummy player into the [[Player subsystem|PlayService]] is a larger undertaking, and best broken up into several //stages.//
+# just moving the existing entities into the final location (typically down in the layer hierarchy)
+# introduce a GeneratorMO and a GeneratorNode, while providing a shortcut to get the GeneratorNode without requiring the (not yet implemented) Builder.
+# rework the ~ProcessImpl and the ~TickService to become the [[calculation stream|CalcStream]] and use the real Engine Interface, but still with an mock implementation hooked up behind
+# extend and elaborate the handling of output, build the foundation of the real OutputManager
+# switch to the real Scheduler and Engine implementation
+
+!!!!new entities to build {{red{#805}}}
+* the ~DisplayService needs to be generalised into an OutputManager interface
+* we need to introduce a new kind of StructAsset, the ViewerAsset.
+* then, of course, the PlayService interface needs to be created
+* as a temporary soltion, a ''~DummyPlayConnection'' will be created hard-wired, within the ~DummyPlayer service. This dumy container is a test setup, explicitly creating a OutputManager implementation, a GeneratorMO, a GeneratorNode (thus omitting the not-yet-implemented Builder) and the corresponding ModelPort. This way, the ~DummyPlayer service can be changed to use the real PlayService for creating the dummy generated output data. It will then pass back the resulting PlayController to the existing GUI setup.
+
+The Method of pushing frames to the Viewer widget is changed fundamentally during that transition: while previously, in the PlayerDummy design study, the GUI opened a Display-façade interface -- now rather the GUI has to register an OutputSlot implementation with the global OutputManager. Such a registration requires an explicit deregistration on shutdown, and this can be made to block, until no further data will be delivered. Using this approach we hope to circumvent problems with occasional crashes on closing the application, due to dispatching frame output signals in the event thread while shutdown is already in progress.
+
 
@@ -1655,7 +1801,7 @@ Cinelerra uses this term in a related manner but with a somewhat shifted focus: In this usage, the EDL in most cases will be almost synonymous to &raquo;the session&laquo;, just the latter emphasizes more the state aspect. While the Lumiera project started out using the same terminology, later on, when support for multiple "containers" within the session and for [[meta-clips|VirtualClip]] was determined to be of much importance, the new term &raquo;[[Sequence]]&laquo; was preferred.
-
+
These are the tools provided to any client of the Proc layer for handling and manipulating the entities in the Session. When defining such operations, //the goal should be to arrive at some uniformity in the way things are done.// Ideally, when writing client code, one should be able to guess how to achieve some desired result.
 
 !guiding principle
@@ -1679,7 +1825,7 @@ This has some obvious and some subtle consequences. Of course, manipulating //ta
 Consequently, sometimes there needs to be done sort of a ''read-copy-update'', i.e. self-replacement by copy followed by manipulation of the new copy, while ongoing processes use the unaltered original object until they receive some sort of reset.
 
 !!undo
-Basically, each elementary operation has to record the informations necessary to be undone. It does so by registering a Memento with some central UndoManager facility. This Memento object contains a functor pre-bound with the needed parameter values. (Besides, the UndoManager is free to implement a second level of security by taking independent state snapshots). 
+Basically, each elementary operation has to record the information necessary to be undone. It does so by registering a Memento with some central UndoManager facility. This Memento object contains a functor pre-bound with the needed parameter values. (Besides, the UndoManager is free to implement a second level of security by taking independent state snapshots). 
 {{red{to be defined in more detail later...}}}
 
@@ -1693,6 +1839,19 @@ 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.
+
+
The primary interface used by the upper application layers to interact with the render engine, to create and manage ongoing [[calculation streams|CalcStream]].
+
+Below this facade, there is a thin adaptadion and forwarding layer, mainly talking to
+* the individual [[Calculation Streams|CalcStream]] created for each PlayProcess.
+* the FrameDispatcher, which translates such streams into a series of RenderJob entries
+* the [[Scheduler]], which is responsible to perform these jobs in a timely fashion
+
+!Quality of Service
+Within the Facade, there is the definition of the {{{EngineService::Quality}}} tag, alongside with several pre-defined quality settings.
+Actually this interface is a strategy, allowing to define quite specific quality levels, in case we need that. Clients can usually just use
+these ~QoS-tags like enum values (they are copyable), without caring for the engine implementation related details.
+
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.
                                                                                       
@@ -1735,8 +1894,10 @@ To make the intended use of the classes more clear, consider the following two e
 * a video clip placed relatively, with an attached HUE effect &rarr;[[Example2]]
 
-
+
a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
+
+The render nodes network is always built separate for each [[timeline segment|Segmentation]], which is //constant in wiring configuration.// Thus, while exit node(s) are per segment, the corresponding exit nodes of consecutive segments together belong to a ModelPort, which in turn corresponds to a global pipe (master bus not connected any further). These relations guide the possible configuration for an exit node: It may still provide multiple channels -- but all those channels are bound to belong to a single logical stream -- same StreamPrototype, always handled as bundle, connected and routed in one step. For example, when there is an 5.1 Audio master bus with a single fader, then "5.1 Audio" would be a prototype and these 6 channels will always be handled together; in such a case it makes perfectly sense to access these 6 audio channels through a single exit node, which is keyed (identified) by the same [[Pipe]]-ID as the corresponding ModelPort and the corresponding global pipe ("5.1 Audio master bus")
 
@@ -1801,15 +1962,16 @@ Some further details * a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
-
-
a specially configured view -- joining together high-level and low-level model
+
+
a specially configured view -- joining together high-level and low-level model.
+The Fixture acts as //isolation layer// between the two models, and as //backbone to attach the render nodes.//
 * all MObjects have their position, length and configuration set up ready for rendering.
 * any nested sequences (or other kinds of indirections) have been resolved.
 * every MObject is attached by an ExplicitPlacement, which declares a fixed position (Time, [[Pipe|OutputDesignation]])
 * these ~ExplicitPlacements are contained immediately within the Fixture, ordered by time
 * besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPortRegistry]]
 
-As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]].
+As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important first step and sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]].
 ''Note'': all of the especially managed storage of the LowLevelModel is hooked up behind the Fixture
 &rarr; FixtureStorage
 &rarr; FixtureDatastructure
@@ -1827,62 +1989,62 @@ The fixture is like a grid, where one dimension is given by the [[model ports|Mo
 ;Segmentation
 :The segmentation partitiones the time axis of a single timeline into segments of constant (wiring) configuration
 :Together, the segments form a seamless sequence of time intervals. They contain a copy of each (explicit) placement of a visible object touching that time interval. Besides that, segments are the top level grouping device of the render engine node graph; they are always built and discarded at once.
-:Segments may be //hot swapped// into an ongoing render.
+:Segments (and even a different Segmentation) may be //hot swapped// into an ongoing render.
 
 ;Exit Nodes
 :Each segment holds an ExitNode for each relevant ModelPort of the corresponding timeline.
 :Thus the exit nodes are keyed by ~Pipe-ID as well (and consequently have a distinct [[stream type|StreamType]]) -- each model port coresponds to {{{<number_of_segments>}}} separate exit nodes, but of course an exit node may be //mute.//
 
-
-
Generally speaking, the [[here|Fixture]] comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline.
-This page focusses on the actual datastructure and usage details on that level. See also &rarr; [[storage|FixtureStorage]] considerations.
+
+
Generally speaking, the datastructure to implement the ''Fixture'' (&rarr; see a more general description [[here|Fixture]]) is comprised of a ModelPortRegistry and a set of [[segmentations|Segmentation]] per Timeline.
+This page focusses on the actual data structure and usage details on that level. See also &rarr; [[storage|FixtureStorage]] considerations.
 
 !transactional switch
-A key point to note is the fact that the fixture is frequently [[re-built||BuildFixture]] by the [[Builder]], while render processes may be going in in parallel. Thus, when a build process is finished, a transactional ''commit'' happens to ''hot swap'' the new parts of the model. This is complemented by a cleanup of tainted render processes; finally, storage can be relaimed.
+A key point to note is the fact that the fixture is frequently [[re-built|BuildFixture]] by the [[Builder]], while render processes may be going on in parallel. Thus, when a build process is finished, a transactional ''commit'' happens to ''hot swap'' the new parts of the model. This is complemented by a clean-up of tainted render processes; finally, storage can be reclaimed.
 
 To support this usage pattern, the Fixture implementation makes use of the [[PImpl pattern|http://c2.com/cgi/wiki?PimplIdiom]]
 
 !Collecting usage scenarios {{red{WIP 12/10}}}
 * ModelPort access
 ** get the model port for a given ~Pipe-ID
-** ennumerate the model ports for a Timeline
+** enumerate the model ports for a Timeline
 * rendering frame dispatch
 ** get or create the frame dispatcher table
 ** dispatch a single frame to yield the corresponding ExitNode
 * (re)building
-** create a new implementation frame
+** create a new implementation transaction
 ** create a new segmentation
 ** establish what segments actually need to be rebuilt
 ** dispatch a newly built segment into the transaction
-** schedule superseded segments and tainted process for cleanup
+** schedule superseded segments and tainted process for clean-up
 ** commit a transaction
  
 
 !Conclusions about the structure {{red{WIP 12/10}}}
 * the ~PImpl needs to be a single (language) pointer. This necessitates having a monolithic Fixture implementation holder
-* moreover, this necessitates a tight integration up to implementation level, both with the cleanup and the render processes themselves
+* moreover, this necessitates a tight integration down to implementation level, both with the clean-up and the render processes themselves
 
-
+
The Fixture &rarr; [[data structure|FixtureDatastructure]] acts as umbrella to hook up the elements of the render engine's processing nodes network (LowLevelModel).
-Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes are known to be terminated.
+Each segment within the [[Segmentation]] of any timeline serves as ''extent'' or unit of memory management: it is built up completely during the corresponding build process and becomes immutable thereafter, finally to be discarded as a whole when superseded by a modified version of that segment (new build process) -- but only after all related render processes (&rarr; CalcStream) are known to be terminated.
 
 Each segment owns an AllocationCluster, which in turn manages all the numerous small-sized objects comprising the render network implementing this segment -- thus the central question is when to //release the segment.//
-* for one, we easily detect the point when a segment is swapped out of the segmentation; at this point we also have to detect the //tainted render processes.//
-* but those render processes terminate asynchronously, and that forces us to do some kind of registration and deregistration.
+* for one, we easily detect the point when a segment is swapped out of the segmentation; at this point we also have to detect the //tainted calculation streams.//
+* but those render processes (calc streams) terminate asynchronously, and that forces us to do some kind of registration and deregistration.
 
 !!question: is ref-counting acceptable here?
 //Not sure yet.// Of course it would be the simplest approach. KISS.
-Basically the concern is that each new render process had to access the shared counts of all segments it touches.
+Basically the concern is that each new CalcStream had to access the shared counts of all segments it touches.
 
 ''Note'': {{{shared_ptr}}} is known to be implemented by a lock-free algorithm (yes it is, at least since boost 1.33. Don't believe what numerous FUD spreaders have written). Thus lock contention isn't a problem, but at least a memory barrier is involved (and if I&nbsp;judge GCC's internal documentation right, currently their barriers extend to //all// globally visible variables)
 
 !!question: alternatives?
-There are. As the builder is known to be run again and again, no one forces us to deallocate as soon as we could. That's the classical argument exploited by any garbage collector too. Thus we could just note the fact that a render process had terminated and evaluate all those noted results on later occasion.
+There are. As the builder is known to be run again and again, no one forces us to deallocate as soon as we could. That's the classical argument exploited by any garbage collector too. Thus we could just note the fact that a calculation stream is done and re-evaluate all those noted results on later occasion. Obviously, the [[Scheduler]] is in the best position for notifying the rest of the system when this and that [[job|RenderJob]] has terminated, because the Scheduler is the only facility required to touch each job reliably. Thus it seems favourable to add basic support for either termination callbacks or for guaranteed execution of some notification jobs to the [[Scheduler's requirements|SchedulerRequirements]].
 
 !!exploiting the frame-dispatch step
-Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add a back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
+Irrespective of the decision in favour or against ref-counting, it seems reasonable to make use of the //frame dispatch step,// which is necessary anyway. The idea is to give each render process (maybe even each CalcStream)  a //copy//&nbsp; of an dispatcher table object -- basically just a list of time breaking points and a pointer to the relevant exit node. If we keep track of those dispatcher tables, add some kind of back-link to identify the process and require the process in turn to deregister, we get a tracking of tainted processes for free.
 
 !!assessment {{red{WIP 12/10}}}
 But the primary question here is to judge the impact of such an implementation. What would be the costs?
@@ -1901,22 +2063,22 @@ But the primary question here is to judge the impact of such an implementation.
 &rArr; we should spend a second thought about how actually to find and process the segments to be discarded
 
 !!!identifying tainted and disposable segments
-Above estimation hints at the necessity of frequently finding some 30 to 100 segments to be disposed, out of thousands, assumed the original reason for triggering the build process was a typically local change in the high-level model. We can only discard when all related processes are finished, but there is a larger number of segments as candidate for eviction. These candidates are rather easy to pinpoint -- they will be uncovered during a linear comparison pass prior to commiting the changed Fixture. Usually, the number of candidates will be low (localised changes), but global manipulations might invalidate thousands of segments.
-* if we frequently pick the segments actually to disposed, there is the danger of performance degeneration when the number if segments is high
+Above estimation hints at the necessity of frequently finding some 30 to 100 segments to be disposed, out of thousands, assumed the original reason for triggering the build process was a typically local change in the high-level model. We can only discard when all related processes are finished, but there is a larger number of segments as candidate for eviction. These candidates are rather easy to pinpoint -- they will be uncovered during a linear comparison pass prior to committing the changed Fixture. Usually, the number of candidates will be low (localised changes), but global manipulations might invalidate thousands of segments.
+* if we frequently pick the segments actually to be disposed, there is the danger of performance degeneration when the number of segments is high
 * the other question is if we can afford just to keep all of those candidates around, as all of them are bound to get discardable eventually
 * and of course there is also the question how to detect //when// they're due.
 
 ;Model A
-:use a logarithmic datastructure, e.g. a priority queue
-:problem here is that the priorities change, which either means shared access or a lot of "superseeded" entries
+:use a logarithmic datastructure, e.g. a priority queue. Possibly together with LRU ordering
+:problem here is that the priorities change, which either means shared access or a lot of "superseded" entries
 ;Model B
-:keep all candidates around and track the tainted processes instead
+:keep all superseded segments around and track the tainted processes instead
 :problem here is how to get the tainted processes precisely and with low overhead
 //currently {{red{12/10}}} I tend to prefer Model B...// while the priority queue remains to be investigated in more detail for organising the actual build process.
 But actually I'm struck here, because of the yet limited knowledge about those render processes....
 * how do we //join// an aborted/changed rendering process to his successor, without creating a jerk in the output?
-* is it even possible to continue a process when parts of the covered timerange is affected by a build?
-If the latter question is answered with "No!", then the problem gets simple in solution, but maybe memory consuming: In that case, //all//&nbsp; processes linked to a timeline gets affected and thus tainted; we'd just dump them onto a pile and delay releasing all of the superseeded segments until all of them are known to be terminated.
+* is it even possible to continue a process when parts of the covered time-range are affected by a build?
+If the latter question is answered with "No!", then the problem gets simple in solution, but maybe memory consuming: In that case, //all//&nbsp; processes linked to a timeline gets affected and thus tainted; we'd just dump them onto a pile and delay releasing all of the superseded segments until all of them are known to be terminated.
 
@@ -1956,6 +2118,9 @@ Additionally, they may be used for resource management purposes by embedding a r #* one OpenGL Dataframe could contain raw texture data (but I am lacking expertise for this topic)
+
+
An implementation facility within the RenderEngine, responsible for translating a logical [[calculation stream|CalcStream]] (corresponding to a PlayProcess) into a sequence of individual RenderJob entries, which can then be handed over to the [[Scheduler]]. Performing this operation involves a special application of [[time quantisation|TimeQuant]]: after establishing a suitable starting point, a typically contiguous series of frame numbers need to be generated, together with the time coordinates for each of those frames
+
/***
 |Name|FullScreenPlugin|
@@ -2216,19 +2381,21 @@ Finally, this example shows an ''automation'' data set controlling some paramete
 
 
-
+
!Observations, Ideas, Proposals
 ''this page is a scrapbook for collecting ideas'' &mdash; please don't take anything noted here too literal. While writing code, I observe that I (ichthyo) follow certain informal guidelines, some of which I'd like to note down because they could evolve into general style guidelines for the Proc-Layer code.
 * ''Inversion of Control'' is the leading design principle.
 * but deliberately we stay just below the level of using Dependency Injection. Singletons and call-by-name are good enough. We're going to build //one// application, not any conceivable application.
-* write error handling code only if the error situation can be actually //handled// at this place. Otherwise, be prepared for exceptions just passing by and thus handle any resources by "resource acquisition is initialisation" (RAII). Remember: error handling defeats decoupling and encapsulation
+* write error handling code only if the error situation can be actually //handled// at this place. Otherwise, be prepared for exceptions just passing by and thus handle any resources by "resource acquisition is initialisation" (RAII). Remember: error handling defeats decoupling and encapsulation.
 * (almost) never {{{delete}}} an object directly, use {{{new}}} only when some smart pointer is at hand.
-* when user/client code is intended to create objects, make the ctor protected and provide a factory member called {{{create}}} instead, returning a smart pointer
+* clearly distinguish ''value objects'' from objects with ''reference semantics'', i.e. objects having a distict //object identity.//
+* when user/client code is intended to create reference-semantics objects, make the ctor protected and provide a factory member called {{{create}}} instead, returning a smart pointer
 * similarly, when we need just one instance of a given service, make the ctor protected and provide a factory member called {{{instance}}}, to be implemented by the lumiera::[[Singleton]] factory.
 * whenever possible, prefer this (lazy initialised [[Singleton]]) approach and avoid static initialisation magic
 * avoid doing anything non-local during the startup phase or shutdown phase of the application, especially avoid doing substantial work in any dtor.
 * avoid asuming anything that can't be enforced by types, interfaces or signatures; this means: be prepared for open possibilities
 * prefer {{{const}}} and initialisation code over assignment and active changes (inspired by functional programming)
+* code is written for ''being read by humans''; code shall convey its meaning //even to the casual reader.//
 
@@ -2646,8 +2813,8 @@ The ''pull up'' sequence performs basic initialisation of the session facilities The ''shut down'' sequence does exactly that: halt processing and rendering, disconnect an existing session, if any, get back into initial state. It doesn't care for unwinding session contents, which is assumed to happen automatically when references to previous session contents go out of scope.
-
-
Opening and accessing media files on disk poses several problems, most of which belong to the domain of Lumiera's data backend. Here, we focus on the questions related to making media data available to the session and the render engine. Each media will be represented by an MediaAsset object, which indeed could be a compound object (in case of MultichannelMedia). Building this asset object thus includes getting informations from the real file on disk. For delegating this to the backend, we use the following query interface:
+
+
Opening and accessing media files on disk poses several problems, most of which belong to the domain of Lumiera's data backend. Here, we focus on the questions related to making media data available to the session and the render engine. Each media will be represented by an MediaAsset object, which indeed could be a compound object (in case of MultichannelMedia). Building this asset object thus includes getting information from the real file on disk. For delegating this to the backend, we use the following query interface:
 * {{{queryFile(char* name)}}} requests accessing the file and yields some (opaque) handle when successful.
 * {{{queryChannel(fHandle, int)}}} will then be issued in sequence with ascending index numbers, until it returns {{{NULL}}}.
 * the returned struct (pointer) will provide the following information:
@@ -2947,12 +3114,12 @@ These are used as token for dealing with other objects and have no identity of t
 
 
-
+
Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denotes a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
 
-A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination and some other entity actually uses this designation as a target, either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
+A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination, while some other entity at the same time actually //uses this designation as a target,// either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
 
-Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
+Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewConnection]]. Any render or display process happens at a model port.
 
 !formal specification
 Model port is a //conceptual entity,// denoting the possibility to pull generated data of a distinct (stream)type from a specific bus within the model -- any possible output produced or provided by Lumiera is bound to appear at a model port. The namespace of model ports is global, each being associated with a ~Pipe-ID.
@@ -2993,7 +3160,7 @@ __Note__: nothing within the PlacementIndex requires the root object to be of a
 
 
-
+
Based on practical experiences, Ichthyo tends to consider Multichannel Media as the base case, while counting media files providing just one single media stream as exotic corner cases. This may seem counter intuitive at first sight; you should think of  it as an attempt to avoid right from start some of the common shortcomings found in many video editors, especially
 * having to deal with keeping a "link" between audio and video clips
 * silly limitations on the supported audio setups (e.g. "sound is mono, stereo or Dolby-5.1")
@@ -3026,6 +3193,12 @@ While the general approach and reasoning remains valid, a lot of the details loo
 * while the asset related parts remain as specified, we get a distinct ChannelConfig asset instead of the clip asset (which seems to be redundant)
 * either the ~ClipMO referres this ChannelConfig asset &mdash; or in case of the VirtualClip a BindingMO takes this role. Clip Asset and MO could be joined into a single entity
 * as the BindingMO is also used to implement the top-level timelines, the treatment of global and local pipes is united
+* every pipe (bus) should be able to carry multiple channels, //but with the limitation to only a single media StreamType//
+* this "multichannel-of-same-kind" capability carries over to the ModelPort entries and even the OutputSlot elements
+* only when allocating / "opening" an OutputSlot, we get multiple handles for plain single channels.
+* this can be considered the breaking point, where we enter the realm of the render engine. Here, indeed, only single channels are processed
+
+
 
@@ -3048,21 +3221,33 @@ While the general approach and reasoning remains valid, a lot of the details loo In the most general case the render network may be just a DAG (not just a tree). Especially, multiple exit points may lead down to the same node, and following each of this possible paths the node may be at a different depth on each. This rules out a simple counter starting from the exit level, leaving us with the possibility of either employing a rather convoluted addressing scheme or using arbitrary ID numbers.{{red{...which is what we do for now}}}
-
-
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. There is no such thing as an "engine object" calling nodes iteratively or table driven, rather, the nodes themselves issue recursive calls to their predecessor(s). For this to work, we need the nodes to adhere to a specific protocol:
-# Node is pulled, with a StateProxy object as parameter (encapsulating the access to the frames or buffers)
-# Node may now access current parameter values, using the state accessible via the StateProxy
-# using it's //input-output and wiring descriptor,// the Node creates a StateAdapter wrapping the StateProxy for allocating buffers and accessing the required input
-# StateAdapter might first try to get the output frames from the Cache in the Backend. In case of failure, a {{{process()}}} call is prepared by generating {{{pull()}}} call(s) for the input
-# as late as possible, typically on return, these recursive pull-calls have allocated a buffer containing the input data.
-# when input is ready prior to the {{{process()}}} call, output buffers will be allocated, either from the cache, or (if not caching) from the "parent" StateAdapter up the callstack.
-# after all buffers are available, the StateAdapter issues the {{{process()}}} call back to the originating node, which now may dereference the frame pointers and do its calculations
-# finally, when the {{{pull()}}} call returns, "parent" state originating the pull holds onto the buffers containing the calculated output result.
+
+
The [[nodes|ProcNode]] are wired to form a "Directed Acyclic Graph"; each node knows its predecessor(s), but not its successor(s).  The RenderProcess is organized according to the ''pull principle'', thus we find an operation {{{pull()}}} at the core of this process. Meaning that there isn't an central entity invoking nodes consecutively. Rather, the nodes themselves contain the detailed knowledg regarding prerequisites, so the calculation plan is worked out recursively. Yet there are some prerequisite resources to be made available for any calculation to happen. Thus the actual calculation is broken down into atomic chunks of work, resulting in a 2-phase invocation whenever "pulling" a node. For this to work, we need the nodes to adhere to a specific protocol:
+;planning phase
+:when a node invocation is foreseeable to be required for getting a specific frame for a specific nominal and actual time, the engine has to find out the actual operations to happen
+:# the planning is initiated by issuing an "get me output" request, finally resulting in a JobTicket
+:# recursively, the node propagates "get me output" requests for its prerequisites
+:# after retrieving the planning information for these prerequisites, the node encodes specifics of the actual invocation situation into a closure called StateAdapter <br/>{{red{TODO: why not just labeling this &raquo;~StateClosure&laquo;?}}}
+:# finally, all this information is packaged into a JobTicket representing the planning results.
+;pull phase
+:now the actual node invocation is embedded within a job, activated through the scheduler to deliver //just in time.//
+:# Node is pulled, with a StateProxy object as parameter (encapsulating BufferProvider for access to the required frames or buffers)
+:# Node may now retrieve current parameter values, using the state accessible via the StateProxy
+:# to prepare for the actual {{{process()}}} call, the node now has to retrieve the input prerequisites
+:#* when the planning phase determined availability from the cache, then just these cached buffer(s) are now retrieved, dereferencing a BuffHandle
+:#* alternatively the planning might have arranged for some other kind of input to be provided through a prerequisite Job. Again, the corresponding BuffHandle can now be dereferenced
+:#* Nodes may be planned to have a nested structure, thus directly invoking {{{pull()}}} call(s) to prerequisite nodes without further scheduling
+:# when input is ready prior to the {{{process()}}} call, output buffers will be allocated by locking the output [[buffer handles|BuffHandle]] prepared during the planning phase
+:# since all buffers and prerequistes are available, the Node may now prepare a frame pointer array and finally invoke the external {{{process()}}} to kick off the actual calculations
+:# finally, when the {{{pull()}}} call returns, "parent" state originating the pull holds onto the buffers containing the calculated output result.
+{{red{WIP as of 9/11  -- many details here are still to be worked out and might change as we go}}}
+
 some points to note:
 * the WiringDescriptor is {{{const}}} and precalculated while building (remember another thread may call in parallel)
 * when a node is "inplace-capable", input and output buffer may actually point to the same location
 * but there is no guarantee for this to happen, because the cache may be involved (and we can't overwrite the contents of a cache frame)
 * generally, a node may have N inputs and M output frames, which are expected to be processed in a single call
+* some of the technical details of buffer management are encapsulated within the BufferTable of each invocation
 
 &rarr; the [["mechanics" of the render process|RenderMechanics]]
 &rarr; more fine grained [[implementation details|RenderImplDetails]]
@@ -3109,15 +3294,15 @@ But because I know the opinions on this topc are varying (users tend to be delig
 My proposed aproach is to treat OpenGL as a separate video raw data type, requiring separete and specialized [[Processing Nodes|ProcNode]] for all calculations. Thus the Builder could connect OpenGL nodes if it is possible to cover the render path in whole or partially or maybe even just for preview.
 
-
+
A low-level abstraction within the [[Builder]] &mdash; it serves to encapsulate the details of making multi-channel connections between the render nodes: In some cases, a node can handle N channels internally, while in other cases we need to replicate the node N times and wire each channel individually. As it stands, the OperationPoint marks the ''borderline between high-level and low-level model'': it is invoked in terms of ~MObjects and other entities of the high-level view, but internally it manages to create ProcNode and similar entities of the low-level model.
 
 The operation point is provided by the current BuilderMould and used by the [[processing pattern|ProcPatt]] executing within this mould and conducting the current build step. The operation point's interface allows //to abstract//&nbsp; these details, as well as to //gain additional control//&nbsp; if necessary (e.g. addressing only one of the channels). The most prominent build instruction used within the processing patterns (which is the instruction {{{"attach"}}}) relies on the aforementioned //approach of abstracted handling,// letting the operation point determine automatically how to make the connection.
 
-This is possible because the operation point has been provided (by the mould) with informations about the media stream type to be wired, which, together with information accessible at the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
+This is possible because the operation point has been provided (by the mould) with information about the media stream type to be wired, which, together with information accessible at the [[render node interface|ProcNode]] and from the [[referred processing assets|ProcAsset]], with the help of the [[connection manager|ConManager]] allows to figure out what's possible and how to do the desired connections. Additionally, in the course of deciding about possible connections, the PathManager is consulted to guide strategic decisions regarding the [[render node configuration|NodeConfiguration]], possible type conversions and the rendering technology to employ.
 
-
+
An ever recurring problem in the design of Luimiera's ~Proc-Layer is how to refer to output destinations, and how to organise them.
 Wiring the flexible interconnections between the [[pipes|Pipe]] should take into account both the StreamType and the specific usage context ([[scope|PlacementScope]]) -- and the challenge is to avoid hard-linking of connections and tangling with the specifics of the target to be addressed and connected. This page, started __6/2010__ by collecting observations to work out the relations, arrives at defining a //key abstraction// of output management.
 
@@ -3129,7 +3314,7 @@ Wiring the flexible interconnections between the [[pipes|Pipe]] should take into
 ** as Timeline is just a façade, BindingMO has to expose something which can be referred for attaching effects (to global pipes)
 ** when used as VirtualClip, there is somehow a channel configuration, either as asset, or exposed by the BindingMO
 * Placements always resolve at least two dimensions: time and output. The latter means that a [[Placement]] can figure out to where to connect
-* the resolution ability of Placements could help to overcome the problems in conjunction with a VirtualClip: missing output destination informations could be inherited down....
+* the resolution ability of Placements could help to overcome the problems in conjunction with a VirtualClip: missing output destination information could be inherited down....
 * expanding on the basic concept of a Placement in N-dimensional configuration space, this //figuring out// would denote the ability to resolve the final output destination
 * this resolution to a final destination is explicitly context dependent. We engage into quite some complexities to make this happen (&rarr; BindingScopeProblem)
 * [[processing patterns|ProcPatt]] are used for creating nodes on the source network of a clip, and similarly for fader, overlay and mixing into a summation pipe
@@ -3141,21 +3326,21 @@ Wiring the flexible interconnections between the [[pipes|Pipe]] should take into
 * //indirect// means to derive the destination transitively (looking at the destination's output designation and so on)
 * //relative// in this context means that we refer to "the N^^th^^ of this kind" (e.g. the second video out)
 * we need to attach some metadata with an output; especially we need an associated StreamType
-* output designation is structured into several //levels://
+* the referral to an output designation can be observed on and is structured into several //levels://
 ** within the body of the model, mostly we address output destinations relatively
 ** at some point, we'll address a //subgroup// within the global pipes, which acts like a summation sink
-** there might be //master pipes//
-** finally, there is the hardware output or the distinct channel within the rendered result &mdash; never to be referred explicitly
+** there might be //master pipe(s),// collecting the output of various subgroups
+** finally, there is the hardware output or the distinct channel within the rendered result &mdash; never to be referenced explicitly
 !!!relation to Pipes
 in almost every case mentioned above, the output designation is identical with the starting point of a [[Pipe]]. This might be a global pipe or a ClipSourcePort. Thus it sounds reasonable to use pipe-~IDs directly as output designation. Pipes, as an accountable entity (=asset) just //leap into existence by being referred.// On the other hand, the //actual//&nbsp; pipe is a semantic concept, a specific structural arrangement of objects. Basically it means that some object acts as attachment point and thereby //claims//&nbsp; to be the entrance side of a pipe, while other processor objects chain up in sequence.
 !!!system outputs
-System level output connections seem to be an exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. But the question is if system outputs shall be directly addressable at all as output designation? Generally speaking, Lumiera is not intended to be a system wide connection manager or a real time performance software. Thus it's advisable to refrain from direct referrals to system level connections from within the model. Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen. So these external outputs become rather a matter of application configuration &mdash; and for all the other purposes we're free to ''use pipe-~IDs as output designation''.
+System level output connections seem to be an exception to the above rule. There is no pipe at an external port, and these externally visible connection points can appear and disappear, driven by external conditions. Yet the question is if system outputs shall be directly addressable at all as output designation? Generally speaking, Lumiera is not intended to be a system wide connection manager or a real time performance software. Thus it's advisable to refrain from direct referrals to system level connections from within the model. Rather, there should be a separate OutputManagement to handle external outputs and displays, both in windows or full screen. So these external outputs become rather a matter of application configuration &mdash; and for all the other purposes we're free to ''use pipe-~IDs as output designation''.
 !!!consequences of mentioning an output designation
 The immediate consequence is that a [[Pipe]] with the given ID exists as an accountable entity. Only if &mdash; additionally &mdash; a suitable object within the model //claims to root this pipe,//  a connection to this designation is wired, using an appropriate [[processing pattern|ProcPatt]]. A further consequence then is for the mentioned output designation to become a possible candidate for a ModelPort, an exit node of the built render nodes network. By default, only those designations without further outgoing connections actually become active model ports (but an existing and wired pipe exit node can be promoted to a model port explicitly).
 &rarr; OutputManagement
 
 !!Challenge: mapping of output designations
-An entire sequence can be embedded within another sequence as a VirtualClip. While for a simple clip there is a Clip-~MObject placed into the model, holding an reference to a media asset, in case of a virtual clip an BindingMO takes on the role of the clip object. Note that BindingMO also acts as [[Timeline]] implementation &mdash; indeed the same sequence might be bound simultaneously as a timeline and into a virtual clip. This flexibility creates a special twist, as the contents of this sequence have no way of finding out if they're used as timeline or embedded virtual clip. So parts of this sequence might mention a OutputDesignation which, when using the sequence as virtual clip, needs to be translated into a virtual media channel, which can be treated in the same fashion as any channel (video, audio,...) found within real media. Especially, a new output designation has to be derived in a sensible way, which largely depends on how the original output designation has been specified.
+An entire sequence can be embedded within another sequence as a VirtualClip. While for a simple clip there is a Clip-~MObject placed into the model, holding an reference to a media asset, in case of a virtual clip an BindingMO takes on the role of the clip object. Note that, within another context, BindingMO also acts as [[Timeline]] implementation &mdash; indeed even the same sequence might be bound simultaneously as a timeline and into a virtual clip. This flexibility creates a special twist, as the contents of this sequence have no way of finding out if they're used as timeline or embedded virtual clip. So parts of this sequence might mention a OutputDesignation which, when using the sequence as virtual clip, needs to be translated into a virtual media channel, which can be treated in the same manner like any channel (video, audio,...) found within real media. Especially, a new output designation has to be derived in a sensible way, which largely depends on how the original output designation has been specified.
 &rarr; see OutputMapping regarding details and implementation of this mapping mechanism
 
 
@@ -3163,7 +3348,7 @@ An entire sequence can be embedded within another sequence as a VirtualClip. Whi
 
 !Output designation definition
 OutputDesignation is a handle, denoting a target [[Pipe]] to connect.
-It exposes a function to resolve to a Pipe, and to retrieve the StreamType of that resolved output. It can be ''defined'' either explicitly by ~Pipe-ID, or by an indirect or relative specification. The later cases are resolved on demand only (which may be later and might change the meaning depending on the context). It's done this way intentionally to gain flexibility and avoid hard wiring (=explicit ~Pipe-ID)
+It exposes a function to resolve to a Pipe, and to retrieve the StreamType of that resolved output. It can be ''defined'' either explicitly by ~Pipe-ID, or by an indirect or relative specification. The later cases are resolved on demand only (which may be later and might even change the meaning, depending on the context). It's done this way intentionally to gain flexibility and avoid hard wiring (=explicit ~Pipe-ID)
 
 !!!Implementation notes
 Thus the output designation needs to be a copyable value object, but opaque beyond that. Mandated by the various ways to specify an output designation, a hidden state arises regarding the partial resolution. The implementation solves that dilemma by relying on the [[State|http://en.wikipedia.org/wiki/State_pattern]] pattern in combination with an opaque in-place buffer.
@@ -3174,17 +3359,18 @@ While actually data frames are //pulled,// on a conceptual level data is assumed
 * first of all, we need to know //what to route// -- kind of the traits of the data. This is given by the //current pipe.//
 * then we'll need to determine an output designation //suitable for this data.// This is given by a "Plug" (WiringRequest) in the placement, and may be derived.
 * finally, this output designation will be //resolved// -- at least partially, resulting in a target pipe to be used for the wiring
-As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type (e.g. and audio output when the pipe currently in question deals with video) is irrelevant.
+As both of these specifications are given by [[Pipe]]-~IDs, the actual designation information may be reduced. Much can be infered from the circumstances, because any pipe includes a StreamType, and an output designation for an incompatible stream type is irrelevant. (e.g. and audio output when the pipe currently in question deals with video)
 
-
+
//writing down some thoughts//
 
 * ruled out the system outputs as OutputDesignation.
 * thus, any designation is a [[Pipe]]-ID.
 * consequently, it is not obviously clear if such an designation is the final exit point
-* please note the [[Engine interface proposal|http://lumiera.org/Lumiera/DesignProcess/EngineInterfaceOverview.html]]
+* please note the [[Engine interface proposal|http://lumiera.org/documentation/devel/rfc_pending/EngineInterfaceOverview.html]]
 * this introduces the notion of a ModelPort: //a point in the (high level) model where output can be produced//
+* thus obviously we need an OutputManager element to track the association of OutputDesignation to OutputSlot
 
 Do we get a single [[Fixture]] &mdash; guess yes
 
@@ -3193,30 +3379,62 @@ From the implementation side, the only interesting exit nodes are the ones to be
 * __playback__ always happens at a viewer element
 
 !Attaching and mapping of exit nodes
-[[Output designations|OutputDesignation]] are created by using a [[Pipe]]-ID and &mdash; at the same time &mdash; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of model ports as part of the current [[Fixture]]. A RenderProcess can be started to pull from these active exit points of a given timeline. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping relies on relative addressing of the output sinks, starting connections at the "first of each kind".
+Initially, [[Output designations|OutputDesignation]] are typically just local or relative references to another OutputDesignation; yet after some resolution steps, we'll arrive at an OutputDesignation //defined absolute.// Basically, these are equivalent to a [[Pipe]]-ID choosen as target for the connection and -- they become //real//&nbsp; by some object //claiming to root this pipe.// The applicability of this pattern is figured out dynamically while building the render network, resulting in a collection of [[model ports|ModelPort]] as part of the current [[Fixture]]. A RenderProcess can be started to pull from these -- and only from these -- active exit points of the model. Besides, when the timeline enclosing these model ports is [[connected to a viewer|ViewerPlayConnection]], an [[output network|OutputNetwork]] //is built to allow hooking exit points to the viewer component.// Both cases encompass a mapping of exit nodes to actual output channels. Usually, this mapping just relies on relative addressing of the output sinks, starting to allocate connections with the //first of each kind// (e.g. "connect to the first usable audio output destination").
 
-We should note that in both cases this mapping operation is controlled and driven by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format, again with fixed and pre-settled channel configuration (when configurting a render process, it might be necessary to account for //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
+We should note that in both cases this [[mapping operation|OutputMapping]] is controlled and driven and constrained by the output side of the connection: A viewer has fixed output capabilities, and rendering targets a specific container format -- again with fixed and pre-settled channel configuration ({{red{TODO 9/11}}} when configurting a render process, it might be necessary to pre-compute the //possible kinds of output streams,// so to provide a sensible pre-selection of possible output container formats for the user to select from). Thus, as a starting point, we'll create a default configured mapping, assigning channels in order. This mapping then should be exposed to modification and tweaking by the user. For rendering, this is part of the render options dialog, while in case of a viwer connection, a switch board is created to allow modifying the default mapping.
 
 !Connection to external outputs
-External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read, there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
+External output destinations are never addressed directly from within the model. This is an design decision. Rather, model parts connect to an OutputDesignation, and these in turn may be  [[connected to a viewer element|ViewerPlayConnection]]. At this point, related to the viewer element, there is a mapping to external destination(s): for images, a viewer typically has an implicit, natural destination (read: actually there is a corresponding viewer window or widget), while for sound we use an mapping rule, which could be overridden locally in the viewer.
 
-Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle timeouts gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
-&rarr; see also the PlayerDummy
+Any external output sink is managed as a [[slot|DisplayerSlot]] in the ~OutputManager. Such a slot can be opened and allocated for a playback process, which allows the latter to push calculated data frames to the output. Depending on the kind of output, there might be various, often tight requirements on the timed delivery of output data, but any details are abstracted away &mdash; any slot implementation provides a way to handle time-outs gracefully, e.g. by just showing the last video frame delivered, or by looping and fading sound
+&rarr; the OutputManager interface describes handling this mapping association
+&rarr; see also the PlayService
+
+!the global output manager
+Within the model routing is done mostly just by referring to an OutputDesignation -- but at some point finally we need to map these abstract designations to real output capabilities. This happens at the //output managing elements.// This interface, OutputManager, exposes these mappings of logical to real outputs and allows to  manage and control them. Several elements within the application, most notably the [[viewers|ViewerAsset]], provide an implementation of this interface -- yet there is one primary implementation, the ''global output manager'', known as OutputDirector. It can be accessed through the {{{Output}}} façade interface and is the final authority when it comes to allocating and mapping of real output possibilities. The OutputDirector tracks all the OutputSlot elements currently installed and available for output.
+
+The relation between the central OutputDirector and the peripheral OutputManager implementations is hierarchical. Because output slots are usually registered rather at some peripheral output manager implementation, a direct mapping from OutputDesignation (i.e. global pipe) to these slots is created foremost at that peripheral level. Resolving a global pipe into an output slot is the core concern of any OutputManager implementation. Thus, when there is a locally preconfigured mapping, like e.g. for a viewer's video master pipe to the output slot installed by the corresponding GUI viewer element, then this mapping will be picked up foremost to resolve the video master output.
+
+For a viewer widget in the GUI this yields exactly the expeted behaviour, but in other cases, e.g. for sound output, we need more general, more globally scoped output slots. In these cases, when a local mapping is absent, the query for output resolution is passed on up to the  OutputDirector, drawing on the collection of globally available output slots for that specific kind of media.
+{{red{open question 11/11: is it possible to retrieve a slot from another peripheral node?}}}
+
+!!!output modes
+Most output connections and drivers embody some kind of //operation mode:// Display is characterised by resolution and colour depth, sound by number of channels and sampling rate, amongst others. There might be a mismatch with the output expectations represented by [[output designations|OutputDesignation]] within the model. Nontheless we limit those actual operation modes strictly to the OutputManager realm. They should not leak out into the model within the session.
+In practice, this decision might turn out to be rather rigid, but some additional mechanisms allow for more flexibility
+* when [[connecting|ViewerPlayConnection]] timeline to viewer and output, stream type conversions may be added automatically or manually
+* since resolution of an output designation into an OutputSlot is initiated by querying an output manager, this query might include additional constraints, which //some// (not all) concrete output implementations might evaluate to provide an more suitably configured output slot variant.
 
-
+
+
The term &raquo;''Output Manager''&laquo; might denote two things: first, there is an {{{proc::play::OutputManager}}} interface, which can be exposed by several components within the application, most notably the [[viewer elements|ViewerAsset]]. And then, there is "the" global output manager, the OutputDirector, which finally tracks all registered OutputSlot elements and thus is the gatekeeper for any output leaving the application.
+
+&rarr; see [[output management overview|OutputManagement]]
+&rarr; see OutputSlot
+&rarr; see ViewerPlayConnection
+
+!Role of an output manager
+The output manager interface describes an entity handling two distinct concerns, tied together within a local //scope.//
+* a ''mapping'' to resolve a ModelPort (given as ~Pipe-ID) into an OutputSlot
+* the ''registration'' and management of possible output slots, thereby creating a preferred local mapping for future connections
+
+Note that an OutputSlot acts as a unit for registration and also for allocating / "opening" an output, while generally there might still be multiple physical outputs grouped into a single slot. This is relevant especially for sound output. A single slot is just the ability to allocate output ports up to a given limit (e.g. 2 for a stereo device, or 6 for a 5.1 device). These multiple channels are allways connected following a natural channel ordering. Thus the mapping is a simple 1:1 association from pipe to slot, assuming that the media types are compatible (and this has been checked already on a higher level).
+
+The //registration//&nbsp; of an output slot installs a functor or association rule, which later on allows to claim and connect up to a preconfigured number of channels. This allocation or usage of a slot is exclusive (i.e. only a single client at a time can allocate a slot, even if not using all the possible channels). Each output manager instance may or may not be configured with a //fall-back rule:// when no association or mapping can be established locally, the connection request might be passed down to the global OutputDirector. Again, we can expect this to be the standard behaviour for sound, while video likely will rather be handled locally, e.g. within a GUI widget (but we're not bound to configure it exactly this way)
+
+
+
An output mapping serves to //resolve//&nbsp; [[output designations|OutputDesignation]].
 
 !Mapping situations
 ;external outputs
 :external outputs aren't addressed directly. Rather we set up a default (channel) mapping, which then can be overridden by local rules.
-:Thus, in this case we query with an internal OutputDesignation as parameter and expect an OutputManager //slot//
+:Thus, in this case we query with an internal OutputDesignation as parameter and expect an OutputSlot
 ;viewer connections
 :any Timeline produces a number of [[model ports|ModelPort]]. On the other hand, any viewer exposes a small number of output designations, representing the image and sound output(s).
 :Thus, in this case we resolve similar to a bus connection, possibly overridden by already pre-existing or predefined connections.
 ;switch board
-:a viewer might receive multiple outputs and overlays, necessitating a user operated control to select what's actually to displayed
-:Thus, in this case we need a backwards resolution at the lower end of the output network, to connect to the model port as defined by the switchboard
+:a viewer might receive multiple outputs and overlays, necessitating a user operated control to select what's actually to be displayed
+:Thus, in this case we need a backwards resolution at the lower end of the output network, to connect to the model port as selected through the viewer's SwitchBoard
 ;global pipes or virtual media
 :when binding a Sequence as Timeline or VirtualClip, a mapping from output designations used within the Sequence to virtual channels or global pipes is required
 :Thus, in this case we need to associate output designations with ~Pipe-IDs encountered in the context according to some rules &mdash; again maybe overridden by pre-existing connections
@@ -3231,11 +3449,158 @@ All these mapping steps are listed here, because they exhibit a common pattern.
 * there is an //unconnected//&nbsp; state.
 
 !Implementation notes
-Thus the mapping is a copyable value object, based on a associative array. It may be attached to a model object and persisted alongside. The mapping is assumed to run a defaults query when necessary. To allow for that, it should be configured with a query template (string). Frequently, special //default pipe// markers will be used at places, where no distinct pipe-ID is explicitly specified. Besides that, invocations might supply additional predicates (e.g. {{{ord(2)}}} to denote "the second stream of this kind") to hint the defaults resolution. Moreover, the mapping needs a way to retrieve the set of possible results, allowing to filter the results of the rules based default. Mappings might be defined explicitly. Instead of storing a //bottom value,// an {{{isDefined()}}} predicate might be preferable.
+Thus the mapping is a copyable value object, using an associative array. It may be attached to a model object and persisted alongside. The mapping is assumed to run a defaults query when necessary. To allow for that, it should be configured with a query template (string). Frequently, special //default pipe// markers will be used at places where no distinct pipe-ID is specified explicitly. Besides that, invocations might supply additional predicates (e.g. {{{ord(2)}}} to point at "the second stream of this kind") thereby hinting the defaults resolution. Moreover, the mapping needs a way to retrieve the set of possible results, allowing to filter the results of the rules based default. Mappings might be defined explicitly. Instead of storing a //bottom value,// an {{{isDefined()}}} predicate might be preferable.
 
 First and foremost, mapping can be seen as a //functional abstraction.// As it's used at implementation level, encapsulation of detail types in't the primary concern, so it's a candidate for generic programming: For each of those use cases outlined above, a distinct mapping type is created by instantiating the {{{OutputMapping<DEF>}}} template with a specifically tailored definition context ({{{DEF}}}), which takes on the role of a strategy. Individual instances of this concrete mapping type may be default created and copied freely. This instantiation process includes picking up the concrete result type and building a functor object for resolving on the fly. Thus, in the way typical for generic programming, the more involved special details are moved out of sight, while being still in scope for the purpose of inlining. But there //is// a concern better to be encapsulated and concealed at the usage site, namely accessing the rules system. Thus mapping leads itself to the frequently used implementation pattern where there is a generic frontend as header, calling into opaque functions embedded within a separate compilation unit.
 
+
+
Within the Lumiera player and output subsystem, actually sending data to an external output requires to allocate an ''output slot''
+This is the central metaphor for the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
+
+!Properties of an output slot
+Each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output, or a group of such outputs (e.g. left and right soundcard output channels), or an output file or similar capability accepting media content. First off, an output slot needs to be provided, configured and registered, using an implementation for the kind of media data to be output (sound, video) and the special circumstances of the output capability (render a file, display video in a GUI widget, send video to a full screen display, establish a Jack port, just use some kind of "sound out"). An output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels (stereoscopic video, multichannel sound).
+
+In order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client. At any time, there may be only a single client using a given output slot this way. To stress this point: output slots don't provide any kind of inherent mixing capability; any adaptation, mixing, overlaying and sharing needs to be done within the nodes network producing the output data fed to the slot. (in special cases, some external output capabilities -- e.g. the Jack audio connection system -- may still provide additional mixing capabilities, but that's beyond the scope of the Lumiera application)
+
+[>img[Outputslot implementation structures|uml/fig151685.png]]
+Once allocated, the output slot returns a set of concrete ''sink handles'' (one for each physical channel expecting data). The calculating process feeds its results into those handles. Size and other characteristics of the data frames are assumed to be suitable, which typically won't be verified at that level anymore (but the sink handle provides a hook for assertions). Besides that, the allocation of an output slot reveals detailed ''timing expectations''. The client is required to comply to these timings when ''emitting'' data -- he's even required to provide a //current time specification,// alongside with the data. Based on this information, the output slot has the ability to handle timing failures gracefully; the concrete output slot implementation is expected to provide some kind of de-click or de-flicker facility, which kicks in automatically when a timing failure is detected.
+
+!!!usage and implementation
+Clients retrieve just a reference to an output slot by asking a suitable OutputManager for an output possibility supporting a specific format. Usually, they just "claim" this slot by invoking {{{allocate()}}}, which behind the scenes causes building of the actual output connections and mechanisms. For each such connection -- corresponding to a single channel within the media format handled by this ~OutputSlot -- the client gets a smart-handle {{{DataSink}}}. The concrete ~OutputSlot implementation performs operations quite specific to the kind of output and external interface in question. All tese specific handling is embodied within the concrete connection implementation used by the concrete ~OutputSlot
+
+!!!timing expectations
+Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These [[Timings]] are detailed and explicit, including a grid of deadlines for each frame to deliver, plus a fixed //latency.// Within this context, &raquo;latency&laquo; means the requirement to be ahead of the nominal time by a certain amount, to compensate for the processing time necessary to propagate the media to the physical output pin. The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
+
+The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is assumed to be a job running within the engine scheduler and thus can be configured to run //after// another job has finished, and to run within certain time limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
+
+!!!output modes
+some concrete output connections and drivers embody a specific operation mode (e.g. sample rate or number of channels). The decision and setup of these operational configuration is initiated together with the [[resolution|OutputMapping]] of an OutputDesignation within the OutputManager, finally leading to an output slot (reference), which can be assumed to be suitably configured, before the client allocates this slot for active use. Moreover, an individual output sink (corresponding to a single channel) may just remain unused -- until there is an {{{emit()}}} call and successful data handover, this channel will just feature silence or remain black. (More flexible system, e.g. Jack, allow to generate an arbitrary number of output pins -- Lumiera will support this by allowing to set up additional output slots and attach this information to the current session &rarr; SessionConfigurationAttachment)
+
+!!!Lifecycle and storage
+The concrete OutputSlot implementation is owned and managed by the facility actually providing the output possibility in question. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. The associated OutputSlot implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be de-registered explicitly prior to destruction -- and this deregistration may block until all clients using this slot did terminate. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferably just emitting a signal (callback functor).
+{{red{TODO 7/11: Deregistration is an unsolved problem....}}}
+
+!!!unified data exchange cycle
+The planned delivery time of a frame is used as an ID throughout that cycle
+# within a defined time window prior to delivery, the client can ''allocate and retrieve the buffer'' from the BufferProvider.
+# the client has to ''emit'' within a (short) time window prior to deadline
+# now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.
+
+!!!lapses
+This data exchange protocol operates on a rather low level; there is only limited protection against timing glitches
+|  !step|!problem ||!consequences | !protection |
+| (1)|out of time window ||interference with previous/later use of the buffer | prevent in scheduler! |
+|~|does not happen ||harmless as such | emit ignored |
+|~|buffer unavailable ||inhibits further operation | ↯ |
+| (2)|out of time window ||harmless as such | emit ignored |
+|~|out of order ||allowed, unless out of time | -- |
+|~|does not happen ||frame treated as glitch | -- |
+|~|buffer unallocated ||frame treated as glitch | emit ignored |
+| (3)|emit missing ||frame treated as glitch | -- |
+|~|fail to release buffer ||unable to use buffer further | mark unavailable |
+|~|buffer unavailable ||serious malfunction of playback | request playback stop |
+
+Thus there are two serious problem situations
+* allocating the buffer out of time window bears the danger of output data corruption; but the general assumption is for the scheduler to ensure each job start time remains within the defined window and all prerequisite jobs have terminated successfully. To handle clean-up, we additionally need special jobs running always, in order, and be notified of prerequisite failures.
+* failure to release a buffer in a timely fashion blocks any further use of that buffer, any further jobs in need of that buffer will die immediately. This situation can only be caused by a serious problem //within the slot, related to the output mechanism.// Thus there should be some kind of trigger (e.g. when this happens 2 times consecutively) to request aborting the playback or render as a whole.
+&rarr; SchedulerRequirements
+&rarr; OutputSlotDesign
+&rarr; OutputSlotImpl
+
+
+
+
The OutputSlot interface describes a point where generated media data can actually be sent to the external world. It is expected to be implemented by adapters and bridges to existing drivers or external interface libraries, like a viewer widget in the GUI, ALSA or Jack sound output, rendering to file, using an external media format library. The design of this core facility was rather difficult and stretched out over quite some time span -- this page documents the considerations and the decisions taken.
+
+!intention
+OutpotSlot is a metaphor to unify the organisation of actual (system level) outputs; using this concept allows to separate and abstract the data calculation and the organisation of playback and rendering from the specifics of the actual output sink. Actual output possibilities (video in GUI window, video fullscreen, sound, Jack, rendering to file) can be added and removed dynamically from various components (backend, GUI), all using the same resolution and mapping mechanisms (&rarr; OutputManagement)
+
+!design possibilities
+!!properties as a starting point
+* each OutputSlot is an unique and distinguishable entity. It corresponds explicitly to an external output
+* an output slot needs to be provided, configured and registered, using an implementation specifically tailored for the kind of media data
+* an output slot is always limited to a single kind of media, and to a single connection unit, but this connection may still be comprised of multiple channels.
+* in order to be usable as //output sink,// an output slot needs to be //allocated,// i.e. tied to and locked for a specific client.
+* this allocation is exclusive: at any time, there may be only a single client using a given output slot.
+* the calculating process feeds its results into //sink handles//&nbsp; provided by the allocated output slot.
+* allocation of an output slot leads to very specific [[timing expectations|Timings]]
+* the client is required to comply to these timings and operate according to a strictly defined protocol.
+* timing glitches will be detected due to this protocol; the output slot provides mechanisms for failing gracefully in these cases
+
+!!data exchange models
+Data is handed over by the client invoking an {{{emit(time,...)}}} function on the sink handle. Theoretically there are two different models how this data hand-over might be performed. This corresponds to the fact, that in some cases our own code manages the output and the buffers, while in other situations we intend to use existing library solutions or even external server applications to handle output
+;buffer handover model
+:the client owns the data buffer and cares for allocation and de-allocation. The {{{emit()}}}-call just propagates a pointer to the buffer holding the data ready for output. The output slot implementation in turn has the liability to copy or otherwise use this data within a given time limit.
+;shared buffer model
+:here the output mechanism owns the buffer. Within a certain time window prior to the expected time of the {{{emit()}}}-call, the client may obtain this buffer (pointer) to fill in the data. The slot implementation won't touch this buffer until the {{{emit()}}} handover, which in this case just provides the time and signalles the client is done with that buffer. If the data emitting handshake doesn't happen at all, it counts as late and superseded by the next handshake.
+
+!!relation to timing
+Besides the sink handles, allocation of an output slot defines some timing constraints, which are binding for the client. These include a grid of deadlines for each frame to deliver, plus a fixed //latency.// The output slot implementation itself is bound by external constraints to deliver data at a fixed framerate and aligned to an externally defined timing grid, plus the data needs to be handed over ahead of these time points by an time amount given by the latency. Depending on the data exchange model, there is an additional time window limiting the buffer management.
+
+The assumption is for the client to have elaborate timing capabilities at his disposal. More specifically, the client is assumed to be a job running within the engine scheduler and thus can be configured to run //after// another job has finished, and to run within certain time limits. Thus the client is able to provide a //current nominal time// -- which is suitably close to the actual wall clock time. The output slot implementation can be written such as to work out from this time specification if the call is timely or overdue -- and react accordingly.
+
+{{red{TODO 6/11}}}in this spec, both data exchange models exhibit a weakness regarding the releasing of buffers. At which time is it safe to release a buffer, when the handover didn't happen? Do we need an explicit callback, and how could this callback be triggered? This is similar to the problem of closing a network connection, i.e. the problem is generally unsolvable, but can be handled pragmatically within certain limits.
+{{red{WIP 11/11}}}meanwhile I've worked out the BufferProvider interface in deail. There's now a deatiled buffer handover protocol defined, which is supported by a little state engine tracking BufferMetadata. This mechanism provides a transition to {{{BLOCKED}}} state when order or timing constraints are being violated, which practically solves this problem. How to detect and resolve such a floundered state from the engine point of view still remains to be addressed.
+
+!!!Lifecycle and storage
+The concrete OutputSlot implementation is owned and managed by the facility actually providing the output possibility in question. For example, the GUI provides viewer widgets, while some sound output backend provides sound ports. The associated OutputSlot implementation object is required to stay alive as long as it's registered with some OutputManager. It needs to be de-registered explicitly prior to destruction -- and this deregistration may block until all clients using this slot did terminate. Beyond that, an output slot implementation is expected to handle all kinds of failures gracefully -- preferably just emitting a signal (callback functor).
+{{red{TODO 7/11: Deregistration is an unsolved problem....}}}
+
+-----
+!Implementation / design problems
+How to handle the selection of those two data exchange models!
+-- Problem is, typically this choice isn't up to the client; rather, the concrete OutputSlot implementation dictates what model to use. But, as it stands, the client needs to cooperate and behave differently to a certain degree. Unless we manage to factor out an common denominator of both models.
+
+Thus: Client gets an {{{OutputSlot&}}}, without knowing the exact implementation type &rArr; how can the client pick up the right strategy?
+Solving this problem through //generic programming// -- i.e coding both cases effectively different, but similar -- would require to provide both implementation options already at //compile time!//
+
+{{red{currently}}} I see two possible, yet quite different approaches...
+;generic
+:when creating individual jobs, we utilise a //factory obtained from the output slot.//
+;unified
+:extend and adapt the protocol such to make both models similar; concentrate all differences //within a separate buffer provider.//
+!!!discussion
+the generic approach looks as it's becoming rather convoluted in practice. We'd need to hand over additional parameters to the factory, which passes them through to the actual job implementation created. And there would be a coupling between slot and job (the slot is aware it's going to be used by a job, and even provides the implementation). Obviously, a benefit is that the actual code path executed within the job is without indirections, and all written down in a single location. Another benefit is the possibility to extend this approach to cover further buffer handling models -- it doesn't pose any requirements on the structure of the buffer handling --
+On the other hand, if we accept to retrieve the buffer(s) via an indirection, which we kind of do anyway //within the render node implementation// -- the unified model looks more like a clean solution. It's more like doing away with some local optimisations possible if we handle the models explicitly, so it's not much of a loss, given that the majority of the processing time will be spent within the inner pixel calculation loops for frame processing anyway. When following this approach, the BufferProvider becomes a third, independent partner, and the slot cooperates tightly with this buffer provider, while the client (processing node) still just talks to the slot. Basically, this unified solution works like extending the shared buffer model to both cases.
+&rArr; __conclusion__: go for the unified approach!
+
+!!!unified data exchange cycle
+The planned delivery time of a frame is used as an ID throughout that cycle
+# within a defined time window prior to delivery, the client can ''allocate and retrieve the buffer'' from the BufferProvider.
+# the client has to ''emit'' within a (short) time window prior to deadline
+# now the slot gets exclusive access to the buffer for output, signalling the buffer release to the buffer provider when done.
+
+&rarr; OutputSlotImpl
+
+
+
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot will be outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
+
+!operation steps
+[>img[Sequenz of output data exchange steps|uml/fig145157.png]]The OutputSlot class defines some standard operations as protected virtual functions. These represent the actual steps in the data exchange protocol corresponding to this output slot.
+;lock()
+:claims the specified buffer for exclusive use.
+:Client may now dispose output data
+;transfer()
+:transfers control from the client to the actual output connection for feeding data.
+:Triggered by the client invoking {{{emit()}}}
+;pushout()
+:request the actual implementation to push the data to output.
+:Usually invoked from the {{{transfer()}}} implementation, alternatively from a separate thread.
+
+
+!buffer states
+While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the server side (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
+|        !state||>| !lock()   |>| !transfer() |>| !pushout() |
+|     {{{NIL}}}||↯| ·        |↯|             | ↯ |          |
+|    {{{FREE}}}||✔|↷ locked  |✔|· (ignore)  | · | ·         |
+|  {{{LOCKED}}}||↯|↷ emitted |✔|↷ emitted   | · |↷ free    |
+| {{{EMITTED}}}||↯|↷ blocked |↯|↷ blocked   | · |↷ free    |
+| {{{BLOCKED}}}||↯| ✗        |↯| ✗          | ∅ |↷ free    |
+where · means no operation, ✔ marks the standard cases (OK response to caller), ↯ will throw and thus kill the calling job, ∅ will treat this frame as //glitch,// ✗ will request playback stop.
+The rationale is for all states out-of-order to transition into the {{{BLOCKED}}}-state eventually, which, when hit by the next operation, will request playback stop.
+
+
The Lumiera Processing Layer is comprised of various subsystems and can be separated into a low-level and a high-level part. At the low-level end is the [[Render Engine|OverviewRenderEngine]] which basically is a network of render nodes cooperating closely with the Backend Layer in order to carry out the actual playback and media transforming calculations. Whereas on the high-level side we find several different [[Media Objects|MObjects]] that can be placed into the session, edited and manipulated. This is complemented by the [[Asset Management|Asset]], which is the "bookkeeping view" of all the different "things" within each [[Session|SessionOverview]].
 
@@ -3246,7 +3611,7 @@ There is rather strong separation between these two levels, and &mdash; <
 [img[Block Diagram|uml/fig128005.png]]
 
-
+
Render Engine, [[Builder]] and [[Controller]] are closely related Subsystems. Actually, the [[Builder]] //creates// a newly configured Render Engine //for every// RenderProcess. Before doing so, it queries from the Session (or, to be more precise, from the [[Fixture]] within the current Session) all necessary Media Object Placement information. The [[Builder]] then derives from this information the actual assembly of [[Processing Nodes|ProcNode]] comprising the Render Engine. Thus:
  * the source of the build process is a sequence of absolute (explicit) [[Placements|Placement]] called the [[Playlist]]
  * the [[build process|BuildProcess]] is driven, configured and controlled by the [[Controller]] subsystem component. It encompasses the actual playback configuration and State of the System.
@@ -3254,6 +3619,7 @@ There is rather strong separation between these two levels, and &mdash; <
 
 see also: RenderEntities, [[two Examples (Object diagrams)|Examples]] 
 
+{{red{TODO: adjust terminology in this drawing: "Playlist" &rarr; "Fixture" and "Graph" &rarr; "Segment"}}}
 [img[Overview: Components of the Renderengine|uml/fig128261.png]]
 
@@ -3893,7 +4259,7 @@ Placements have //value semantics,// i.e. we don't stress the identity of a plac
-
+
An implementation facility used to keep track of individual Placements and their relations.
 Especially, the [[Session]] maintains such an index, allowing to use the (opaque) PlacementRef tags for referring to a specific "instance" of an MObject, //placed// in a unique way into the current session. And, moreover, this index allows for one placement referring to another placement, so to implement a //relative// placement mode. Because there is an index behind the scenes, it is possible to actually access such a referral in the reverse direction, which is necessary for implementing the desired placement behaviour (if an object instance used as anchor is moved, all objects placed relatively to it have to move accordingly, which necessitates finding those other objects).
 
@@ -3907,13 +4273,13 @@ Using a ''flat hashtable'' allows to access a Placement denoted by ID in O(1). T
 * allowing to create a path "up" from this scope, which is used for resolving any queries
 * (maybe/planned) relations to other placements
 
-Alternatively, we could try to use a ''structure based index'', thereby avoiding the mentioned description record by folding any of the contained informations into the surrounding data structure:
+Alternatively, we could try to use a ''structure based index'', thereby avoiding the mentioned description record by folding any of the contained information into the surrounding data structure:
 * the scope would be obvious from the index, resp. from the path used to resolve this index
-* any other informations, especially the relations, would be folded into the placement
+* any other information, especially the relations, would be folded into the placement
 * this way, the "index" could be reduced to being the session data structure itself.
 
 //does a placement need to know it's own ID?//&nbsp; Obviously, there needs to be a way to find out the ID for a given placement, especially in the following situations:
-* for most of the operations above, when querying additional informations from index for a given placement
+* for most of the operations above, when querying additional information from index for a given placement
 * to create a PlacementRef (this is a variant of the "shared ptr from this"-problem)
 On second sight, this problem turns out to be more involved, because either we have to keep a second index table for the reverse lookup (memory address -> ID), or have to tie the placement by a back-link when adding it to the index/session data structure, or (alternatively) it forces us to store a copy of the ID //within// the placement itself. The last possibility seems to create the least impact; but implementing it this way effectively gears the implementation towards a hashtable based approach.
 
@@ -4094,31 +4460,61 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T
 <<tasksum end>>
 
-
+
With //play process//&nbsp; we denote an ongoing effort to calculate a stream of frames for playback or rendering.
-The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls dispatched as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
+The play process is an conceptual entity linking together several activities in the [[Backend]] and the RenderEngine. Creating a play process is the central service provided by the [[player subsystem|Player]]: it maintains a registration entry for the process to keep track of associated entities, resources allocated and calls [[dispatched|FrameDispatcher]] as a consequence, and it wires and exposes a PlayController to serve as an interface and information hub.
 
-''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this stream of calculations happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical.
-* the [[Backend]] is responsible for scheduling and dispatching the calculations
-* the RenderEngine has the ability to cary out individual frame calculations
-* the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery
+''Note'': the player is in no way engaged in any of the actual calculation and management tasks necessary to make this [[stream of calculations|CalcStream]] happen. The play process code contained within the player subsystem is largely comprised of organisational concerns and not especially performance critical. +* the [[Backend]] is responsible for [[dispatching|Dispatcher]] the [[calculation stream|CalcStream]] and scheduling individual calculation jobs +* the RenderEngine has the ability to carry out individual frame calculations +* the OutputSlot exposed by the [[output manager|OutputManagement]] is responsible for accepting timed frame delivery + +[>img[Anatomy of a Play Process|uml/fig144005.png]] +!Anatomy of a Play Process +The Controller is exposed to the client and acts as frontend handle, while the play process body groups and manages all the various parts cooperating to generate output. For each of the participating global pipes we get a feed to drive that pipeline to deliver media of a specific kind. + +Right within the play process, there is a separation into two realms, relying on different programming paradigms. Obviously the play controller is a state machine, and similarily the body object (play process) has a distinct operation state. Moreover, the current collection of individual objects hooked up at any given instance is a stateful variable. To the contrary, when we enter the realm of actual processing, operations are carried out in parallel, relying on stateless descriptor objects, wired into individual calculation jobs, to be scheduled as non-blocking units of operation. For each series of consecutive frames to be calculated, there is a descriptor object, the CalcStream, which also links to a specificaly tailored dispatcher table, allowing to schedule the individual frame jobs. Whenever the controller determines a change in the playback plan (speed change, skip, scrubbing, looping, ...), a new CalcStream is created, while the existing one is just used to mark any not-yet processed job as superseded. +
-
-
Within Lumiera, &raquo;Player&laquo; denotes the [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
+
+
The [[Player]] is an independent [[Subsystem]] within Lumiera, located at Proc-Layer level. A more precise term would be "rendering and playback coordination subsystem". It provides the capability to generate media data, based on a high-level model object, and send this generated data to an OutputDesignation, creating an continuous and timing controlled output stream. Clients may utilise these functionality through the ''play service'' interface.
+
+!provided services
+* creating a PlayProcess
+* managing existing play processes
+* convenience short-cuts for //performing//&nbsp; several kinds of high-level model objects
+
+!creating a play process
+This is the core service provided by the player subsystem. The purpose is to create a collaboration between several entities, creating media output
+;data producers
+:a set of ModelPort elements to ''pull'' for generating output. They can either be handed in direcly, or resolved from a set of OutputDesignation elements
+;data sinks
+:to be able to create output, the PlayProcess needs to cooperate with [[output slots|OutputSlot]]
+:physical outputs are never handled directly, rather, the playback or rendering needs an OutputManager to resolve the output designations into output slots
+;controller
+:when provided with these two prerequisites, the play service is able to build a PlayProcess.
+:for clients, this process can be accessed and maintained through a PlayController, which acts as (copyable) handle and front-end.
+;engine
+:the actual processing is done by the RenderEngine, which in itself is a compound of several services within [[Backend]] and Proc-Layer
+:any details of this processing remain opaque for the clients; even the player subsystem just accesses the EngineFaçade
+
+
+
+
Within Lumiera, &raquo;Player&laquo; is the name for a [[Subsystem]] responsible for organising and tracking //ongoing playback and render processes.// &rarr; [[PlayProcess]]
 The player subsystem does not perform or even manage any render operations, nor does it handle the outputs directly.
-Yet it adresses some central concerns:
+Yet it addresses some central concerns:
 
 ;uniformity
 :all playback and render processes are on equal footing, handled in a similar way.
 ;integration
 :the player cares for the necessary integration with the other subsystems
-:it consults the OutputManagement, retrieves the necessary informations from the [[Session]] and coordinates [[Backend]] calls.
+:it consults the OutputManagement, retrieves the necessary information from the [[Session]] and coordinates the forwarding of [[Backend]] calls.
 ;time quantisation
 :the player translates continuous time values into discrete frame counts.
 :to perform this [[quantisation|TimeQuant]], the help of the session for building a TimeGrid for each output channel is required.
 
-!{{red{WIP 12/10}}} under construction
-The player subsystem is currently about to be designed and built up; some time ago, __Joel Holdsworth__ and __Ichthyo__ did a design study with a PlayerDummy, which is currently hooked up with the TransportControl in the Lumiera GUI.
+!{{red{WIP 5/2011}}} under construction
+The player subsystem is currently about to be designed and built up; some time ago, __Joel Holdsworth__ and __Ichthyo__ did a design study with a PlayerDummy, which is currently hooked up with the TransportControl in the Lumiera GUI. Starting from these experiences, and the general requirements of an NLE, the [[design of the Player subsystem|DesignPlayerSubsystem]] is being worked out.
 
@@ -4252,7 +4648,7 @@ The system is ''open'' inasmuch every part mirrors the structure of correspondin &rarr; see RenderProcess
-
+
This special type of [[structural Asset|StructAsset]] represents information how to build some part of the render engine's processing nodes network. Processing patterns can be thought of as a blueprint or micro program for construction. Most notably, they are used for creating nodes reading, decoding and delivering source media material to the render network, and they are used for building the output connection via faders, summation or overlay nodes to the global pipes (busses). Each [[media Asset|MediaAsset]] has associated processing patterns describing the codecs and other transformations needed to get at the media data of this asset. (and because media assets are typically compound objects, the referred ~ProcPatt will be compound too). Similarily, for each stream kind, we can retrieve a processing pattern for making output connections. Obviously, the possibilities opened by using processing patterns go far beyond.
 
 Technically, a processing pattern is a list of building instructions, which will be //executed// by the [[Builder]] on the render node network under construction. This implies the possibility to define further instruction kinds when needed in future; at the moment the relevant sorts of instructions are
@@ -4266,7 +4662,7 @@ Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming s
 The basic working set of processing patterns can be expected to be just there (hard wired or default configuration, mechanism for creating an sensible fallback). Besides, the idea is that new processing patterns can be added via rules in the session and then referred to by other rules controlling the build process. Any processing pattern is assembled by adding individual build instructions, or by including another (nested) processing pattern.
 
 !!retrieving a suitable Processing Pattern
-For a given situation, the necessary ProcPatt can be retrieved by issuing a [[configuration query|ConfigQuery]]. This query should include the needed capabilities in predicate form (technically this query is a Prolog goal), but it can leave out informations by just requesting "the default" &rarr; see DefaultsManagement
+For a given situation, the necessary ProcPatt can be retrieved by issuing a [[configuration query|ConfigQuery]]. This query should include the needed capabilities in predicate form (technically this query is a Prolog goal), but it can leave out some pieces of information by just requesting "the default" &rarr; see DefaultsManagement
 
 !!how does this actually work?
 Any processing pattern needs the help of a passive holder tool suited for a specific [[building situation|BuilderPrimitives]]; we call these holder tools [[building moulds|BuilderMould]]. Depending on the situation, the mould has been armed up by the builder with the involved objects to be connected and extended. So, just by issuing the //location ID// defined within the individual build instruction (in most cases simply {{{"current"}}}), the processing pattern can retrieve the actual render object to use for building from the mould it is executed in.
@@ -4682,10 +5078,10 @@ config.macros.rssFeedUpdate = {
 //}}}
 
-
+
What is the Role of the asset::Clip and how exactly are Assets and (Clip)-MObjects related?
 
-First of all: ~MObjects are the dynamic/editing/manipulation view, while Assets are the static/bookkeeping/searching/information view of the same entities. Thus, the asset::Clip contains the general configuration, the ref to the media and descriptive properties, while all parameters being "manipulated" belong to the session::Clip (MObject). Besides that, the practical purpose of asset::Clip is that you can save and remember some selection as a Clip (Asset), maybe even attach some informations or markup to it, and later be able to (re)create a editable representation in the Session (the GUI could implement this by allowing to drag from the asset::Clip GUI representation to the timeline window)
+First of all: ~MObjects are the dynamic/editing/manipulation view, while Assets are the static/bookkeeping/searching/information view of the same entities. Thus, the asset::Clip contains the general configuration, the ref to the media and descriptive properties, while all parameters being "manipulated" belong to the session::Clip (MObject). Besides that, the practical purpose of asset::Clip is that you can save and remember some selection as a Clip (Asset), maybe even attach some information or markup to it, and later be able to (re)create a editable representation in the Session (the GUI could implement this by allowing to drag from the asset::Clip GUI representation to the timeline window)
 
 !!dependencies
 The session::Clip (frequently called "clip-MO", i.e. the MObject) //depends on the Asset.// It can't exist without the Asset, because the Asset is needed for rendering. The other direction is different: the asset::Clip knows that there is a dependant clip-MO, there could be //at most one// such clip-MO depending on the Asset, but the Asset can exist without the clip-MO (it gives the possibility to re-create the clip-MO).
@@ -4702,28 +5098,32 @@ The link between ~MObject and Asset should be {{{const}}}, so the clip can't cha
 At first sight the link between asset and clip-MO is a simple logical relation between entities, but it is not strictly 1:1 because typical media are [[multichannel|MultichannelMedia]]. Even if the media is compound, there is //only one asset::Clip//, because in the logical view we have only one "clip-thing". On the other hand, in the session, we have a compound clip ~MObject comprised of several elementary clip objects, each of which will refer to its own sub-media (channel) within the compound media (and don't forget, this structure can be tree-like)
 {{red{open question:}}} do the clip-MO's of the individual channels refer directly to asset::Media? does this mean the relation is different from the top level, where we have a relation to a asset::Clip??
-
+
Conceptually, the Render Engine is the core of the application. But &mdash; surprisingly &mdash; we don't even have a distinct »~RenderEngine« component in our design. Rather, the engine is formed by the cooperation of several components spread over two layers (Backend and Proc-Layer): The [[Builder]] creates a network of [[render nodes|ProcNode]], which is used by the Backend to pull individual Frames.
 &rarr; OverviewRenderEngine
+&rarr; EngineFaçade 
 
-
+
The [[Render Engine|Rendering]] only carries out the low-level and performance critical tasks. All configuration and decision concerns are to be handled by [[Builder]] and [[Controller]]. While the actual connection of the Render Nodes can be highly complex, basically each Segment of the Timeline with uniform characteristics is handled by one Processor, which is a graph of [[Processing Nodes|ProcNode]] discharging into a ExitNode. The Render Engine Components as such are //stateless// themselves; for the actual calculations they are combined with a StateProxy object generated by and connected internally to the [[Controller]], while at the same time holding the Data Buffers (Frames) for the actual calculations.
 
+{{red{Warning: obsolete as of 9/11}}}
+Currently the Render/Playback is beeing targetted for implementation; almost everything in this diagram will be implemented in a slightly differently way....
 [img[Entities comprising the Render Engine|uml/fig128389.png]]
 
-
+
Below are some notes regarding details of the actual implementation of the render process and processing node operation. In the description of the [[render node operation protocol|NodeOperationProtocol]] and the [[mechanics of the render process|RenderMechanics]], these details were left out deliberately.
+{{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
 !Layered structure of State
 State can be seen as structured like an onion. All the [[StateAdapter]]s in one call stack are supposed to be within one layer: they all know of a "current state", which in turn is a StateProxy (and thus may refer yet to another state, maybe accros the network or in the backend or whatever). The actual {{{process()}}} function "within" the individual nodes just sees a single StateAdapter and thus can be thought to be a layer below.
 
 !Buffer identification
-For the purpose of node operation, Buffers are identified by a //Buffer-handle,// which contains both the actual buffer pointer and an internal indes and classification of the source providing the buffer; the latter information is used for deallocation. Especially for calling the {{{process()}}} function (which is supposed to be plain C) the respective StateAdapter provides an array containing just the output and input buffer pointers
+For the purpose of node operation, Buffers are identified by a [[buffer-handle|BuffHandle]], which contains both the actual buffer pointer and an internal indes and classification of the source providing the buffer; the latter information is used for deallocation. Especially for calling the {{{process()}}} function (which is supposed to be plain C) the node invocation needs to prepare and provide an array containing just the output and input buffer pointers. Typically, this //frame pointer array//&nbsp; is allocated on the call stack.
 
 !Problem of multi-channel nodes
-Some data processors simply require to work on multiple channels simultanously, while others work just on a single channel and will be replicated by the builder for each channel invoved. Thus, we are struck with the nasty situation that the node graph may go through some nodes spanning the chain of several channels. Now the decision is //not to care for this complexity within a single chain calculating a single channel.// We rely solely on the cache to avoid duplicated calculations. When a given node happens to produce multiple output buffers, we are bound to allocate them for the purpose of this nodes {{{process()}}} call, but we just "let go" the buffers not needed immediately for the channel acutally to be processed. For this to work, it is supposed that the builder has wired in a caching, and that the cache will hit when we touch the same node again for the other channels.
+Some data processors simply require to work on multiple channels simultanously, while others work just on a single channel and will be replicated by the builder for each channel invoved. Thus, we are struck with the nasty situation that the node graph may go through some nodes spanning the chain of several channels. Now the decision is //not to care for this complexity within a single chain calculating a single channel.// We rely solely on the cache to avoid duplicated calculations. When a given node happens to produce multiple output buffers, we are bound to allocate them for the purpose of this node's {{{process()}}} call, but afterwards we'r just "letting go", releasing the buffers not needed immediately for the channel acutally to be processed. For this to work, it is supposed that the builder has wired in a caching, and that the cache will hit when we touch the same node again for the other channels.
 
 Closely related to this is the problem how to number and identify nodes and thus to be able to find calculated frames in cache (&rarr; [[here|NodeFrameNumbering]])
 
@@ -4736,23 +5136,24 @@ Every node is actually decomposed into three parts
 Thus, the outer container can be changed polymorphically to support the different kinds of nodes (large-scale view). The actual wiring of the nodes is contained in the WiringDescriptor, including the {{{process()}}} function pointer. Additionally, this WiringDescriptor knows the actual type of the operation Strategy, and this actual type has been chosen by the builder such as to select details of the desired operation of this node, for example caching / no caching or maybe ~OpenGL rendering or the special case of a node pulling directly from a source reader. Most of this configuration is done by selecting the right template specialisation within the builder; thus in the critical path most of the calls can be inlined
 
 !!!! composing the actual operation Strategy
-As shown in the class diagram to the right, the actual implementation is assembled by chaining together the various policy classes governing parts of the node operation, like Caching, in-Place calculation capability, etc. (&rarr; see [[here|WiringDescriptor]] for details). The rationale is that the variable part of the Invocation data is allocated at runtime directly on the stack, while a precisely tailored call sequence for "calculating the predecessor nodes" can be defined out of a bunch of simple building blocks. This helps avoiding "spaghetti code", which would be especially dangerous because of the large number of different execution paths to get right. Additionally, a nice side effect of this implementation technique is that a good deal of the implementation is eligible to inlining.
-We //do employ//&nbsp; some virtual calls for the buffer management in order to avoid coupling the policy classes to the actual number of in/out buffers. (As of 6/2008, this is mainly a precaution to be able to control the number of generated template instances. If we ever get in the region of several hundred individual specialisations, we'd need to separate out the allocation of the "buffer table" into a hand-made stack-like buffer allocated from the heap.)
+As shown in the class diagram to the right, the actual implementation is assembled by chaining together the various policy classes governing parts of the node operation, like Caching, in-Place calculation capability, etc. (&rarr; see [[here|WiringDescriptor]] for details). The rationale is that the variable part of the Invocation data is allocated at runtime directly on the stack, while a precisely tailored call sequence for "calculating the predecessor nodes" can be defined out of a bunch of simple building blocks. This helps avoiding "spaghetti code", which would be especially dangerous and difficult to get right because of the large number of different execution paths. Additionally, a nice side effect of this implementation technique is that a good deal of the implementation is eligible to inlining.
+We //do employ//&nbsp; some virtual calls for the buffer management in order to avoid coupling the policy classes to the actual number of in/out buffers. (As of 6/2008, this is mainly a precaution to be able to control the number of generated template instances. If we ever get in the region of several hundred individual specialisations, we'd need to separate out further variable parts to be invoked through virtual functions.)
 
 !Rules for buffer allocation and freeing
 * only output buffers are allocated. It is //never necessary//&nbsp; to allocate input buffers!
 * buffers are to be allocated as late as possible, typically just before invoking {{{process()}}}
-* buffers are allways allocated by calling to the preceeding StateAdapter in the callstack ("parent stae"), because of the possibility of writing the result to cache.
-* {{{pull()}}} returns a handle for the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StaeProxy.
-* any other buffers filled with results in the course of the same {{{process()}}} call can be released immediately before returning from the {{{pull()}}}
-* similar, and input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
-* buffers are allways released by calling to the "current state" (which is a StateProxy), providing the buffer-ID to be released
+* buffers are allways allocated by activating a [[buffer handle|BuffHandle]], preconfigured already during the planning phase
+* {{{pull()}}} returns a handle at least for the single output requested by this call, allowing the caller to retrieve the result data
+* any other buffers filled with results during the same {{{process()}}} call can be released immediately before returning from {{{pull()}}}
+* similar, any input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
+* while any handle contains the necessary information for releasing or "committing" this buffer, this has to be triggered explicitly.
 
 @@clear(right):display(block):@@
 
-
+
While the render process, with respect to the dependencies, the builder and the processing function is sufficiently characterized by referring to the ''pull principle'' and by defining a [[protocol|NodeOperationProtocol]] each node has to adhere to &mdash; for actually get it coded we have to care for some important details, especially //how to manage the buffers.// It may well be that the length of the code path necessary to invoke the individual processing functions is finally not so important, compared with the time spent at the inner pixel loop within these functions. But my guess is (as of 5/08), that the overall number of data moving and copying operations //will be//&nbsp; of importance.
+{{red{WIP as of 9/11 -- need to mention the planning phase more explicitly}}}
 
 !requirements
 * operations should be "in place" as much as possible
@@ -4766,6 +5167,8 @@ On the other hand, the processing function within the individual node needs to b
 Not everything can be preconfigured though. The pull principle opens the possibility for the node to decide on a per call base what predecessor(s) to pull (if any). This decision may rely on automation parameters, which thus need to be accessible prior to requesting the buffer(s). Additionally, in a later version we plan to have the node network calculate some control values for adjusting the cache and backend timings &mdash; and of course at some point we'll want to utilize the GPU, resulting in the need to feed data from our processing buffers into some texture representation.
 
 !buffer management
+{{red{NOTE 9/11: the following is partially obsolete and needs to be rewritten}}} &rarr; see the BufferTable for details regarding new buffer management...
+
 Besides the StateProxy representing the actual render process and holding a couple of buffer (refs), we employ a lightweight adapter object in between. It is used //for a single {{{pull()}}}-call// &mdash; mapping the actual buffers to the input and output port numbers of the processing node and for dealing with the cache calls. While the StateProxy manages a pool of frame buffers, this interspersed adapter allows us to either use a buffer retrieved from the cache as an input, possibly use a new buffer located within the cache as output, or (in case no caching happens) to just use the same buffer as input and output for "in-place"-processing. The idea is that most of the configuration of this adapter object is prepared in the wiring step while building the node network.
 
 The usage patern of the buffers can be stack-like when processing nodes require multiple input buffers. In the standard case, which also is the simplest case, a pair of buffers (or a single buffer for "in-place" capable nodes) suffices to calculate a whole chain of nodes. But &mdash; as the recursive descent means depth-first processing &mdash; in case multiple input buffers are needed, we may encounter a situation where some of these input buffers already contain processed data, while we have to descend into yet another predecessor node chain to pull the data for the remaining buffers. Care has to be taken //to allocate the buffers as late as possible,// otherwise we could end up holding onto a buffer almost for each node in the network. Effectively this translates into the rule to allocate output buffers only after all input buffers are ready and filled with data; thus we shouldn't allocate buffers when //entering// the recursive call to the predecessor(s), rather we have to wait until we are about to return from the downcall chain.
@@ -4803,8 +5206,8 @@ __see also__
 &rarr; the protocol [[how to operate the nodes|NodeOperationProtocol]]
 
-
-
For each segment (of the effective timeline), there is a Processor holding the exit node(s) of a processing network, which is a "Directed Acyclic Graph" of small, preconfigured, stateless [[processing nodes|ProcNode]]. This network is operated according to the ''pull principle'', meaning that the rendering is just initiated by "pulling" output from the exit node, causing a cascade of recursive downcalls or prerequisite calculations to be scheduled as separate jobs. Each node knows its predecessor(s), thus the necessary input can be pulled from there. Consequently, there is no centralized "engine object" which may invoke nodes iteratively or table driven &mdash; rather, the rendering can be seen as a passive service provided for the backend, which may pull from the exit nodes at any time, in any order (?), and possibly multithreaded.
+
+
For each segment (of the effective timeline), there is a Processor holding the exit node(s) of a processing network, which is a "Directed Acyclic Graph" of small, preconfigured, stateless [[processing nodes|ProcNode]]. This network is operated according to the ''pull principle'', meaning that the rendering is just initiated by "pulling" output from the exit node, causing a cascade of recursive downcalls or prerequisite calculations to be scheduled as individual [[jobs|RenderJob]]. Each node knows its predecessor(s), thus the necessary input can be pulled from there. Consequently, there is no centralized "engine object" which may invoke nodes iteratively or table driven &mdash; rather, the rendering can be seen as a passive service provided for the backend, which may pull from the exit nodes at any time, in any order (?), and possibly multithreaded.
 All State necessary for a given calculation process is encapsulated and accessible by a StateProxy object, which can be seen as the representation of "the process". At the same time, this proxy provides the buffers holding data to be processed and acts as a gateway to the backend to handle the communication with the Cache. In addition to this //top-level State,// each calculation step includes a small [[state adapter object|StateAdapter]] (stack allocated), which is pre-configured by the builder and serves the purpose to isolate the processing function from the detals of buffer management.
 
 
@@ -4838,6 +5241,21 @@ Later on we expect a distinct __query subsystem__ to emerge, presumably embeddin
 
 &rarr; QuantiserImpl
+
+
The [[Scheduler]] is responsible for geting the individual [[render jobs|RenderJob]] to run. The basic idea is that individual render jobs //should never block// -- and thus the calculation of a single frame might be split into several jobs, including resource fetching. This, together with the data exchange protocol defined for the OutputSlot, and the requirements of storage management (especially releasing of superseded render nodes &rarr; FixtureStorage), leads to certain requirements to be ensured by the scheduler:
+;ordering of jobs
+:the scheduler has to ensure all prerequisites of a given job are met
+;job time window
+:when it's not possible to run a job within the defined target time window, it must be marked as failure
+;failure propagation
+:when a job fails, either due to an job internal error, or by timing glitch, any dependent jobs need to receive that failure state
+;guaranteed execution
+:some jobs are marked as "ensure run". These need to run reliable, even when prerequisite jobs fail -- and this failure state needs to be propagated
+
+!detecting termination
+The way other parts of the system are built, requires us to obtain a guaranteed knowledge of some job's termination. It is possible to obtain that knowledge with some limited delay, but it nees to be absoultely reliable (violations leading to segfault). The requirements stated above assume this can be achieved through //jobs with guaranteed execution.// Alternatively we could consider installing specific callbacks -- in this case the scheduler itself has to guarantee the invocation of these callbacks, even if the corresponding job fails or is never invoked. It doesn't seem there is any other option.
+
+
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.
 
@@ -4890,13 +5308,13 @@ We need to detect attaching and detaching of
 * root &harr; [[Track]]
 
-
+
//Segmentation of timeline// denotes a data structure and a step in the BuildProcess.
 When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Placements -- are grouped below each timeline using them; Placements are then to be resolved into [[explicit Placements|ExplicitPlacement]], resulting in a single well defined time interval for each object. This allows to cut this effective timeline into slices of constant wiring structure, which are represented through the ''Segmentation Datastructure'', a time axis with segments holding object placements and [[exit nodes|ExitNode]]. &nbsp;&rarr; see [[structure of the Fixture|Fixture]]
 * for each Timeline we get a Segmentation
 ** which in turn is a list of non-overlapping segments
 *** each holding
-**** an ExplicitPlacement for each covered object
+**** an ExplicitPlacement for each object touching that time interval
 **** an ExitNode for each ModelPort of the corresponding timeline
 
 !Usage pattern
@@ -4905,9 +5323,9 @@ When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Pla
 :&rarr; after //sorting,// the segmentation can be established, thereby copying placements spanning multiple segments
 :&rarr; only //after running the complete build process for each segment,// the list of model ports and exit nodes can be established
 ;(2) commit stage
-: -- after the build process(es) are completed, the new fixture gets ''committed'', thus becoming the officially valid state to be rendered. As render processes might be going on in parallel, some kind of locking or barrier is required. It seems advisable to make the change into a single atomic hot-swap. Meaning we'd get a single access point to be protected. But there is another twist: We need to find out which render processes to cancel an restart, to pick up the changes introduced by this build process, which might include adding and deleting of timelines as a whole, and any changes to the segmentation grid. Because of the highly dynamic nature of the placements, on the other hand it isn't viable to expect the high-level model to provide this information. Thus we need to find out about a ''change coverage'' at this point. We might expand on that idea to //prune any new segments which aren't changed.// This way, only a write barrier would be necessary on switching the actually changed segments, and any render processes touching these would be //tainted.// Old allocations could be released after all tainted processes are known to be terminated.
+: -- after the build process(es) are completed, the new fixture gets ''committed'', thus becoming the officially valid state to be rendered. As render processes might be going on in parallel, some kind of locking or barrier is required. It seems advisable to make the change into a single atomic hot-swap. Meaning we'd get a single access point to be protected. But there is another twist: We need to find out which render processes to cancel and restart, to pick up the changes introduced by this build process -- which might include adding and deleting of timelines as a whole, and any conceivable change to the segmentation grid. Because of the highly dynamic nature of the placements, on the other hand it isn't viable to expect the high-level model to provide this information. Thus we need to find out about a ''change coverage'' at this point. We might expand on that idea to //prune any new segments which aren't changed.// This way, only a write barrier would be necessary on switching the actually changed segments, and any render processes touching these would be //tainted.// Old allocations could be released after all tainted processes are known to be terminated.
 ;(3) rendering use
-:Each play/render process employs a ''frame dispatch step'' to get the right exit node for pulling a given frame. From there on, the process proceeds into the [[processing nodes|ProcNodes]], interleaved with backend/scheduler actions due to splitting into individually scheduled jobs. The storage of these processing nodes and accompanying wiring descriptors is hooked up behind the individual segments, by sharing a common {{{AllocationCluster}}}. Yet the calculation of individual frames also depends on ''parameters'' and especially ''automation'' connected with objects in the high-level model. It is likely that there might be some sharing, as the intention was to allow ''live changes'' to automated values. <br/>{{red{WIP 12/2010}}} details need to be worked out. &rarr; [[parameter wiring concept|Wiring]]
+:Each play/render process employs a ''frame dispatch step'' to get the right exit node for pulling a given frame. From there on, the process proceeds into the [[processing nodes|ProcNodes]], interleaved with backend/scheduler actions due to splitting into individually scheduled jobs. The storage of these processing nodes and accompanying wiring descriptors is hooked up behind the individual segments, by sharing a common {{{AllocationCluster}}}. Yet the calculation of individual frames also depends on ''parameters'' and especially ''automation'' linked with objects in the high-level model. It is likely that there might be some sharing or some kind of additional communication interface, as the intention was to allow ''live changes'' to automated values. <br/>{{red{WIP 12/2010}}} details need to be worked out. &rarr; [[parameter wiring concept|Wiring]]
 !!!observations
 * Storage and initialisation for explicit placements is an issue. We should strive at making that inline as much as possible.
 * the overall segmentation emerges from a sorting of time points, which are start points of explicit placements
@@ -4920,8 +5338,8 @@ When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Pla
 
 !!!conclusions
 The Fixture is mostly comprised of the Segementation datastructure, but some other facilities are involved too
-# at top level, access is structured by groups of model ports, actually grouped by timeline. This first access level handled by the Fixture
-# during the build process, there is a collection of placements; this can be discarded afterwards
+# at top level, access is structured by groups of model ports, actually grouped by timeline. This first access level is handled by the Fixture
+# during the build process, there is a collecting and ordering of placements; these intermediaries as well as the initial collection can be discarded afterwards
 # the backbone of the segmentation is closely linked to an ordering by time. Initially it should support sorting, access by time interval search later on.
 # discarding a segment (or failing to do so) has an high impact on the whole application. We should employ a reliable mechanism for that.
 # the frame dispatch and the tracking of processes can be combined; data duplication is a virtue when it comes to parallel processes
@@ -4943,8 +5361,8 @@ A sequence is always tied to a root-placed track, it can't exist without such. W
 &rarr; see detailed [[discussion of dependent objects' behaviour|ModelDependencies]]
 
-
-
The Session contains all informations, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be [[saved and loaded|SessionLifecycle]]. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[Sequence]].
+
+
The Session contains all information, state and objects to be edited by the User. From a users view, the Session is synonymous to the //current Project//. It can be [[saved and loaded|SessionLifecycle]]. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[Sequence]].
 &rarr; [[Session design overview|SessionOverview]]
 
 !Session structure
@@ -4959,6 +5377,9 @@ The Session object is a singleton &mdash; actually it is a »~PImpl«-Facade
 &rarr; see [[relation of timeline, sequences and objects|TimelineSequences]]
 
+
+
While the core of the persistent session state corresponds just to the HighLevelModel, there is additionaly attached state, annotations and specific bindings, which allow to connect the session model to the local application configuration on each system. A typical example would be the actual output channels, connections and drivers to use on a specific system. In a Studio setup, these setup and wiring might be quite complex, it may be specific to just a single project, and the user might want to work on the same project on different systems. This explains why we can't just embody these configuration information right into the actual model.
+
Querying and retrieving objects within the session model is always bound to a [[scope|PlacementScope]]. When using the //dedicated API,// this scope is immediately defined by the object used to issue the query, like e.g. when searching the contents of a track. But when using the //generic API,// this scope is rather implicit, because in this case a (stateful) QueryFocus object is used to invoke the queries. Somewhat in-between, the top-level session API itself exposes dedicated query functions working on the whole-session scope (model root).
 Based on the PlacementIndex, the treelike scope structure can be explored efficiently; each Placement attached to the session knows its parent scope. But any additional filtering currently is implemented on top of this basic scope exploration, which obviously may degenerate when searching large scopes and models. Filtering may happen implicitly; all scope queries are parametrised to a specific kind of MObject, while the PlacementIndex deals with all kinds of {{{Placement<MObject>}}} uniformly. Thus, more specifically typed queries automatically have to apply a type filter based on the RTTI of the discovered placements. The plan is later to add specialised sub-indices and corresponding specific query functions to speed up the most frequently used kinds of queries.
@@ -5073,7 +5494,7 @@ Currently, I'm planning to modify MObjectRef to return only a const ref to the u
 <<<
 
-
+
The current [[Session]] is the root of any state found within Proc-Layer. Thus, events defining the session's lifecycle influence and synchronise the cooperative behaviour of the entities within the model, the ProcDispatcher, [[Fixture]] and any facility below.
 * when ''starting'', on first access an empty session is created, which puts any related facility into a defined initial state.
 * when ''closing'' the session, any dependent facilities are disabled, disconnected, halted or closed
@@ -5086,7 +5507,7 @@ The SessionManager is responsible for conducting the session lifecycle. Accessib
 Beyond that, client code usually doesn't interact much with the lifecycle, which mostly is a pattern of events to happen in a well-defined sequence. So the //implementation// of the session management operations has to comply to this lifecycle, and does so by relying on a self-contained implementation service, the LifecycleAdvisor. But (contrary to an application framework) the lifecycle of the Lumiera session is rather fixed, the only possibility for configuration or extension being the [[lifecycle hooks|LifecycleEvent]], where other parts of the system (and even plug-ins) may install some callback methods.
 
 !Synchronising access to session's implementation facilities
-Some other parts and subsystems within the ~Proc-Layer need specialised access to implementation facilities within the session. Informations about some conditions and configurations might be retrieved through [[querrying the session|Query]], and especially default configurations for many objects are [[bound to the session|DefaultsImplementation]]. The [[discovery of session contents|SessionStructureQuery]] relies on an [[index facility|PlacementIndex]] embedded within the session implementation. Moreover, some "properties" of the [[media objects|MObject]] are actually due to the respective object being [[placed|Placement]] in some way into the session; consequently, there might be an dependency on the actual [[location as visible to the placement|PlacementScope]], which in turn is constituted by [[querying the index|QueryFocus]].
+Some other parts and subsystems within the ~Proc-Layer need specialised access to implementation facilities within the session. Information about some conditions and configurations might be retrieved through [[querrying the session|Query]], and especially default configurations for many objects are [[bound to the session|DefaultsImplementation]]. The [[discovery of session contents|SessionStructureQuery]] relies on an [[index facility|PlacementIndex]] embedded within the session implementation. Moreover, some "properties" of the [[media objects|MObject]] are actually due to the respective object being [[placed|Placement]] in some way into the session; consequently, there might be an dependency on the actual [[location as visible to the placement|PlacementScope]], which in turn is constituted by [[querying the index|QueryFocus]].
 
 Each of these facilities relies on a separate access point to session services, corresponding to distinct service interfaces. But &mdash; on the implementation side &mdash; all these services are provided by a (compound) SessionServices implementation object. This approach allows to switch the actual implementation of all these services simply by swapping the ~PImpl maintained by the session manager. A new implementation level service can thus be added to the ~SessionImpl just by hooking it into the ~SessionServices compound object. But note, this mechanism as such is ''not thread safe'', unless the //implementation// of the invoked functions is synchronised in some way to prevent switching to a new session implementation while another thread is still executing session implementation code.
 
@@ -5118,8 +5539,8 @@ As detailed above, {{{Session::current}}} exposes the management / lifecycle API
 {{red{none of the above is implemented as of 11/09}}}
 
-
-
The Session contains all informations, state and objects to be edited by the User (&rarr;[[def|Session]]).
+
+
The Session contains all information, state and objects to be edited by the User (&rarr;[[def|Session]]).
 As such, the SessionInterface is the main entrance point to Proc-Layer functionality, both for the primary EditingOperations and for playback/rendering processes. Proc-Layer state is rooted within the session and guided by the [[session's lifecycle events|SessionLifecycle]].
 Implementation facilities within the Proc-Layer may access a somewhat richer [[session service API|SessionServices]].
 
@@ -5154,14 +5575,14 @@ The answer is simple: the one who needs to know about their existence. Because b
 Interestingly, there seems to be an alternative answer to this question. We could locate the setup and definition of all commands into a central administrative facility. Everyone in need of a command then ought to know the name and retrieve this command. Sounds like bureaucracy.
 
-
+
<<<
 {{red{WARNING: Naming was discussed (11/08) and decided to be changed....}}}
 * the term [[EDL]] was phased out in favour of ''Sequence''
 * [[Session]] is largely synonymous to ''Project''
 * there seems to be a new entity called [[Timeline]] which holds the global Pipes
 <<<
-The [[Session]] (sometimes also called //Project// ) contains all informations and objects to be edited by the User. Any state within the Proc-Layer is directly or indirectly rooted in the session. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one or multiple collections within the Session, which we call [[sequence(s)|Sequence]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration, all being represented as [[Asset]]. At any given time, there is //only one current session// opened within the application. The [[lifecycle events|SessionLifecycle]] of the session define the lifecycle of ~Proc-Layer as a whole.
+The [[Session]] (sometimes also called //Project// ) contains all information and objects to be edited by the User. Any state within the Proc-Layer is directly or indirectly rooted in the session. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one or multiple collections within the Session, which we call [[sequence(s)|Sequence]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration, all being represented as [[Asset]]. At any given time, there is //only one current session// opened within the application. The [[lifecycle events|SessionLifecycle]] of the session define the lifecycle of ~Proc-Layer as a whole.
 
 The Session is close to what is visible in the GUI. From a user's perspective, you'll find a [[Timeline]]-like structure, containing an [[Sequence]], where various Media Objects are arranged and placed. The available building blocks and the rules how they can be combined together form Lumiera's [[high-level data model|HighLevelModel]]. Basically, besides the [[media objects|MObjects]] there are data connections and all processing is organized around processing chains or [[pipes|Pipe]], which can be either global (in the Session) or local (in real or virtual clips).
 
@@ -5303,14 +5724,14 @@ if (oldText.indexOf("SplashScreen")==-1)
 }
 //}}}
-
-
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
+
+
A small (in terms of storage) and specifically configured StateProxy object which is created on the stack {{red{Really on the stack? 9/11}}} for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
-
+
An Object representing a //Render Process// and containing associated state information.
-* it is created in the Controller subsystem while initiating the BuildProcess
+* it is created in the Player subsystem while initiating the RenderProcess
 * it is passed on to the generated Render Engine, which in turn passes it down to the individual Processors
-* moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of Thread communication if necessary. (thus the name Proxy)
+* moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of state synchronisation and management if necessary. (thus the name Proxy)
 * in a future version, it may also encapsulate the communication in a distributed render farm
 
@@ -5321,7 +5742,7 @@ if (oldText.indexOf("SplashScreen")==-1) * besides, between different stream //implementation types,// there can be a ''rendering'' (lossy conversion) &mdash; or no conversion at all.
-
+
The stream Prototype is part of the specification of a media stream's type. It is a semantic (or problem domain oriented) concept and should be distinguished from the actual implementation type of the media stream. The latter is provided by an [[library implementation|StreamTypeImplFacade]]. While there are some common predefined prototypes, mostly, they are defined within the concrete [[Session]] according to the user's needs. 
 
 Prototypes form an open (extensible) collection, though each prototype belongs to a specific media kind ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}).
@@ -5334,7 +5755,7 @@ Consequently, as we can't get away with an fixed Enum of all stream prototypes,
 * we can determine if another prototype is //convertible//
 
 !!Examples
-NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus linear PCM...
+In practice, several things might be considered "quite different" and thus be distinguished by protorype: NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus linear PCM...
 
@@ -5513,13 +5934,14 @@ Instead, we should try to just connect the various subsystems via Interfaces and * to shield the rendering code of all complexities of thread communication and synchronization, we use the StateProxy
-
+
Structural Assets are intended mainly for internal use, but the user should be able to see and query them. They are not "loaded" or "created" directly, rather they //leap into existence // by creating or extending some other structures in the session, hence the name. Some of the structural Asset parametrisation can be modified to exert control on some aspects of the Proc Layer's (default) behaviour.
 * [[Processing Patterns|ProcPatt]] encode information how to set up some parts of the render network to be created automatically: for example, when building a clip, we use the processing pattern how to decode and pre-process the actual media data.
 * [[Tracks|Track]] are one of the dimensions used for organizing the session data. They serve as an Anchor to attach parametrisation of output pipe, overlay mode etc. By [[placing|Placement]] to a track, a media object inherits placement properties from this track.
 * [[Pipes|Pipe]] form &mdash; at least as visible to the user &mdash; the basic building block of the render network, because the latter appears to be a collection of interconnected processing pipelines. (this is the //outward view; // in fact the render network consists of [[nodes|ProcNode]] and is [[built|Builder]] from the Pipes, clips, effects...)[>img[Asset Classess|uml/fig131205.png]]
 * [[Sequence]] assets act as a façade to the fundamental compound building blocks within the model, a sequence being a collection of clips placed onto a tree of tracks. Sequences, as well as the top-level tracks enclosed will be created automatically on demand. Of course you may create them deliberately. Without binding it to a timeline or meta-clip, a sequence remains invisible.
-* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID 
+* [[Timeline]] assets are the top level structures to access the model; similar to the sequences, they act as façade to relevant parts of the model (BindingMO) and will be created on demand, alongside with a new session if necessary, bound to the new timeline. Likewise, they can be referred by their name-ID
+* [[Viewer|ViewerAsset]] assets correspond to the available viewer elements in the GUI. By [[connecting to a viewer|ViewConnection]], session elements derive a concrete (physical) output and gain the ability to be [[played|PlayService]].
 
 
 
@@ -5705,6 +6127,14 @@ h1,h2,h3,h4,h5,h6 {
 /*}}}*/
 
+
+
Viewers for displaying output are connected to the timelines or other elements to be displayed through a ViewConnection. This creates an additional mapping step (&rarr; BindingMO), similar to the attachment of a sequence or virtual clip to the [[global busses|GlobalBus]]. Yet the viewers don't provide such an elaborate mixing desk like view as the timelines -- rather there is only a simplified version of the global busses, exposed to the user as ''switch board''.
+
+The corresponding GUI control will allow to choose among possible data sources (esp. ProbePoint) for any given StreamType (more precisely, for every //prototype,// e.g. image, sound). Moreover, there are some (limited) overlay capabilities (split image, mix sound). By default, this additional control will likely be hidden, as there is a default 1:1 association between the master busses of the timeline and the usable output destinations.
+
+!Model and Interfaces
+Regarding the internal wiring of components, the Viewer with a switch board behaves similar as a timeline: At the output side, there are a small number of standard OutputDesignation elements for the selection of primary kinds of media handled within this session (typically Video and Audio). But contrary to a timeline, a Viewer element also exposes an OutputManager interface, which is backed by a specific OutputMapping element, which internally connects to the main OutputManager for the whole Application. This way, a Viewer has the capability to get a PlayController
+
<<timeline better:true maxDays:55 maxEntries:45>>
@@ -6708,7 +7138,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]]
-
+
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 relevant point to note is that for quantised time values, the effect of a change can be disproportional to 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 amount of 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
@@ -6769,46 +7199,54 @@ As rationale, consider the following
 * by materialising a quantised change prior to applying it, we get the "chaining effect"
 
 ----------
-!!! solutions ideas{{red{WIP 12/10}}}
+this leads to the following solutions approach
 * 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.// &rarr;{{red{No! any kind of abstraction requires an indirection}}}
+* for simple time calculations the ~TimeVar is sufficient
+* but we allow to fed an abstract //modification object.//
+* ''nudge by unit(s)'' is subsumed here as a special case
 
-{{red{Question 3/11}}} -- can we avoid accepting mutations for Time (and thus only mutate ~TimeSpan)?
+As the actual change logic to apply depends both on the target value and the kind of the change, we end up with //double dispatch.//
+
+ -- can we avoid accepting mutations for Time (and thus only mutate ~TimeSpan)?
 Rationale: allowing mutations for Time bears the danger of making ~TimeVar obsolete
 * //4/11 yes, implementing it now this way....//
 * we have ~TimeVar, so the only thing that //needs// to be mutable is a whole object &rArr; ~TimeSpan
 * err, because MObject will include a Duration separate from the start time in the Placement, Duration needs to be mutable too
 
 !!!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)"
+{{red{Question 5/11}}} 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.
 
-!!! design draft
+!!!design draft
 The special focus of this problem seems to lead itself to a __visitor pattern__ based implementation. Because the basic hierarchy of applicable types is fixed, but the behaviour is open ended (and not yet fully determined). A conventional implementation would scatter this behaviour over all the time entities, thus making it hard to understand and reason about. The classical ~GoF cyclic visitor solution to the contrary allows us to arrange closely related behaviour into thematically grouped visitor classes. As a plus, the concrete action can be bound dynamically, allowing for more flexibility when it comes to dealing with the intricate situations when a quantised time span (= a clip) recieves a quantised mutation (= is re-alligend to a possibly different frame grid)
 
 Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}}} function, which is defined to reflect back into the (virtual) visitation functions of the Mutation Interface. Additionally, we provide some static convenience shortcuts right in the Mutation interface, performing the trivial mutations.
+
+!!!life value changes and monitoring
+Based on this time::Mutation design, we provide a specialised element for dealing with //running time values:// When attached to a target time entity, a "life" connection is established. From then on, continuous changes and mutations can be fed to the target by invoking a functor interface. Besides, a change notification signal (callback) can be installed, which will be invoked on each change. This {{{time::Control}}} element is the foundation for implementing all kinds of running time display widgets, spin buttons, timeline selections, playheads, loop playback and similar.
 
-
+
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.
 :the time values are just numbers; the point of reference and the meaning is implicit.
 :within Lumiera, time is encoded as integral number of //micro ticks,// practically continuous
-;duration
+;time distance
 :a range of time, a ''distance'' on the time axis, measured with the same arbitrary precision as time points.
-:distances and durations can be determined by //subtracting// two time points, consequently they are //signed numbers.//
-:a duration always abstracts from the time //when// this duration or distance happens, the relation to any time scale remains implicit
+:distances can be determined by //subtracting// two time points, consequently they are //signed numbers.//
 ;offset
-:offsetting a time or a duration is an operation (not an entity): it means changing the denoted time point or duration.
-:the //target// of an offset operation is a time or duration, while it's //argument// is a distance (synonymous to duration).
-:Time values are //immutable,// like numbers. Only a ''time variable'' can be changed. Durations to the contrary can be mutable or  const.
+:a distance can be used to adjust (offset) a time or a duration: this means applying a relative change.
+:the //target// of an offset operation is a time or duration, while it's //argument// is a distance (synonymous to offset).
+;duration
+:the length of a time range yields a ''time metric''.
+:the duration can be defined as the //absolute value//&nbsp; of the offset between start and endpoint of the time range.
+:a duration always abstracts from the time //when// this duration or distance happens, the relation to any time scale remains implicit
 ;time span
 :contrary to a mere duration, a ''time interval'' or time span is actually //anchored// at a specific point in time.
 :it can be seen as a //special kind of duration,// which explicitly states the information //when// this time span takes place.
+;changing time
+:Time values are //immutable,// like numbers. Only a ''time variable'' can be changed.
+:yet some of the special time entities can recieve [[mutation messages|TimeMutation]], allowing e.g. for adjustments to a time interval selection from the GUI
 
 ;internal time
 :While the basic continuous time values don't imply any provision regarding the time scale and origin to be used, actually, within the implementation of the application, the meaning of time values is uniform and free of contradictions. Thus effectively there is an ''implementation time scale'' -- but its scope of validity is //strictly limited to the implementation level of a single application instance.// It is never exposed and never persisted. It might not be reproducible over multiple instantiations of the application. The implementation reserves the right to recalibrate this internal scale. Later, when Lumiera gains the capability to run within a network of render nodes, these instance connections will include a negotiation about the internal time scale, which remains completely opaque to the outer world. This explains, why {{{lumiera::Time}}} instances lack the ability to show their time value beyond debugging purposes. This is to avoid confusion and to stress their opaque nature.
@@ -6817,7 +7255,7 @@ Thus the possibly mutalble time entities get an {{{accept(time::Mutation&)}}
 :This implies the presence of a //running synchronisation process,// with the authority to adjust the time base;
 :contrast this to the internal time, which is static and unconnected -- 
 ;quantised time
-:The ''act of quantisation'' transforms a continuous property into a ''discrete'' structure. Prominent examples can be found in the domain of micro physics and with digital information processing. In a broader sense, any measurement or //quantification// also encompasses a quantisation. Regarding time and time measurement, quantisation means alignment to a predefined ''time grid''. Quantisation necessarily is an //irreversible process// -- possible additional informations get discarded.
+:The ''act of quantisation'' transforms a continuous property into a ''discrete'' structure. Prominent examples can be found in the domain of micro physics and with digital information processing. In a broader sense, any measurement or //quantification// also encompasses a quantisation. Regarding time and time measurement, quantisation means alignment to a predefined ''time grid''. Quantisation necessarily is an //irreversible process// -- possible additional information is discarded.
 :Note that quantisation introduces an ''time origin'' and a ''reference scale''
 ;frame count
 :within the context of film and media editing, the specification of a ''frame number'' is an especially important instance of quantisation.
@@ -6854,7 +7292,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
@@ -6874,6 +7312,8 @@ At the level of individual timecode formats, we're lacking a common denominator;
 :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. 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)
+;dispatching individual frames for calculation
+:when a [[render or playback process|PlayProcess]] is created, at some point we need to translate this logical unit ("calculation stream") into individual frame job entries. This requires to break continuous time into individual frames, and then ennumerating these frames.
 ;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 (especially note this!) on the actual values which can be set and changed through the GUI.
@@ -6895,6 +7335,8 @@ Another closely related problem is ''when to allow [[mutations|TimeMutation]]'',
 
 The ''problem with playback position'' is -- that indeed it's an attempt to conceptualise a non-existing entity. There is no such thing like "the" playback position. Yet most applications I'm aware off //do// employ this concept. Likely they got trapped by the metaphor of the tape head, again. We should do away with that. On playback, we should show a //projection of wall-clock time onto the expected playback range// -- not more, not less. It should be acknowledged that there is //no direct link to the ongoing playback processes,// besides the fact that they're assumed to sync to wall-clock time as well. Recall, typically there are multiple playback processes going on in compound, and each might run on a different update rate. If we really want a //visual out-of-sync indicator,// we should treat that as a separate reporting facility and display it apart of the playback cursor.
 
+An interesting point to note for the ''frame dispatch step'' is the fact, that in this case quantised values and quantisation are approached in the reverse direction, compared with the other uses. Here, after establishing a start point on the time scale, we proceed with ennumerating distinct frames and later on need to access the corresponding raw time, especially to find out about the [[timeline segment|Segmentation]] to address, or for retrieving parameter automation. &rarr; FrameDispatcher.
+
 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.//
 
 !the complete time value usage cycle
@@ -6956,12 +7398,13 @@ Currently (1/11), the strategy is implemented according to (1) and (4) above, le
 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 {{red{Question 4/11 -- isn't this done and fixed by now??}}}
 
-
+
Timeline is the top level element within the [[Session (Project)|Session]]. It is visible within a //timeline view// in the GUI and represents the effective (resulting) arrangement of media objects, to be rendered for output or viewed in a Monitor (viewer window). A timeline is comprised of:
 * a time axis in abolute time ({{red{WIP 1/10}}}: not clear if this is an entity or just a conceptual definition) 
-* a PlayControler ({{red{WIP Summer 2010: see discussion on ML. It seems rather that the controller will be attached}}})
 * a list of [[global Pipes|GlobalPipe]] representing the possible outputs (master busses)
 * //exactly one// top-level [[Sequence]], which in turn may contain further nested Sequences.
+* when used for Playback, a ViewConnection is necessary, allowing to get or connect to a PlayController
+
 Please note especially that following this design //a timeline doesn't define tracks.// [[Tracks form a Tree|Track]] and are part of the individual sequences, together with the media objects placed to these tracks. Thus sequences are independent entities which may exist stand-alone within the model, while a timeline is //always bound to hold a sequence.// &rarr; see ModelDependencies
 [>img[Fundamental object relations used in the session|uml/fig136453.png]]
 
@@ -7083,7 +7526,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.
 
@@ -7109,7 +7552,7 @@ A registration service backed by an index table can be used to //translate//&
 
 !!!{{red{WIP}}}Analysis and discussion
 Still some contradictions &mdash; and rather seems helpful, not so much necessary.
-We //already have an registration service,// both for Assets (AssetManager) and for Placements (PlacementIndex). These facilities maintain not only a raw ID &harr; object association, but also structuring informations, albeit bound to more specific circumstances (the system of placement scopes, and the asset category). The lookup uniqueID &rArr; object could be implemented by sequentially querying this small number of central registration facilities. Thus, still lacking is a ''system of sub index tables''.
+We //already have an registration service,// both for Assets (AssetManager) and for Placements (PlacementIndex). These facilities maintain not only a raw ID &harr; object association, but also structuring information, albeit bound to more specific circumstances (the system of placement scopes, and the asset category). The lookup uniqueID &rArr; object could be implemented by sequentially querying this small number of central registration facilities. Thus, still lacking is a ''system of sub index tables''.
 
 As mentioned above, an ID &harr; type association plays a crucial role when it comes to implementing any kind of rules based configuration. It would allow to bridge from our session objects to rules and resolution working entirely symbolic. (&rarr; [[more|ConfigQueryIntegration]]). But, as of 3/2010 this is a //planned feature and not required to get the initial pipeline working.// Thus, according to the YAGNI principle, we shouldn't engage into any implementation details right now and just create the extension points.
 
@@ -7130,7 +7573,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
@@ -7141,7 +7584,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 a 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 information 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.
@@ -7182,14 +7625,29 @@ For now, as of 6/10, we use specialised QueryResolver instances explicitly and d
 &rarr; QueryRegistration
 
-
+
+
For any kind of playback to happen, timeline elements (or similar model objects) need to be attached to a Viewer element through a special kind of [[binding|BindingMO]], called a ''view connection''. In the most general case, this creates an additional OutputMapping (and in the typical standard case, this boils down to a 1:1 association, sending the master bus of each media kind to the standard OutputDesignation for that kind).
+
+Establishing a ~ViewConnection is prerequisite for creating or attaching an PlayController through the PlayService. Multiple "play control" GUI elements can be associated with such a play controller, causing them to work as being linked together: if you e.g. push "play" on one of them, the button states of all linked GUI controls will reflect the state change of the underlying play controller.
+
+View connections are part of the model and thus persistent. They can be created explicitly, or just derived by //allocating a viewer.// And a new view connection can push aside (and thus "break") an existing one from another timeline or model element. When a view connection is //broken,// any associated PlayProcess needs to be terminated (this is a blocking operation). Thus, at any time, there can be only one active view connection to a given viewer; here "active" means, that a PlayController has been hooked up, and the connection is ready for playback or rendering. But on the other hand, nothing prevents a timeline (or similar model object) to maintain multiple view connections -- consequently the actual playback position behaves as if associated with the view connection.
+
+
+
+
A [[structural Asset|StructAsset]] corresponding to a Viewer element in the GUI.
+
+These Viewer (or Monitor) elements play an enabling role for any output generation. In order to [[initiate playback|PlayService]], we need a fully resolved OutputDesignation, which typcially can be achieved by creating a ViewConnection, i.e. connecting a timeline or similarily suitable model element to such a viewer. Actually, this connection is modelled by attaching to a BindingMO which is linked to a ViewerAsset. This way, the model tracks and persists the available viewer windows and the current connection situation.
+
+When the GUI is outfitted, based on the current Session or HighLevelModel, it is expected to retrieve the viewer assets and for each of them, after installing the necessary widgetes, registers an OutputSlot with the global OutputManager.
+
+
for showing output, three entities are involved
 * the [[Timeline]] holds the relevant part of the model, which gets rendered for playback
-* by connecting to a viewer component, an actual output target is established
+* by connecting to a viewer component (&rarr; ViewConnection), an actual output target is established
 * the playback process itself is coordinated by a PlayController, which in turn creates a PlayProcess
 
 !the viewer connection
-A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputManager slot &mdash; again an output mapping step.
+A viewer element gets connected to a given timeline either by directly attaching it, or by //allocating an available free viewer.// Anyway, as a model element, the viewer is just like another set of global pipes chained up after the global pipes present in the timeline. Connecting a timeline to a viewer creates a ViewConnection, which is a special [[binding|BindingMO]]. The number and kind of pipes provided is a configurable property of the viewer element &mdash; more specifically: the viewer's SwitchBoard. Thus, connecting a viewer activates the same internal logic employed when connecting a sequence into a timeline or meta-clip: a default channel association is established, which can be overridden persistently (&rarr; OutputMapping). Each of the viewer's pipes in turn gets connected to a system output through an OutputSlot registered with the OutputManager &mdash; again an output mapping step.
 
diff --git a/wiki/support_library.html b/wiki/support_library.html deleted file mode 100644 index 8a6bc456b..000000000 --- a/wiki/support_library.html +++ /dev/null @@ -1,11816 +0,0 @@ - - - - - - - - - - - -
My TiddlyWiki is loading ...

Requires Javascript.
- - Support Library - things we find useful - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #8cf
-PrimaryLight: #18f
-PrimaryMid: #04b
-PrimaryDark: #014
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
/*{{{*/
-body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-
-a {color:[[ColorPalette::PrimaryMid]];}
-a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
-a img {border:0;}
-
-h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
-h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
-h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
-
-.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
-.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
-.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
-
-.header {background:[[ColorPalette::PrimaryMid]];}
-.headerShadow {color:[[ColorPalette::Foreground]];}
-.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
-.headerForeground {color:[[ColorPalette::Background]];}
-.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
-
-.tabSelected{color:[[ColorPalette::PrimaryDark]];
-	background:[[ColorPalette::TertiaryPale]];
-	border-left:1px solid [[ColorPalette::TertiaryLight]];
-	border-top:1px solid [[ColorPalette::TertiaryLight]];
-	border-right:1px solid [[ColorPalette::TertiaryLight]];
-}
-.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
-.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
-.tabContents .button {border:0;}
-
-#sidebar {}
-#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
-#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
-
-.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
-.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
-.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
-	border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
-.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
-.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
-.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
-	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
-.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
-.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
-	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
-
-#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
-#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
-
-.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
-
-.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
-.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
-.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
-.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
-
-.tiddler .defaultCommand {font-weight:bold;}
-
-.shadow .title {color:[[ColorPalette::TertiaryDark]];}
-
-.title {color:[[ColorPalette::SecondaryDark]];}
-.subtitle {color:[[ColorPalette::TertiaryDark]];}
-
-.toolbar {color:[[ColorPalette::PrimaryMid]];}
-.toolbar a {color:[[ColorPalette::TertiaryLight]];}
-.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
-.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
-
-.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
-.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
-.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
-.tagging .button, .tagged .button {border:none;}
-
-.footer {color:[[ColorPalette::TertiaryLight]];}
-.selected .footer {color:[[ColorPalette::TertiaryMid]];}
-
-.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
-.sparktick {background:[[ColorPalette::PrimaryDark]];}
-
-.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
-.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
-.lowlight {background:[[ColorPalette::TertiaryLight]];}
-
-.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
-
-.imageLink, #displayArea .imageLink {background:transparent;}
-
-.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
-
-.viewer .listTitle {list-style-type:none; margin-left:-2em;}
-.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
-.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
-.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
-.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
-.viewer code {color:[[ColorPalette::SecondaryDark]];}
-.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
-
-.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
-
-.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
-.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
-.editorFooter {color:[[ColorPalette::TertiaryMid]];}
-
-#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
-#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
-#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
-#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
-.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
-.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
-#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
-/*}}}*/
-
-
-
/*{{{*/
-* html .tiddler {height:1%;}
-
-body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
-
-h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
-h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
-h4,h5,h6 {margin-top:1em;}
-h1 {font-size:1.35em;}
-h2 {font-size:1.25em;}
-h3 {font-size:1.1em;}
-h4 {font-size:1em;}
-h5 {font-size:.9em;}
-
-hr {height:1px;}
-
-a {text-decoration:none;}
-
-dt {font-weight:bold;}
-
-ol {list-style-type:decimal;}
-ol ol {list-style-type:lower-alpha;}
-ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol {list-style-type:decimal;}
-ol ol ol ol ol {list-style-type:lower-alpha;}
-ol ol ol ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol ol ol ol {list-style-type:decimal;}
-
-.txtOptionInput {width:11em;}
-
-#contentWrapper .chkOptionInput {border:0;}
-
-.externalLink {text-decoration:underline;}
-
-.indent {margin-left:3em;}
-.outdent {margin-left:3em; text-indent:-3em;}
-code.escaped {white-space:nowrap;}
-
-.tiddlyLinkExisting {font-weight:bold;}
-.tiddlyLinkNonExisting {font-style:italic;}
-
-/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
-a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
-
-#mainMenu .tiddlyLinkExisting,
-	#mainMenu .tiddlyLinkNonExisting,
-	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
-#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
-
-.header {position:relative;}
-.header a:hover {background:transparent;}
-.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
-.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
-
-.siteTitle {font-size:3em;}
-.siteSubtitle {font-size:1.2em;}
-
-#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
-
-#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
-#sidebarOptions {padding-top:0.3em;}
-#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
-#sidebarOptions input {margin:0.4em 0.5em;}
-#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
-#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
-#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
-#sidebarTabs .tabContents {width:15em; overflow:hidden;}
-
-.wizard {padding:0.1em 1em 0em 2em;}
-.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizardStep {padding:1em 1em 1em 1em;}
-.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
-.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
-.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
-.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
-
-#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
-.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
-#messageArea a {text-decoration:underline;}
-
-.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
-.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
-
-.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
-.popup .popupMessage {padding:0.4em;}
-.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
-.popup li.disabled {padding:0.4em;}
-.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
-.listBreak {font-size:1px; line-height:1px;}
-.listBreak div {margin:2px 0;}
-
-.tabset {padding:1em 0em 0em 0.5em;}
-.tab {margin:0em 0em 0em 0.25em; padding:2px;}
-.tabContents {padding:0.5em;}
-.tabContents ul, .tabContents ol {margin:0; padding:0;}
-.txtMainTab .tabContents li {list-style:none;}
-.tabContents li.listLink { margin-left:.75em;}
-
-#contentWrapper {display:block;}
-#splashScreen {display:none;}
-
-#displayArea {margin:1em 17em 0em 14em;}
-
-.toolbar {text-align:right; font-size:.9em;}
-
-.tiddler {padding:1em 1em 0em 1em;}
-
-.missing .viewer,.missing .title {font-style:italic;}
-
-.title {font-size:1.6em; font-weight:bold;}
-
-.missing .subtitle {display:none;}
-.subtitle {font-size:1.1em;}
-
-.tiddler .button {padding:0.2em 0.4em;}
-
-.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
-.isTag .tagging {display:block;}
-.tagged {margin:0.5em; float:right;}
-.tagging, .tagged {font-size:0.9em; padding:0.25em;}
-.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
-.tagClear {clear:both;}
-
-.footer {font-size:.9em;}
-.footer li {display:inline;}
-
-.annotation {padding:0.5em; margin:0.5em;}
-
-* html .viewer pre {width:99%; padding:0 0 1em 0;}
-.viewer {line-height:1.4em; padding-top:0.5em;}
-.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
-.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
-.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
-
-.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
-.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
-table.listView {font-size:0.85em; margin:0.8em 1.0em;}
-table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
-
-.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
-.viewer code {font-size:1.2em; line-height:1.4em;}
-
-.editor {font-size:1.1em;}
-.editor input, .editor textarea {display:block; width:100%; font:inherit;}
-.editorFooter {padding:0.25em 0em; font-size:.9em;}
-.editorFooter .button {padding-top:0px; padding-bottom:0px;}
-
-.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
-
-.sparkline {line-height:1em;}
-.sparktick {outline:0;}
-
-.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
-.zoomer div {padding:1em;}
-
-* html #backstage {width:99%;}
-* html #backstageArea {width:99%;}
-#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageToolbar {position:relative;}
-#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
-#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
-#backstage {position:relative; width:100%; z-index:50;}
-#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
-.backstagePanelFooter {padding-top:0.2em; float:right;}
-.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
-#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
-
-.whenBackstage {display:none;}
-.backstageVisible .whenBackstage {display:block;}
-/*}}}*/
-
-
-
/***
-StyleSheet for use when a translation requires any css style changes.
-This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
-***/
-
-/*{{{*/
-body {font-size:0.8em;}
-
-#sidebarOptions {font-size:1.05em;}
-#sidebarOptions a {font-style:normal;}
-#sidebarOptions .sliderPanel {font-size:0.95em;}
-
-.subtitle {font-size:0.8em;}
-
-.viewer table.listView {font-size:0.95em;}
-
-.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
-/*}}}*/
-
-
-
/*{{{*/
-@media print {
-#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
-#displayArea {margin: 1em 1em 0em 1em;}
-/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
-noscript {display:none;}
-}
-/*}}}*/
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-<div class='headerShadow'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-<div class='headerForeground'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-</div>
-<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
-<div id='sidebar'>
-<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-<div id='messageArea'></div>
-<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
-<div class='title' macro='view title'></div>
-<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
-<div class='tagging' macro='tagging'></div>
-<div class='tagged' macro='tags'></div>
-<div class='viewer' macro='view text wikified'></div>
-<div class='tagClear'></div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
-<div class='title' macro='view title'></div>
-<div class='editor' macro='edit title'></div>
-<div macro='annotations'></div>
-<div class='editor' macro='edit text'></div>
-<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
-<!--}}}-->
-
-
-
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
-* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
-* MainMenu: The menu (usually on the left)
-* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
-You'll also need to enter your username for signing your edits: <<option txtUserName>>
-
-
-
These InterfaceOptions for customising TiddlyWiki are saved in your browser
-
-Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
-
-<<option txtUserName>>
-<<option chkSaveBackups>> SaveBackups
-<<option chkAutoSave>> AutoSave
-<<option chkRegExpSearch>> RegExpSearch
-<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
-<<option chkAnimate>> EnableAnimations
-
-----
-Also see AdvancedOptions
-
-
- -
-
-
PageTemplate
-|>|SiteTitle - SiteSubtitle|
-|>|MainMenu|
-|DefaultTiddlers<<br>><<br>><<br>>ViewTemplate<<br>><<br>>EditTemplate|SideBarOptions|
-|~|OptionsPanel|
-|~|SideBarTabs|
-|~|AdvancedOptions|
-|~|<<tiddler Configuration.SideBarTabs>>|
-
-''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
-
-ColorPalette
-
-SiteUrl
-
-
-
/***
-|Name|BetterTimelineMacro|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#BetterTimelineMacro|
-|Version|0.5 beta|
-|Requires|~TW2.x|
-!!!Description:
-A replacement for the core timeline macro that offers more features:
-*list tiddlers with only specfic tag
-*exclude tiddlers with a particular tag
-*limit entries to any number of days, for example one week
-*specify a start date for the timeline, only tiddlers after that date will be listed.
-
-!!!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!!!Syntax:
-{{{<<timeline better:true>>}}}
-''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
-
-additonal params:
-(use only the ones you want)
-{{{<<timeline better:true  onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30>>}}}
-
-''explanation of syntax:''
-onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
-excludeTag: tiddlers with this tag will not be listed.
-sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
-firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
-maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
-maxEntries: limit the total number of entries in the timeline.
-
-
-!!!History:
-*28-07-06: ver 0.5 beta, first release
-
-!!!Code
-***/
-//{{{
-// Return the tiddlers as a sorted array
-TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
-{
-          var results = [];
-          this.forEachTiddler(function(title,tiddler)
-          {
-          if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
-                        if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
-                                      results.push(tiddler);
-          });
-          if(field)
-                   results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
-          return results;
-}
-
-
-
-//this function by Udo
-function getParam(params, name, defaultValue)
-{
-          if (!params)
-          return defaultValue;
-          var p = params[0][name];
-          return p ? p[0] : defaultValue;
-}
-
-window.old_timeline_handler= config.macros.timeline.handler;
-config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
-{
-          var args = paramString.parseParams("list",null,true);
-          var betterMode = getParam(args, "better", "false");
-          if (betterMode == 'true')
-          {
-          var sortBy = getParam(args,"sortBy","modified");
-          var excludeTag = getParam(args,"excludeTag",undefined);
-          var includeTag = getParam(args,"onlyTag",undefined);
-          var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
-          var firstDayParam = getParam(args,"firstDay",undefined);
-          var firstDay = (firstDayParam!=undefined)? firstDayParam: "00010101";
-          var lastDay = "";
-          var field= sortBy;
-          var maxDaysParam = getParam(args,"maxDays",undefined);
-          var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
-          var maxEntries = getParam(args,"maxEntries",undefined);
-          var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
-          for(var t=tiddlers.length-1; t>=last; t--)
-                  {
-                  var tiddler = tiddlers[t];
-                  var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
-                  if ((theDay>=firstDay)&& (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
-                     {
-                     if(theDay != lastDay)
-                               {
-                               var theDateList = document.createElement("ul");
-                               place.appendChild(theDateList);
-                               createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
-                               lastDay = theDay;
-                               }
-                  var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
-                  theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
-                  }
-                  }
-          }
-
-          else
-              {
-              window.old_timeline_handler.apply(this,arguments);
-              }
-}
-//}}}
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #ec5
-PrimaryLight: #ec0
-PrimaryMid: #b30
-PrimaryDark: #310
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
This hashing gives guaranteed O(1) lookup complexity and amortized O(1) insert and remove complexity. Hash tables by default grow and shrink automatically. It is posible to preallocate entries and turn automatic shrinking off taking out the memory management factors for insert and remove operations. This implementation uses 3 Tables which exponential growing sizes.
-
-Hash table utilization is commonly between 40-80% which gives better memory usage and far better locality than trees.
-
-
-
[[SupportLibrary]]
-
-
-
We distinguish the way how to cope with Errors, i.e. the ErrorHandlingPolicy, and the actual implementation
-* for code with C semantics &rarr; ErrorHandling-C
-* for C++ code &rarr; ErrorHandling-Cpp
-
-
-
! Proposal:
-We need some centralized way to handle errors and doing hard aborts.
-
-I started using C-string addresses as errors for now. I think that is convenient and unique enough until we find something better. (Actually, this might be even kept in a library. Alternatively, maybe someone wants to investigate libcomerr)
-
-Notes about libcomerr (I took a short look now):
-* needs some central error description table which has to be compiled with compile_et
-* no homepage found, some projects use it, published in 1989, dunno about its state
-
-My following proposal defines a very simplistic way to define unique errors which can distributed throughout the application (each part can define its own errors and will never interfere with others)
-
-
-!!! detailed semantics (proposal):
-a TLS pointer is allocated to keep a thread local error state. NULL means SUCCESS, no error pending.
-
-API:
-
-{{{
-const char*
-lumiera_error_set (const char * err)
-}}}
-
-if there is no error pending, then store err as new error state, if there was an error pending, then the state is not altered.
-
-return the former state (NULL if err got set, some other when an error is pending)
-
-{{{
-const char*
-lumiera_error ()
-}}}
-
-returns the error state ''and'' clears it. The user has to store it temporary when need to be used further. Rationale: less TLS access overhead, never forget to clear the state.
-
-(do we need a {{{lumiera_error_peek()}}}?)
-
-Declaration and definition:
-{{{
-#define LUMIERA_ERROR_DECLARE(err) \
-extern const char* LUMIERA_ERROR_##err
-
-#define LUMIERA_ERROR_DEFINE(err, msg) \
-const char* LUMIERA_ERROR_##err = "LUMIERA_ERROR_" #err ":" msg
-}}}
-
-thus a {{{LUMIERA_ERROR_DEFINE(NFOO, "Foo not found")}}} will result in a string:
-"~LUMIERA_ERROR_NFOO:Foo not found", having the identifier itself prepended to the string will ensure uniqueness of the generated literal (and thus its pointer value), reducing error testing to address comparison {{{lumiera_error()==LUMIERA_ERROR_NFOO}}}. The message string is easily derived by {{{strchr(LUMIERA_ERROR_NFOO, ':')+1}}}
-
-
-
-
-!! Allocation
-The next point is allocation failures. These are possible by C/C++ standard but don't actually happen anymore in Linux (except in few rare cases). Instead of gracefully handling this errors I'll add a {{{LUMIERA_DIE(message)}}} macro to this library later. This macro will just do (NoBug) logging and then doing a hard abort. It should be a macro because we want to preserve file/line location for logging.
-
-
-
-
-
Basically, the C++ error handling techniques are layered on top of the [[C solution|ErrorHandling-C]].
-!Proposal:
-We use a common base class for all our application specific exceptions. These exceptions can be thought of as a classification of error situations, thus the hierarchical approach. The purpose of throwing such a //classified exception// is
-* to do a controlled partial failure
-* to trigger automatic cleanup without the need to implement the details
-
-!!Requirements for the Exception Interface
-* a means for capturing and transporting detail informations
-* the possibility to get at the ''root cause'' of an exception, even after having passed several subsystem barriers.
-* getting standardized error messages automatically
-
-!!provided features
-The C++ errorhandling Classes and functions can be found in {{{common/error.hpp}}} (not to be confused with the elementary C errorhandling of the Lumiera support lib {{{lib/error.h}}}. See also the "exceptionerrortest.cpp"
-* the constructor of the Exception base class will set the C-style error flag as well. Obviously, when an exception gets caught and handled, this error-flag should be reset (and this is the responsibility of the handler).
-* we add a __unknown()__ handler which will print additional diagnostics.
-* the Exception class has a diagnostic message intended for developers and a friendly message for the user. It is encouraged to write a detailed description of each error situation right into a string constant passed to the exception object ctor. This will serve the purpose of documenting the error situation in the source code and at the same time help diagnosis.
-* there is a variant of the constructor taking a reference to an std::exception. This is intended for //chained exceptions//. Whenever an handler catches an exception, but then decides to rethrow it with different classification, the original exception object should be passed on by using this constructor, so it's {{{what()}}} message can be preserved and will be included in the final log entry. While this may look like overkill in a small example, it is a very helpful facility in a larger layered application, where it is often difficult to spot the original cause of an exception encountered.
-* for each mayor category of Exception subclasses, we define a C-style error constant. The client code is free to define further detailed error constants and Exception subclasses.
-* to help defining Exception subclasses, a macro {{{LUMIERA_EXCEPTION_DECLARE}}} is provided.
-
-!!!basic Exception categories
-|!category|!description|
-|error::Logic| contradiction to internal logic assumptions detected|
-|error::Fatal| special subclass of Logic: situation can't be handled, internal logic floundered |
-|error::Config| execution aborted due to misconfiguration |
-|error::State| unforeseen internal state |
-|error::Invalid| invalid input or parameters encountered |
-|error::External| failure in external service the application relies on |
-
-
-
-
! Framerates
-Framerates are stored as rational numbers eg. 30000/1001 for NTSC, this representation allows highly precise integer calculations for frames and times.
-
-! Frames
-All calculations in Lumiera are based on frames which is a signed integer type. Together with a Framerate and a starting point, every frame can be exactly located.
-
-! Time
-Time is used only internally in Lumiera, every external representation of time will be converted to frames. Time has a precision of 1 microsecond and is stored in POSIX struct timeval. Time is always handled as absolute time, thus frame addresses map to absolute times, there is at worst a 1us precision jitter but no drift.
-
-!! SMPTE and other Timecodes
-will be added on demand, but be frame based, not time based
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
-
/***
-|''Name:''|InlineJavascriptPlugin|
-|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
-|''Author:''|Eric Shulman - ELS Design Studios|
-|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
-|''~CoreVersion:''|2.0.10|
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Display script source in tiddler output''
-By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-><script show>
- alert('InlineJavascriptPlugin: this is a demonstration message');
-</script>
-dynamic output:
-><script show>
- return (new Date()).toString();
-</script>
-wikified dynamic output:
-><script show>
- return "link to current user: [["+config.options.txtUserName+"]]";
-</script>
-dynamic output using 'place' to get size information for current tiddler:
-><script show>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-creating an 'onclick' button/link that runs a script:
-><script label="click here" show>
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-loading a script from a source url:
->http://www.TiddlyTools.com/demo.js contains:
->>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
->>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
-><script src="demo.js" show>
- return "loading demo.js..."
-</script>
-><script label="click to execute demo() function" show>
- demo()
-</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
-''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
-''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]'' initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[4]) { // there is script code
- if (lookaheadMatch[3]) // show inline script code in tiddler output
- wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
- if (lookaheadMatch[2]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[4]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else { // run inline script code
- var code="function _out(place){"+lookaheadMatch[4]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
- }
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
! Provided Locking Primitives
-The support library provides wrappers around some pthread locking primitives to make their usage more robust and easier.
-
-The basic concept is that each locking primitive is an object as well as each locker is implemented as object too, this adds a small convenience layer for robustness. We use ~NoBug to assert that locks are properly unlocked.
-
-!! Mutex
-We only support fast (non recursive, non errorcheck) mutexes for now. Debugging deadlock detection will be done in ~NoBug. If we need dynamic deadlock detection we will have to support errorcheck mutexes at demand. Same for recursive mutexes.
-
-!! Condition Variables
-Condition variables are simple synchronization devices, refer to the doxygen docs about using them. One needs to lock them when preparing to wait on them and finally unlock them. While signaling can optionally be done without a locker object (locking is implicit then).
-
-!! Read/Write Locks
-Similar to mutexes we support multiple-reader/one-writer locks, they can be used whenever many concurrent read accesses and rare write accesses are expected to some datastructure (profile this later). When congestion rather unexpected then prefer a mutex.
-
-! No Semaphores Rationale
-Semaphores have quite ugly semantics and are very hard to debug. For now (and likely forever) we will not to use them.
-
-! ~ToDo
-!! trylock and timedlock
-.. is not yet implemented but will be added in request.
-
-!! Barriers
-... will be added on request too.
-
-!! Thread Cancellation
-Thread cancellation policies are not yet finally decided, for now we consider threads uncancelable!
-
-
-
''[[Lumiera|index.html]]''
-SupportLibrary
-[[Threads and Locking]]
-[[Plugins]]
-[[ErrorHandling]]
-[[OS Services]]
-<<fullscreen>>
-
-
-
-
<!--{{{-->
-<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
-<!--}}}-->
-
-<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>My TiddlyWiki</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-	<div class='headerShadow'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-	<div class='headerForeground'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-</div>
-<!-- horizontal MainMenu -->
-<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
-<!-- original MainMenu menu -->
-<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
-<div id='sidebar'>
-	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-	<div id='messageArea'></div>
-	<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
-
/***
-|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
-|''Version:''|1.0.6 (2006-11-07)|
-|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
-|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
-|''Licence:''|[[BSD open source license]]|
-|''TiddlyWiki:''|2.0|
-|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
-!Table of Content<html><a name="TOC"/></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
-!Description<html><a name="Description"/></html>
-With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
-Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts, use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
-
-''Syntax:'' 
-|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
-|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//.|
-|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
-|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
-|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Applications<html><a name="Applications"/></html>
-!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
-Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
-
-Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Citation Index<html><a name="Citation"/></html>
-Create a tiddler "Citations" that contains your "citations". 
-Wrap every citation with a part and a proper name. 
-
-''Example''
-{{{
-<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
-in //Proc. ICSM//, 1998.</part>
-
-<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
-Thesis, Uni Stuttgart, 2002.</part>
-
-<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
-in //Proc. ICSM//, 1999.</part>
-}}}
-
-You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
-You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
-{{{
-* Item 1
-* Item 2
-* Item 3
-}}}
-into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
-
-Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
-
-''Example''
-{{{
-|!Subject|!Items|
-|subject1|<<tiddler ./Cell1>>|
-|subject2|<<tiddler ./Cell2>>|
-
-<part Cell1 hidden>
-* Item 1
-* Item 2
-* Item 3
-</part>
-...
-}}}
-
-Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
-
-BTW: The same approach can be used to create bullet lists with items that contain more than one line.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating Tabs<html><a name="Tabs"/></html>
-The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
-
-With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
-
-''Example''
-The standard tabs at the sidebar are defined by the following eight tiddlers:
-* SideBarTabs
-* TabAll
-* TabMore
-* TabMoreMissing
-* TabMoreOrphans
-* TabMoreShadowed
-* TabTags
-* TabTimeline
-
-Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
-{{{
-<<tabs txtMainTab 
- Timeline Timeline SideBarTabs/Timeline 
- All 'All tiddlers' SideBarTabs/All 
- Tags 'All tags' SideBarTabs/Tags 
- More 'More lists' SideBarTabs/More>>
-<part Timeline hidden><<timeline>></part>
-<part All hidden><<list all>></part>
-<part Tags hidden><<allTags>></part>
-<part More hidden><<tabs txtMoreTab 
- Missing 'Missing tiddlers' SideBarTabs/Missing 
- Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
- Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
-<part Missing hidden><<list missing>></part>
-<part Orphans hidden><<list orphans>></part>
-<part Shadowed hidden><<list shadowed>></part>
-}}}
-
-Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
-
-E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
-{{{
-<<forEachTiddler 
- sortBy 'tiddler.modified' descending 
- write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
-}}}
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Using Sliders<html><a name="Sliders"/></html>
-Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
-
-''Example''
-In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
-{{{
-...
-<<slider chkAboutDetails About/Details details "Click here to see more details">>
-<part Details hidden>
-To give you a better overview ...
-</part>
-...
-}}}
-
-Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Revision history<html><a name="Revisions"/></html>
-* v1.0.6 (2006-11-07)
-** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
-* v1.0.5 (2006-03-02)
-** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
-* v1.0.4 (2006-02-28)
-** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
-* v1.0.3 (2006-02-26)
-** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
-* v1.0.2 (2006-02-05)
-** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
-* v1.0.1 (2006-01-27)
-** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
-** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
-* v1.0.0 (2006-01-25)
-** initial version
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Code<html><a name="Code"/></html>
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-//{{{
-//============================================================================
-// PartTiddlerPlugin
-
-// Ensure that the PartTiddler Plugin is only installed once.
-//
-if (!version.extensions.PartTiddlerPlugin) {
-
-
-
-version.extensions.PartTiddlerPlugin = {
- major: 1, minor: 0, revision: 6,
- date: new Date(2006, 10, 7), 
- type: 'plugin',
- source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
-};
-
-if (!window.abego) window.abego = {};
-if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
-
-//============================================================================
-// Common Helpers
-
-// Looks for the next newline, starting at the index-th char of text. 
-//
-// If there are only whitespaces between index and the newline 
-// the index behind the newline is returned, 
-// otherwise (or when no newline is found) index is returned.
-//
-var skipEmptyEndOfLine = function(text, index) {
- var re = /(\n|[^\s])/g;
- re.lastIndex = index;
- var result = re.exec(text);
- return (result && text.charAt(result.index) == '\n') 
- ? result.index+1
- : index;
-}
-
-
-//============================================================================
-// Constants
-
-var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
-var partEndTagREString = "<\\/part>";
-var partEndTagString = "</part>";
-
-//============================================================================
-// Plugin Specific Helpers
-
-// Parse the parameters inside a <part ...> tag and return the result.
-//
-// @return [may be null] {partName: ..., isHidden: ...}
-//
-var parseStartTagParams = function(paramText) {
- var params = paramText.readMacroParams();
- if (params.length == 0 || params[0].length == 0) return null;
- 
- var name = params[0];
- var paramsIndex = 1;
- var hidden = false;
- if (paramsIndex < params.length) {
- hidden = params[paramsIndex] == "hidden";
- paramsIndex++;
- }
- 
- return {
- partName: name, 
- isHidden: hidden
- };
-}
-
-// Returns the match to the next (end or start) part tag in the text, 
-// starting the search at startIndex.
-// 
-// When no such tag is found null is returned, otherwise a "Match" is returned:
-// [0]: full match
-// [1]: matched "end" tag (or null when no end tag match)
-// [2]: matched "start" tag (or null when no start tag match)
-// [3]: content of start tag (or null if no start tag match)
-//
-var findNextPartEndOrStartTagMatch = function(text, startIndex) {
- var re = new RegExp(partEndOrStartTagRE);
- re.lastIndex = startIndex;
- var match = re.exec(text);
- return match;
-}
-
-//============================================================================
-// Formatter
-
-// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
-//
-// @return true if a complete part section (including the end tag) could be processed, false otherwise.
-//
-var handlePartSection = function(w) {
- var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
- if (!tagMatch) return false;
- if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
-
- // Parse the start tag parameters
- var arguments = parseStartTagParams(tagMatch[3]);
- if (!arguments) return false;
- 
- // Continue processing
- var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
- var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
- if (endMatch && endMatch[1]) {
- if (!arguments.isHidden) {
- w.nextMatch = startTagEndIndex;
- w.subWikify(w.output,partEndTagREString);
- }
- w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
- 
- return true;
- }
- return false;
-}
-
-config.formatters.push( {
- name: "part",
- match: "<part\\s+[^>]+>",
- 
- handler: function(w) {
- if (!handlePartSection(w)) {
- w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
- }
- }
-} )
-
-//============================================================================
-// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
-// as tiddlers.
-
-var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
-
-// Return the match to the first <part ...> tag of the text that has the
-// requrest partName.
-//
-// @return [may be null]
-//
-var findPartStartTagByName = function(text, partName) {
- var i = 0;
- 
- while (true) {
- var tagMatch = findNextPartEndOrStartTagMatch(text, i);
- if (!tagMatch) return null;
-
- if (tagMatch[2]) {
- // Is start tag
- 
- // Check the name
- var arguments = parseStartTagParams(tagMatch[3]);
- if (arguments && arguments.partName == partName) {
- return tagMatch;
- }
- }
- i += tagMatch[0].length;
- }
-}
-
-// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
-// object, using fullName as the Tiddler's title. 
-//
-// All remaining properties of the new Tiddler (tags etc.) are inherited from 
-// the parentTiddler.
-// 
-// @return [may be null]
-//
-var getPart = function(parentTiddler, partName, fullName) {
- var text = parentTiddler.text;
- var startTag = findPartStartTagByName(text, partName);
- if (!startTag) return null;
- 
- var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
- var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
-
- if (indexOfEndTag >= 0) {
- var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
- var partTiddler = new Tiddler();
- partTiddler.set(
- fullName,
- partTiddlerText,
- parentTiddler.modifier,
- parentTiddler.modified,
- parentTiddler.tags,
- parentTiddler.created);
- partTiddler.abegoIsPartTiddler = true;
- return partTiddler;
- }
- 
- return null;
-}
-
-// Hijack the store.fetchTiddler to recognize the "part" addresses.
-//
-
-var oldFetchTiddler = store.fetchTiddler ;
-store.fetchTiddler = function(title) {
- var result = oldFetchTiddler.apply(this, arguments);
- if (!result && title) {
- var i = title.lastIndexOf('/');
- if (i > 0) {
- var parentName = title.substring(0, i);
- var partName = title.substring(i+1);
- var parent = (parentName == ".") 
- ? currentParent 
- : oldFetchTiddler.apply(this, [parentName]);
- if (parent) {
- return getPart(parent, partName, parent.title+"/"+partName);
- }
- }
- }
- return result; 
-};
-
-
-// The user must not edit a readOnly/partTiddler
-//
-
-config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
-
-Tiddler.prototype.isReadOnly = function() {
- // Tiddler.isReadOnly was introduced with TW 2.0.6.
- // For older version we explicitly check the global readOnly flag
- if (config.commands.editTiddler.oldIsReadOnlyFunction) {
- if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
- } else {
- if (readOnly) return true;
- }
-
- return this.abegoIsPartTiddler;
-}
-
-config.commands.editTiddler.handler = function(event,src,title)
-{
- var t = store.getTiddler(title);
- // Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
- // or the tiddler is not readOnly
- if(!t || !t.abegoIsPartTiddler)
- {
- clearMessage();
- story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
- story.focusTiddler(title,"text");
- return false;
- }
-}
-
-// To allow the "./partName" syntax in macros we need to hijack 
-// the invokeMacro to define the "currentParent" while it is running.
-// 
-var oldInvokeMacro = window.invokeMacro;
-function myInvokeMacro(place,macro,params,wikifier,tiddler) {
- var oldCurrentParent = currentParent;
- if (tiddler) currentParent = tiddler;
- try {
- oldInvokeMacro.apply(this, arguments);
- } finally {
- currentParent = oldCurrentParent;
- }
-}
-window.invokeMacro = myInvokeMacro;
-
-// Scroll the anchor anchorName in the viewer of the given tiddler visible.
-// When no tiddler is defined use the tiddler of the target given event is used.
-window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
- var tiddlerElem = null;
- if (tiddler) {
- tiddlerElem = document.getElementById(story.idPrefix + tiddler);
- }
- if (!tiddlerElem && evt) {
- var target = resolveTarget(evt);
- tiddlerElem = story.findContainingTiddler(target);
- }
- if (!tiddlerElem) return;
-
- var children = tiddlerElem.getElementsByTagName("a");
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var name = child.getAttribute("name");
- if (name == anchorName) {
- var y = findPosY(child);
- window.scrollTo(0,y);
- return;
- }
- }
-}
-
-} // of "install only once"
-//}}}
-
-/***
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Licence and Copyright
-Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-Neither the name of abego Software nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-
-
-
Interfaces are declared in header files. They use some tool macros to give a convenient definition language.
-
-! Thoughts
-
-Interfaces are immutable with the exception that new functions may be added. Versioning should stay out of the view most of the time.
-
-! Brainstorming
-
-An interface needs a name and a version. They define a block where the actual function prototypes can be added. New prototypes have to be added at the end, existing prototypes must never be changed.
-{{{
-LUMIERA_INTERFACE(name, version,
-	...
-);
-}}}
-
-
-Each function prototype must be given with its different parts: return type, name, arguments list, and version.
-{{{
-	LUMIERA_IPROTO(ret, name, (args)),
-}}}
-
-
-! Example
-Together this would look like
-{{{
-LUMIERA_INTERFACE(foo, 1,
-	LUMIERA_IPROTO(void, bar, (void)),
-	LUMIERA_IPROTO(int, baz, (int i))
-);
-
-LUMIERA_INTERFACE(foo, 2,
-	LUMIERA_IPROTO(void, bar, (void)),
-	LUMIERA_IPROTO(int, baz, (float i))
-);
-}}}
-
-Note that the version 2 interface changed the parameter from int to float for the 'baz' function.
-
-The above gets expanded to:
-{{{
-struct lumiera_interface_foo_1
-{
-	struct lumiera_interface interface_header_;
-	void (*bar) (void);
-	int (*baz) (int i);
-};
-
-struct lumiera_interface_foo_2
-{
-	struct lumiera_interface interface_header_;
-	void (*bar) (void);
-	int (*baz) (float i);
-};
-}}}
-
-
-
A Plugin realizes an interface. This means that actual functions are mapped to the correspondending slots in the interface structure.
-
-{{{
-LUMIERA_INTERFACE_IMPLEMENT(interface, version, name,
-	/* TODO some hooks here */
-	functionnames...
-);
-}}}
-
-! Example
-{{{
-void
-my_bar_function (void)
-{
-	...
-}
-
-int
-my_baz_function (int i)
-{
-	...
-}
-
-int
-my_new_baz_function (float i)
-{
-	...
-}
-
-LUMIERA_INTERFACE_IMPLEMENT(foo, 1, myfoointerface,
-	/* TODO some hooks here */
-	my_bar_function,
-	my_baz_function
-);
-
-LUMIERA_INTERFACE_IMPLEMENT(foo, 2, myfoointerface,
-	/* TODO some hooks here */
-	my_bar_function,
-	my_new_baz_function
-);
-}}}
-
-The interface implementations expands to something like:
-{{{
-struct lumiera_interface_foo_1 myfoointerface_1 =
-{
-	/* TODO header initialization */
-	my_bar_function,
-	my_baz_function
-}
-
-struct lumiera_interface_foo_2 myfoointerface_2 =
-{
-	/* TODO header initialization */
-	my_bar_function,
-	my_new_baz_function
-}
-}}}
-
-
-
-
! Lumiera Plugin API
-
-There are only a few functions to manage Plugins. Actually a user requests interfaces. The libraries which implement Plugins are managed transparently.
-
-Interfaces are exported as instances and are not necessary singleton. This means that a single Plugin can export the same interface type several times under different names. The naming rules for interfaces need to be defined elsewhere.
-
-!! opening an Interface
-{{{
-LumieraInterface
-lumiera_interface_open (const char* plugin,
-                          const char* name,
-                          size_t min_revision);
-}}}
-
-!!! Parameters
-* ''{{{plugin}}}'' is the name of the Plugin whose interface to use. Plugins are looked up in $~LUMIERA_PLUGIN_PATH, which is a colon separated list of directories, and then in $plugin_install_dir which is the directory where standard plugins get installed when installing Lumiera (example: /usr/local/lib/lumiera/). The name itself can contain slashes, see PluginHierachy for details. It shall not include a library extension (.so). When NULL is passed, an interface from the main application is queried.
-* ''{{{name}}}'' is the name of the queried interface.
-* ''{{{min_revision}}}'' is the expected minimal size of the interface structure, since interfaces are extended by adding new protos at the end, the size gives a unique value for each new revision.
-
-!!! Semantic
-Interfaces can opened multiple times and need to be closed for each call to open.
-
-!!! Return
-This function returns a pointer to the requested interface on success or NULL in case of an error. See {{{lumiera_interface_error}}} about handing errors.
-
-!! closing an Interface
-{{{
-void
-lumiera_interface_close (LumieraInterface self);
-}}}
-
-!!! Parameters
-* ''{{{self}}}'' is the handle to the interface to be closed. It is safe to pass NULL. This makes the call just a no-op.
-
-!!! Semantic
-The interface handle must not be used after this function is called.
-
-This function always succeeds (or results in undefined behavior when the user passes an illegal parameter)
-
-!! calling functions
-
-Calling function is simply done by dereferencing the interface slots. See HowtoUsePlugin for an example.
-
-!! unload unused plugins
-Plugins which are no longer in use are not automatically unloaded. The user can use this functions to unload the Plugins.
-
-{{{
-int
-lumiera_plugin_unload (const char* plugin);
-}}}
-
-!!! Parameters
-* ''{{{plugin}}}'' name of the plugin to be unloaded
-
-!!! Semantic
-Tries to unload the named plugin. This only works when nothing else uses the Plugin.
-
-!!! Return
-Returns 0 on success or the number of active users on failure. See {{{lumiera_interface_error}}} about handing errors.
-
-
-!! expire unused plugins
-{{{
-void
-lumiera_plugin_expire (time_t age);
-}}}
-
-!!! Parameters
-* ''{{{age}}}'' time in seconds when the plugin was last used
-
-!!! Semantic
-Calls {{{lumiera_plugin_unload()}}} for each Plugin which has not been used for more than {{{age}}} seconds. This function might be infrequently called by the scheduler to remove things which are not needed (example: once a hour, remove plugins which have not been used for 2 hours).
-
-!!! Return
-always succeeds.
-
-!! error handling
-{{{
-const char*
-lumiera_plugin_error ();
-}}}
-
-!!! Semantic
-Indicate last error, reset error state. Errors are thread local.
-
-!!! Return
-Returns a pointer to the most recent error occurred in the plugin loader. This pointer is guaranteed to point to a C string with a unique comparable address. NULL if no error happened.
-
-Note that the error state gets cleared by calling this function. The application may store it temporary for further handling.
-
-!! C++ exceptions
-TODO
-
-
-
-
! Compatibility matrix
-
-|>|>|!Source compatibility|
-|!~~CALLER~~\^^CALLEE^^ *|OLD^^**^^|NEW^^**^^|
-|OLD|works|works<<br>>but a recent interface definition must be available|
-|NEW|works|works|
-|>|>|!Binary compatibility|
-|OLD|works|works|
-|NEW|caller gets 'revision not sufficient' at runtime<<br>>and should implement fallbacks|works|
-
-^^*^^) CALLER is the user of an interface, CALLEE is the interface provider (usually a plugin)
-^^**^^) OLD means an initial revision, NEW means some later revision of an interface
-
-! Observations
-
-Compiling a newer Plugin for some older main application release has some quirks (interface definitions are intended to be shipped with the main application). This should be rarely the case.
-
-When compiling, older Plugins should be updated to new interface revisions.
-Caller should provide a fallback to older interface revisions for binary compatibility.
-
-Generally, sources should just be properly maintained and updated to use the most recent interfaces revision.
-
-For binary compatibility everything will work well, provided that the caller kept proper fallback functionality for older interface revisions. Plugins which are independently distributed (packaged) in binary form don't need to be updated with every new main application release and just work.
-
-
-
-
-
Lumiera will use a very simple and language neutral plugin system. The focus is on easy and independent distribution of plugins and small specific interfaces. Ultimate flexibility is of second concern.
-
-! Concept
-Plugins are just shared libraries which offer well defined Interfaces. A Plugin may offer more than one interface and may in turn request/use interfaces from other Plugins or from the main application.
-
-! Interfaces
-Plugin interfaces are simple C structs with some metadata at the beginning and function prototypes added at the end. With some macros we can map simple functions to versioned interfaces. Compiled plugins will stay compatible even if the interface is extended, while sourcecode need maintenance.
-
-This fosters the idea of updating plugins when the source is available, while still having the ability to deploy packaged binary plugins which will be compatible with newer interface versions.
-
-The Plugin System is written in C with some helper preprocessor macros. There will be some support to handle C++ specialties.
-
-! Versioning
-
-Each interface/prototype is versioned. How this works together is explained in PluginVersioningCases. Version identifiers will be used to form a C identifier. I suggest to use a monotonic incrementing number, starting at 1 for versioning and maybe using a special number 0 for interfaces which are in development. When the interface development is finished the 0 has to be replaced by the next number in turn. This ensures that no-one accidentally uses/relies on an interface which is not yet well defined.
-
-! Plugin Support includes
-* [[An interface definition language|PluginInterfaceDefinition]]
-* [[An interface implementation language|PluginInterfaceImplementation]]
-* [[Selecting interface based on capabilities|SelectingInterfaces]]
-* [[Library support to access plugins|PluginLibrary]]
-
-! Tutorial, how to use Plugins
-* [[Define an interface|HowtoDefineInterface]]
-* Implement an Plugin with an interface in
-** [[C|HowtoCPlugin]]
-** [[C++|HowtoCppPlugin]]
-* [[Use this Plugin|HowtoUsePlugin]] 
-
-!! Planned
-* enumerating interfaces of a plugin
-* pattern matching interfaces -- find the best possible interface
-
-
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
! Memory allocation
-* lumiera_malloc()
-  Succceeds or dies, no need for error handling.
-
-! String functions
-Small wrapers and extensions to the string.h functions, handle NULL pointers gracefully.
-* lumiera_strndup()
-  Succceeds or dies, no need for error handling.
-* lumiera_strncmp() lumiera_streq()
-  Optimized comparing same addresses.
-
-! Round robin temporary buffers
-Provides 64 buffers per thread which are recycled with each use, the idea here is to have fast buffers for temporal data without need for explicit heap management or stack waste.
-
-* lumiera_tmpbuf_provide ()
- Acquire a temporary buffer. doesn't need to be freed. Stays valid for the next 63 tmpbuf calls.
-
-* lumiera_tmpbuf_strndup ()
- Duplicate string to a tmpbuf.
-
-* lumiera_tmpbuf_sprintf ()
- Construct a string in a tmpbuf.
-
-
-
-
Each Plugin can export different Interfaces, even same interfaces with different capabilities like a effect for different color models or such.
-
-Plugins announce their interfaces and capabilities and we implement a deduction system which chooses the best matching interface for the task.
-
-
-
-
things we find useful
-
-
-
Support Library
-
-
-
/***
-
-''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
-
-|Name|SplashScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#SplashScreenPlugin|
-|Version|0.21 |
-|Requires|~TW2.08+|
-!Description:
-Provides a simple splash screen that is visible while the TW is loading.
-
-!Installation
-Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
-
-!Customizing
-Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
-
-!History
-* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
-* 26-06-06 : version 0.2, first release
-
-!Code
-***/
-//{{{
-var old_lewcid_splash_restart=restart;
-
-restart = function()
-{   if (document.getElementById("SplashScreen"))
-        document.getElementById("SplashScreen").style.display = "none";
-      if (document.getElementById("contentWrapper"))
-        document.getElementById("contentWrapper").style.display = "block";
-    
-    old_lewcid_splash_restart();
-   
-    if (splashScreenInstall)
-       {if(config.options.chkAutoSave)
-			{saveChanges();}
-        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
-        }
-}
-
-
-var oldText = store.getTiddlerText("MarkupPreHead");
-if (oldText.indexOf("SplashScreen")==-1)
-   {var siteTitle = store.getTiddlerText("SiteTitle");
-   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
-   if (! store.tiddlerExists("MarkupPreHead"))
-       {var myTiddler = store.createTiddler("MarkupPreHead");}
-   else
-      {var myTiddler = store.getTiddler("MarkupPreHead");}
-      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
-      store.setDirty(true);
-      var splashScreenInstall = true;
-}
-//}}}
-
-
-
/*{{{*/
-/* a contrasting background so I can see where one tiddler ends and the other begins */
-body {
-	background: [[ColorPalette::TertiaryLight]];
-}
-
-/* sexy colours and font for the header */
-.headerForeground {
-	color: [[ColorPalette::PrimaryPale]];
-}
-.headerShadow, .headerShadow a {
-	color: [[ColorPalette::PrimaryMid]];
-}
-.headerForeground, .headerShadow {
-	padding: 1em 1em 0;
-	font-family: 'Trebuchet MS' sans-serif;
-	font-weight:bold;
-}
-.headerForeground .siteSubtitle {
-	color: [[ColorPalette::PrimaryLight]];
-}
-.headerShadow .siteSubtitle {
-	color: [[ColorPalette::PrimaryMid]];
-}
-
-/* make shadow go and down right instead of up and left */
-.headerShadow {
-	left: 2px;
-	top: 3px;
-}
-
-/* prefer monospace for editing */
-.editor textarea {
-	font-family: 'Consolas' monospace;
-}
-
-/* sexy tiddler titles */
-.title {
-	font-size: 250%;
-	color: [[ColorPalette::PrimaryLight]];
-	font-family: 'Trebuchet MS' sans-serif;
-}
-
-/* more subtle tiddler subtitle */
-.subtitle {
-	padding:0px;
-	margin:0px;
-	padding-left:0.5em;
-	font-size: 90%;
-	color: [[ColorPalette::TertiaryMid]];
-}
-.subtitle .tiddlyLink {
-	color: [[ColorPalette::TertiaryMid]];
-}
-
-/* a little bit of extra whitespace */
-.viewer {
-	padding-bottom:3px;
-}
-
-/* don't want any background color for headings */
-h1,h2,h3,h4,h5,h6 {
-	background: [[ColorPalette::Background]];
-	color: [[ColorPalette::Foreground]];
-}
-
-/* give tiddlers 3d style border and explicit background */
-.tiddler {
-	background: [[ColorPalette::Background]];
-	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
-	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
-	margin-bottom: 1em;
-	padding-bottom: 2em;
-}
-
-/* make options slider look nicer */
-#sidebarOptions .sliderPanel {
-	border:solid 1px [[ColorPalette::PrimaryLight]];
-}
-
-
-/* the borders look wrong with the body background */
-#sidebar .button {
-	border-style: none;
-}
-
-/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
-.tagglyTagged li.listTitle {
-	display:none
-}
-.tagglyTagged li {
-	display: inline; font-size:90%;
-}
-.tagglyTagged ul {
-	margin:0px; padding:0px;
-}
-
-/* this means you can put line breaks in SidebarOptions for readability */
-#sidebarOptions br {
-	display:none;
-}
-/* undo the above in OptionsPanel */
-#sidebarOptions .sliderPanel br {
-	display:inline;
-}
-
-/* horizontal main menu stuff */
-#displayArea {
-	margin: 1em 15.7em 0em 1em; /* use the freed up space */
-}
-#topMenu br {
-	display: none;
-}
-#topMenu {
-	background: [[ColorPalette::PrimaryMid]];
-	color:[[ColorPalette::PrimaryPale]];
-}
-#topMenu {
-	padding:2px;
-}
-#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
-	margin-left: 0.5em;
-	margin-right: 0.5em;
-	padding-left: 3px;
-	padding-right: 3px;
-	color: [[ColorPalette::PrimaryPale]];
-	font-size: 115%;
-}
-#topMenu .button:hover, #topMenu .tiddlyLink:hover {
-	background: [[ColorPalette::PrimaryDark]];
-}
-
-/* make it print a little cleaner */
-@media print {
-	#topMenu {
-		display: none ! important;
-	}
-	/* not sure if we need all the importants */
-	.tiddler {
-		border-style: none ! important;
-		margin:0px ! important;
-		padding:0px ! important;
-		padding-bottom:2em ! important;
-	}
-	.tagglyTagging .button, .tagglyTagging .hidebutton {
-		display: none ! important;
-	}
-	.headerShadow {
-		visibility: hidden ! important;
-	}
-	.tagglyTagged .quickopentag, .tagged .quickopentag {
-		border-style: none ! important;
-	}
-	.quickopentag a.button, .miniTag {
-		display: none ! important;
-	}
-}
-/*}}}*/
-
-
-
-
The Support Library contains all tools we need at various places, but by themselves don't defines a subsystem on their own.
-
-These things are:
-* [[a Plugin loader|Plugins]]
-* [[ErrorHandling]]
-* a wrapper for POSIX Threads
-** Thread creation joining and canceling
-** [[Locking primitives like Condition variables and Mutexes|LockingPrimitives]]
-* [[Some tools and wrapers around the C library|SafeCLib]]
-* [[O(1) hashtable using Cuckoo hashing|Cuckoo]]
-
-(... to be continued)
-
-
-
-
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
-
- - - - - - - - - - diff --git a/wiki/todo.html b/wiki/todo.html deleted file mode 100644 index 73d716def..000000000 --- a/wiki/todo.html +++ /dev/null @@ -1,11668 +0,0 @@ - - - - - - - - - - - -
My TiddlyWiki is loading ...

Requires Javascript.
- - ToDo - Roadmap and tasks - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #8cf
-PrimaryLight: #18f
-PrimaryMid: #04b
-PrimaryDark: #014
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
/*{{{*/
-body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-
-a {color:[[ColorPalette::PrimaryMid]];}
-a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
-a img {border:0;}
-
-h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
-h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
-h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
-
-.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
-.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
-.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
-
-.header {background:[[ColorPalette::PrimaryMid]];}
-.headerShadow {color:[[ColorPalette::Foreground]];}
-.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
-.headerForeground {color:[[ColorPalette::Background]];}
-.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
-
-.tabSelected{color:[[ColorPalette::PrimaryDark]];
-	background:[[ColorPalette::TertiaryPale]];
-	border-left:1px solid [[ColorPalette::TertiaryLight]];
-	border-top:1px solid [[ColorPalette::TertiaryLight]];
-	border-right:1px solid [[ColorPalette::TertiaryLight]];
-}
-.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
-.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
-.tabContents .button {border:0;}
-
-#sidebar {}
-#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
-#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
-#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
-
-.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
-.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
-.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
-	border:1px solid [[ColorPalette::PrimaryMid]];}
-.wizardStep.wizardStepDone {background::[[ColorPalette::TertiaryLight]];}
-.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
-.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
-.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
-	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
-.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
-.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
-	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
-
-#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
-#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
-
-.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
-
-.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
-.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
-.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
-.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
-.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
-
-.tiddler .defaultCommand {font-weight:bold;}
-
-.shadow .title {color:[[ColorPalette::TertiaryDark]];}
-
-.title {color:[[ColorPalette::SecondaryDark]];}
-.subtitle {color:[[ColorPalette::TertiaryDark]];}
-
-.toolbar {color:[[ColorPalette::PrimaryMid]];}
-.toolbar a {color:[[ColorPalette::TertiaryLight]];}
-.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
-.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
-
-.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
-.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
-.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
-.tagging .button, .tagged .button {border:none;}
-
-.footer {color:[[ColorPalette::TertiaryLight]];}
-.selected .footer {color:[[ColorPalette::TertiaryMid]];}
-
-.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
-.sparktick {background:[[ColorPalette::PrimaryDark]];}
-
-.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
-.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
-.lowlight {background:[[ColorPalette::TertiaryLight]];}
-
-.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
-
-.imageLink, #displayArea .imageLink {background:transparent;}
-
-.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
-
-.viewer .listTitle {list-style-type:none; margin-left:-2em;}
-.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
-.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
-.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
-.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
-
-.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
-.viewer code {color:[[ColorPalette::SecondaryDark]];}
-.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
-
-.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
-
-.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
-.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
-.editorFooter {color:[[ColorPalette::TertiaryMid]];}
-
-#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
-#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
-#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
-#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
-#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
-#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
-.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
-.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
-#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
-/*}}}*/
-
-
-
/*{{{*/
-* html .tiddler {height:1%;}
-
-body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
-
-h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
-h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
-h4,h5,h6 {margin-top:1em;}
-h1 {font-size:1.35em;}
-h2 {font-size:1.25em;}
-h3 {font-size:1.1em;}
-h4 {font-size:1em;}
-h5 {font-size:.9em;}
-
-hr {height:1px;}
-
-a {text-decoration:none;}
-
-dt {font-weight:bold;}
-
-ol {list-style-type:decimal;}
-ol ol {list-style-type:lower-alpha;}
-ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol {list-style-type:decimal;}
-ol ol ol ol ol {list-style-type:lower-alpha;}
-ol ol ol ol ol ol {list-style-type:lower-roman;}
-ol ol ol ol ol ol ol {list-style-type:decimal;}
-
-.txtOptionInput {width:11em;}
-
-#contentWrapper .chkOptionInput {border:0;}
-
-.externalLink {text-decoration:underline;}
-
-.indent {margin-left:3em;}
-.outdent {margin-left:3em; text-indent:-3em;}
-code.escaped {white-space:nowrap;}
-
-.tiddlyLinkExisting {font-weight:bold;}
-.tiddlyLinkNonExisting {font-style:italic;}
-
-/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
-a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
-
-#mainMenu .tiddlyLinkExisting,
-	#mainMenu .tiddlyLinkNonExisting,
-	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
-#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
-
-.header {position:relative;}
-.header a:hover {background:transparent;}
-.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
-.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
-
-.siteTitle {font-size:3em;}
-.siteSubtitle {font-size:1.2em;}
-
-#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
-
-#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
-#sidebarOptions {padding-top:0.3em;}
-#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
-#sidebarOptions input {margin:0.4em 0.5em;}
-#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
-#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
-#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
-#sidebarTabs .tabContents {width:15em; overflow:hidden;}
-
-.wizard {padding:0.1em 1em 0em 2em;}
-.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
-.wizardStep {padding:1em 1em 1em 1em;}
-.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
-.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
-.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
-.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
-
-#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
-.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
-#messageArea a {text-decoration:underline;}
-
-.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
-.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
-
-.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
-.popup .popupMessage {padding:0.4em;}
-.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
-.popup li.disabled {padding:0.4em;}
-.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
-.listBreak {font-size:1px; line-height:1px;}
-.listBreak div {margin:2px 0;}
-
-.tabset {padding:1em 0em 0em 0.5em;}
-.tab {margin:0em 0em 0em 0.25em; padding:2px;}
-.tabContents {padding:0.5em;}
-.tabContents ul, .tabContents ol {margin:0; padding:0;}
-.txtMainTab .tabContents li {list-style:none;}
-.tabContents li.listLink { margin-left:.75em;}
-
-#contentWrapper {display:block;}
-#splashScreen {display:none;}
-
-#displayArea {margin:1em 17em 0em 14em;}
-
-.toolbar {text-align:right; font-size:.9em;}
-
-.tiddler {padding:1em 1em 0em 1em;}
-
-.missing .viewer,.missing .title {font-style:italic;}
-
-.title {font-size:1.6em; font-weight:bold;}
-
-.missing .subtitle {display:none;}
-.subtitle {font-size:1.1em;}
-
-.tiddler .button {padding:0.2em 0.4em;}
-
-.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
-.isTag .tagging {display:block;}
-.tagged {margin:0.5em; float:right;}
-.tagging, .tagged {font-size:0.9em; padding:0.25em;}
-.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
-.tagClear {clear:both;}
-
-.footer {font-size:.9em;}
-.footer li {display:inline;}
-
-.annotation {padding:0.5em; margin:0.5em;}
-
-* html .viewer pre {width:99%; padding:0 0 1em 0;}
-.viewer {line-height:1.4em; padding-top:0.5em;}
-.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
-.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
-.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
-
-.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
-.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
-table.listView {font-size:0.85em; margin:0.8em 1.0em;}
-table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
-
-.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
-.viewer code {font-size:1.2em; line-height:1.4em;}
-
-.editor {font-size:1.1em;}
-.editor input, .editor textarea {display:block; width:100%; font:inherit;}
-.editorFooter {padding:0.25em 0em; font-size:.9em;}
-.editorFooter .button {padding-top:0px; padding-bottom:0px;}
-
-.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
-
-.sparkline {line-height:1em;}
-.sparktick {outline:0;}
-
-.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
-.zoomer div {padding:1em;}
-
-* html #backstage {width:99%;}
-* html #backstageArea {width:99%;}
-#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageToolbar {position:relative;}
-#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
-#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
-#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
-#backstage {position:relative; width:100%; z-index:50;}
-#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
-.backstagePanelFooter {padding-top:0.2em; float:right;}
-.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
-#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
-
-.whenBackstage {display:none;}
-.backstageVisible .whenBackstage {display:block;}
-/*}}}*/
-
-
-
/***
-StyleSheet for use when a translation requires any css style changes.
-This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
-***/
-
-/*{{{*/
-body {font-size:0.8em;}
-
-#sidebarOptions {font-size:1.05em;}
-#sidebarOptions a {font-style:normal;}
-#sidebarOptions .sliderPanel {font-size:0.95em;}
-
-.subtitle {font-size:0.8em;}
-
-.viewer table.listView {font-size:0.95em;}
-
-.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
-/*}}}*/
-
-
-
/*{{{*/
-@media print {
-#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton {display: none ! important;}
-#displayArea {margin: 1em 1em 0em 1em;}
-/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
-noscript {display:none;}
-}
-/*}}}*/
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-<div class='headerShadow'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-<div class='headerForeground'>
-<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-</div>
-</div>
-<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
-<div id='sidebar'>
-<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-<div id='messageArea'></div>
-<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
-<div class='title' macro='view title'></div>
-<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
-<div class='tagging' macro='tagging'></div>
-<div class='tagged' macro='tags'></div>
-<div class='viewer' macro='view text wikified'></div>
-<div class='tagClear'></div>
-<!--}}}-->
-
-
-
<!--{{{-->
-<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
-<div class='title' macro='view title'></div>
-<div class='editor' macro='edit title'></div>
-<div macro='annotations'></div>
-<div class='editor' macro='edit text'></div>
-<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
-<!--}}}-->
-
-
-
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
-* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
-* MainMenu: The menu (usually on the left)
-* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
-You'll also need to enter your username for signing your edits: <<option txtUserName>>
-
-
-
These InterfaceOptions for customising TiddlyWiki are saved in your browser
-
-Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)
-
-<<option txtUserName>>
-<<option chkSaveBackups>> SaveBackups
-<<option chkAutoSave>> AutoSave
-<<option chkRegExpSearch>> RegExpSearch
-<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
-<<option chkAnimate>> EnableAnimations
-
-----
-Also see AdvancedOptions
-
-
- -
-
-
A task has a description, an estimate of how long it will take, and a record of how much time you have spent on it so far.  Here's an example, which shows a task estimated at 3 hours, with 1 hour spent on it, and ''2'' hours remaining:
-<<<
-<<task 3 3 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you hover the mouse over any part of the task -- the bullet, the description, or any of the numeric cells -- a tip will appear explaining it.
-
-Try modifying the time spent.  Suppose you've just spent one more hour and want to record it.  Just click on the second yellow cell, and enter "+1" (sans the quote marks, of course) in the popup window.  Watch the time remaining go down to 1 hour.
-
-In reality, I originally estimated this task at a half-hour, but it ended up taking 3.5 hours.  The macro also tracks your original estimate, if it is different from the current estimate, in a fourth cell like this:
-<<<
-<<task 0.5 2 1>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-You can adjust the current estimate in the same way as you adjusted the time spent.  Click on the current estimate cell (the first yellow cell), and change it to 2.5 hours by typing "2.5" or "+.5".
-
-You can also adjust the time remaining, which will modify either the estimate (if the time remaining increases) or the time spent (if it decreases).  Click on the time remaining and add an hour by typing "+1".
-
-When the time remaining goes to zero, the task is considered complete:
-<<<
-<<task 0.5 3.5 3.5>> Add a double-click handler to the description cell that opens the editor and selects the text
-<<<
-If you haven't already done so, try double-clicking the description.  Yes, it really does open up the editor and select just the text of the description.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
A task's description is a single wikified line, so it can contain any formatting that can be specified on one line:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 0.5>> Put tasksum on the ViewTemplate.
-<<<
-You can specify just the description of a task, and leave it unestimated.  Click the question mark to enter the estimate:
-<<<
-<<task>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-As this task implies, you can enter two values in the popup when you click on any of the time cells.  Separate them with spaces and/or a comma.  Experiment:
-<<<
-<<task 1>> Beef up the time click handlers to allow entry of ''two'' values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<<
-Finally, if you haven't already figured this out, you can double-click on a task's bullet to mark it complete, with the current estimate entered as the time spent.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
If you've been paying attention, you've noticed that I haven't discussed the actual adding of calls to the task macro within your tiddlers -- it's all been about modifying tasks that were already there.  That's because adding tasks via the taskadder macro is much easier and more intuitive than adding them by hand.
-
-And setting up a taskadder is simplicity itself.  Just add {{{<<taskadder>>}}} to your tiddler.  You will see this:
-<<<
-<<taskadder>>
-<<<
-Just type a task description into the first field, and your initial estimate for how long it will take into the second field.  Click the "add task" button, or just hit Enter in either of the fields, to add the new task into the tiddler.  Notice that you can just start typing a new task as soon as you're done entering the first one.
-
-You can have as many taskadders as you like in any tiddler.  The last one you used will capture the keyboard focus when it is redisplayed, meaning you can type a series of tasks without using the mouse.  Try adding some tasks here and in the above adder:
-<<<
-<<taskadder>>
-<<<
-Notice that the one you just used takes focus when this tiddler is redisplayed.
-
-A taskadder by default adds tasks above itself.  You can make it add them below by adding a {{{below}}} argument to the macro call:
-<<<
-<<taskadder below>>
-<<<
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
In this tutorial, we've been looking mostly at individual tasks.  In real life, though, you'll typically have a series of them, or even several series of them in the same tiddler.  In these cases you want a summary that tells you -- at a minimum -- how much time you still expect to spend on these tasks.
-
-To get such a summary, just add {{{<<tasksum start>>}}} before the tasks and {{{<<tasksum end>>}}} after them.  Here's an example:
-<<<
-<<tasksum start>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end>>
-<<<
-If you'd rather have the summary at the top, just add {{{here}}} to the start call, ie {{{<<tasksum start here>>}}}.
-<<<
-<<tasksum start here>>
-<<task 0.25 0.25 0.25>> Add tooltips to the various cells
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<tasksum end>>
-<<<
-You can nest these things if you like, just be sure to match starts and ends:
-<<<
-<<tasksum start here>>
-* Time cell manipulation:<<tasksum start>>
-<<task 1 0.75 0.75>> Figure out how to add auto-updating click handlers to the time cells
-<<task 2 2 0>> Add simple click handlers to cur, spent, rem: just allow direct setting of values
-<<task 1 1 0>> Beef up the time click handlers to allow entry of two values each: cur&spent, spent&rem. Add click handler to done tasks' spent cells too, to reopen them (like with +0, 1).
-<<task 1 1 0>> Beef up the time click handlers to handle leading + or -
-<<tasksum end "Cell manipulation:">>
-<<br>>
-* Double-click handling:<<tasksum start>>
-<<task 1 3.5 2.5>> Add a double-click handler to the desc cell that opens the editor and selects the text
-<<task 1 1 0>> Add a double-click handler to the status cell that functions like typing 0 into the rem cell
-<<tasksum end "Double-clicks:">>
-
-<<tasksum end>>
-<<<
-Finally, the simplest way to use tasksum is to add it to your view template.  See TaskSummaryViewTemplate for an example template.  Note that if no tasks are present between the start and end, nothing is displayed.
-
-----
-To continue, click the down-arrow and choose another section: <<tag TaskMacroTutorial>>
-
-
-
The TaskMacroPlugin can be installed like any other TiddlyWiki plugin, and used without further effort.  However, there are two issues that may affect you.  (To get started with a brand new wiki that does not have these issues, consider downloading the [[empty LabWiki|empty_labwiki.html]].)
-# The task macros don't play nicely with the default TiddlyWiki display of tags.  In the default view template, a tiddler's list of tags is shown in a little box that floats in the upper right corner of the tiddler.  However, this little box may interfere with the tables used by the task macros.  In Firefox, the tables are drawn right over the top of the tag box, rendering both of them illegible.  In Internet Explorer, the tag box forces the tables to be pushed down below the box, which can waste a lot of space.<<br>><<br>>Thus, I recommend changing your view template to eliminate the little box.  If you use Simon Baird's [[TagglyTagging|http://simonbaird.com/mptw/#TagglyTagging]] (as LabWiki does), then my TaskSummaryViewTemplate might be a good alternative.  Simply import it into your wiki and rename it to ViewTemplate.  This template also demonstrates how to incorporate the tasksum macro into every tiddler so any tiddler with tasks has a summary at the top.<<br>><<br>>
-# Most view templates also add a minus sign ("-") before the "close" command.  TiddlyWiki interprets this to mean that you want the close command to be executed if you hit the Escape key from within the tiddler.<<br>><<br>>However, most tiddlers never have focus, and so never give you the opportunity to try it out.  But if you have a taskadder in your tiddler, then you suddenly enable this feature -- and you probably don't want it.  It means that if you type a nice long task description and then hit Escape, that description will be lost and the tiddler will be closed.  So I recommend that you remove the minus sign from the view template's menu altogether, as I have done in LabWiki's own ViewTemplate.
-
-----
-This ends the tutorial.  To go back to any previous section, click the down-arrow and choose it: <<tag TaskMacroTutorial>>
-
-
-
PageTemplate
-|>|SiteTitle - SiteSubtitle|
-|>|MainMenu|
-|DefaultTiddlers<<br>><<br>><<br>>ViewTemplate<<br>><<br>>EditTemplate|SideBarOptions|
-|~|OptionsPanel|
-|~|SideBarTabs|
-|~|AdvancedOptions|
-|~|<<tiddler Configuration.SideBarTabs>>|
-
-''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
-
-ColorPalette
-
-SiteUrl
-
-
-
/***
-|Name|BetterTimelineMacro|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#BetterTimelineMacro|
-|Version|0.5 beta|
-|Requires|~TW2.x|
-!!!Description:
-A replacement for the core timeline macro that offers more features:
-*list tiddlers with only specfic tag
-*exclude tiddlers with a particular tag
-*limit entries to any number of days, for example one week
-*specify a start date for the timeline, only tiddlers after that date will be listed.
-
-!!!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!!!Syntax:
-{{{<<timeline better:true>>}}}
-''the param better:true enables the advanced features, without it you will get the old timeline behaviour.''
-
-additonal params:
-(use only the ones you want)
-{{{<<timeline better:true  onlyTag:Tag1 excludeTag:Tag2 sortBy:modified/created firstDay:YYYYMMDD maxDays:7 maxEntries:30>>}}}
-
-''explanation of syntax:''
-onlyTag: only tiddlers with this tag will be listed. Default is to list all tiddlers.
-excludeTag: tiddlers with this tag will not be listed.
-sortBy: sort tiddlers by date modified or date created. Possible values are modified or created.
-firstDay: useful for starting timeline from a specific date. Example: 20060701 for 1st of July, 2006
-maxDays: limits timeline to include only tiddlers from the specified number of days. If you use a value of 7 for example, only tiddlers from the last 7 days will be listed.
-maxEntries: limit the total number of entries in the timeline.
-
-
-!!!History:
-*28-07-06: ver 0.5 beta, first release
-
-!!!Code
-***/
-//{{{
-// Return the tiddlers as a sorted array
-TiddlyWiki.prototype.getTiddlers = function(field,excludeTag,includeTag)
-{
-          var results = [];
-          this.forEachTiddler(function(title,tiddler)
-          {
-          if(excludeTag == undefined || tiddler.tags.find(excludeTag) == null)
-                        if(includeTag == undefined || tiddler.tags.find(includeTag)!=null)
-                                      results.push(tiddler);
-          });
-          if(field)
-                   results.sort(function (a,b) {if(a[field] == b[field]) return(0); else return (a[field] < b[field]) ? -1 : +1; });
-          return results;
-}
-
-
-
-//this function by Udo
-function getParam(params, name, defaultValue)
-{
-          if (!params)
-          return defaultValue;
-          var p = params[0][name];
-          return p ? p[0] : defaultValue;
-}
-
-window.old_timeline_handler= config.macros.timeline.handler;
-config.macros.timeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
-{
-          var args = paramString.parseParams("list",null,true);
-          var betterMode = getParam(args, "better", "false");
-          if (betterMode == 'true')
-          {
-          var sortBy = getParam(args,"sortBy","modified");
-          var excludeTag = getParam(args,"excludeTag",undefined);
-          var includeTag = getParam(args,"onlyTag",undefined);
-          var tiddlers = store.getTiddlers(sortBy,excludeTag,includeTag);
-          var firstDayParam = getParam(args,"firstDay",undefined);
-          var firstDay = (firstDayParam!=undefined)? firstDayParam: "00010101";
-          var lastDay = "";
-          var field= sortBy;
-          var maxDaysParam = getParam(args,"maxDays",undefined);
-          var maxDays = (maxDaysParam!=undefined)? maxDaysParam*24*60*60*1000: (new Date()).getTime() ;
-          var maxEntries = getParam(args,"maxEntries",undefined);
-          var last = (maxEntries!=undefined) ? tiddlers.length-Math.min(tiddlers.length,parseInt(maxEntries)) : 0;
-          for(var t=tiddlers.length-1; t>=last; t--)
-                  {
-                  var tiddler = tiddlers[t];
-                  var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
-                  if ((theDay>=firstDay)&& (tiddler[field].getTime()> (new Date()).getTime() - maxDays))
-                     {
-                     if(theDay != lastDay)
-                               {
-                               var theDateList = document.createElement("ul");
-                               place.appendChild(theDateList);
-                               createTiddlyElement(theDateList,"li",null,"listTitle",tiddler[field].formatString(this.dateFormat));
-                               lastDay = theDay;
-                               }
-                  var theDateListItem = createTiddlyElement(theDateList,"li",null,"listLink",null);
-                  theDateListItem.appendChild(createTiddlyLink(place,tiddler.title,true));
-                  }
-                  }
-          }
-
-          else
-              {
-              window.old_timeline_handler.apply(this,arguments);
-              }
-}
-//}}}
-
-
-
Background: #fff
-Foreground: #000
-PrimaryPale: #aaa
-PrimaryLight: #f00
-PrimaryMid: #400
-PrimaryDark: #000
-SecondaryPale: #ffc
-SecondaryLight: #fe8
-SecondaryMid: #db4
-SecondaryDark: #841
-TertiaryPale: #eee
-TertiaryLight: #ccc
-TertiaryMid: #999
-TertiaryDark: #666
-Error: #f88
-
-
-
[[Overview]]
-
-
-
/***
-|Name|FullScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#FullScreenPlugin|
-|Version|1.1|
-|Requires|~TW2.x|
-!Description:
-Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
-
-!Demo:
-Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
-
-!Installation:
-Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
-Edit the ViewTemplate to add the fullscreen command to the toolbar.
-
-!History:
-*25-07-06: ver 1.1
-*20-07-06: ver 1.0
-
-!Code
-***/
-//{{{
-var lewcidFullScreen = false;
-
-config.commands.fullscreen =
-{
-            text:" ↕ ",
-            tooltip:"Fullscreen mode"
-};
-
-config.commands.fullscreen.handler = function (event,src,title)
-{
-            if (lewcidFullScreen == false)
-               {
-                lewcidFullScreen = true;
-                setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
-               }
-            else
-               {
-                lewcidFullScreen = false;
-                setStylesheet(' ',"lewcidFullScreenStyle");
-               }
-}
-
-config.macros.fullscreen={};
-config.macros.fullscreen.handler =  function(place,macroName,params,wikifier,paramString,tiddler)
-{
-        var label = params[0]||" ↕ ";
-        var tooltip = params[1]||"Fullscreen mode";
-        createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
-}
-
-var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
-Story.prototype.closeTiddler =function(title,animate,slowly)
-{
-           lewcid_fullscreen_closeTiddler.apply(this,arguments);
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-
-
-Slider.prototype.lewcidStop = Slider.prototype.stop;
-Slider.prototype.stop = function()
-{
-           this.lewcidStop();
-           if (story.isEmpty() && lewcidFullScreen == true)
-              config.commands.fullscreen.handler();
-}
-//}}}
-
-
-
/***
-''InlineJavascriptPlugin for ~TiddlyWiki version 1.2.x and 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.TiddlyTools.com/#InlineJavascriptPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-{{{
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-}}}
-<script>alert('InlineJavascriptPlugin: this is a demonstration message');</script>
-
-dynamic output:
-{{{
-<script>return (new Date()).toString();</script>
-}}}
-<script>return (new Date()).toString();</script>
-
-wikified dynamic output:
-{{{
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-}}}
-<script>return "link to current user: [["+config.options.txtUserName+"]]";</script>
-
-dynamic output using 'place' to get size information for current tiddler
-{{{
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-}}}
-<script>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-
-creating an 'onclick' button/link that runs a script
-{{{
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-}}}
-<script label="click here">
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-
-loading a script from a source url
-{{{
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-}}}
-where http://www.TiddlyTools.com/demo.js contains:
->function demo() { alert('this output is from demo(), defined in demo.js') }
->alert('InlineJavascriptPlugin: demo.js has been loaded');
-<script src="demo.js">return "loading demo.js..."</script>
-<script label="click to execute demo() function">demo()</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.05 [1.4.0]''
-added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]''
-when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]''
-for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content
-Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]''
-handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]''
-pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]''
-initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 4, revision: 0, date: new Date(2006,1,5)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[2] && lookaheadMatch[3]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[3]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else if (lookaheadMatch[3]) { // run inline script code
- var code="function _out(place){"+lookaheadMatch[3]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output);
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
-
/***
-|''Name:''|InlineJavascriptPlugin|
-|''Source:''|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
-|''Author:''|Eric Shulman - ELS Design Studios|
-|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
-|''~CoreVersion:''|2.0.10|
-
-Insert Javascript executable code directly into your tiddler content. Lets you ''call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
-!!!!!Usage
-<<<
-When installed, this plugin adds new wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be treated as embedded javascript and executed each time the tiddler is rendered.
-
-''Deferred execution from an 'onClick' link''
-By including a label="..." parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.
-
-''External script source files:''
-You can also load javascript from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
-
-''Display script source in tiddler output''
-By including the keyword parameter "show", in the initial {{{<script>}}} marker, the plugin will include the script source code in the output that it displays in the tiddler.
-
-''Defining javascript functions and libraries:''
-Although the external javascript file is loaded while the tiddler content is being rendered, any functions it defines will not be available for use until //after// the rendering has been completed. Thus, you cannot load a library and //immediately// use it's functions within the same tiddler. However, once that tiddler has been loaded, the library functions can be freely used in any tiddler (even the one in which it was initially loaded).
-
-To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that will be rendered as soon as your TiddlyWiki document is opened. For example, you could put your {{{<script src="..."></script>}}} syntax into a tiddler called LoadScripts, and then add {{{<<tiddler LoadScripts>>}}} in your MainMenu tiddler.
-
-Since the MainMenu is always rendered immediately upon opening your document, the library will always be loaded before any other tiddlers that rely upon the functions it defines. Loading an external javascript library does not produce any direct output in the tiddler, so these definitions should have no impact on the appearance of your MainMenu.
-
-''Creating dynamic tiddler content''
-An important difference between this implementation of embedded scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document:
-* In a typical web document, you use the document.write() function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
-* However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and completely replaces the entire ~TiddlyWiki document in your browser window.
-* To allow these scripts to work unmodified, the plugin automatically converts all occurences of document.write() so that the output is inserted into the tiddler content instead of replacing the entire ~TiddlyWiki document.
-
-If your script does not use document.write() to create dynamically embedded content within a tiddler, your javascript can, as an alternative, explicitly return a text value that the plugin can then pass through the wikify() rendering engine to insert into the tiddler display. For example, using {{{return "thistext"}}} will produce the same output as {{{document.write("thistext")}}}.
-
-//Note: your script code is automatically 'wrapped' inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.//
-
-''Accessing the ~TiddlyWiki DOM''
-The plugin provides one pre-defined variable, 'place', that is passed in to your javascript code so that it can have direct access to the containing DOM element into which the tiddler output is currently being rendered.
-
-Access to this DOM element allows you to create scripts that can:
-* vary their actions based upon the specific location in which they are embedded
-* access 'tiddler-relative' information (use findContainingTiddler(place))
-* perform direct DOM manipulations (when returning wikified text is not enough)
-<<<
-!!!!!Examples
-<<<
-an "alert" message box:
-><script show>
- alert('InlineJavascriptPlugin: this is a demonstration message');
-</script>
-dynamic output:
-><script show>
- return (new Date()).toString();
-</script>
-wikified dynamic output:
-><script show>
- return "link to current user: [["+config.options.txtUserName+"]]";
-</script>
-dynamic output using 'place' to get size information for current tiddler:
-><script show>
- if (!window.story) window.story=window;
- var title=story.findContainingTiddler(place).id.substr(7);
- return title+" is using "+store.getTiddlerText(title).length+" bytes";
-</script>
-creating an 'onclick' button/link that runs a script:
-><script label="click here" show>
- if (!window.story) window.story=window;
- alert("Hello World!\nlinktext='"+place.firstChild.data+"'\ntiddler='"+story.findContainingTiddler(place).id.substr(7)+"'");
-</script>
-loading a script from a source url:
->http://www.TiddlyTools.com/demo.js contains:
->>{{{function demo() { alert('this output is from demo(), defined in demo.js') } }}}
->>{{{alert('InlineJavascriptPlugin: demo.js has been loaded'); }}}
-><script src="demo.js" show>
- return "loading demo.js..."
-</script>
-><script label="click to execute demo() function" show>
- demo()
-</script>
-<<<
-!!!!!Installation
-<<<
-import (or copy/paste) the following tiddlers into your document:
-''InlineJavascriptPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.06.01 [1.5.1]'' when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
-''2006.04.19 [1.5.0]'' added 'show' parameter to force display of javascript source code in tiddler output
-''2006.01.05 [1.4.0]'' added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
-''2005.12.13 [1.3.1]'' when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
-''2005.11.09 [1.3.0]'' for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
-''2005.11.08 [1.2.0]'' handle loading of javascript from an external URL via src="..." syntax
-''2005.11.08 [1.1.0]'' pass 'place' param into scripts to provide direct DOM access 
-''2005.11.08 [1.0.0]'' initial release
-<<<
-!!!!!Credits
-<<<
-This feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]]
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.inlineJavascript= {major: 1, minor: 5, revision: 1, date: new Date(2006,6,1)};
-
-config.formatters.push( {
- name: "inlineJavascript",
- match: "\\<script",
- lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
-
- handler: function(w) {
- var lookaheadRegExp = new RegExp(this.lookahead,"mg");
- lookaheadRegExp.lastIndex = w.matchStart;
- var lookaheadMatch = lookaheadRegExp.exec(w.source)
- if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
- if (lookaheadMatch[1]) { // load a script library
- // make script tag, set src, add to body to execute, then remove for cleanup
- var script = document.createElement("script"); script.src = lookaheadMatch[1];
- document.body.appendChild(script); document.body.removeChild(script);
- }
- if (lookaheadMatch[4]) { // there is script code
- if (lookaheadMatch[3]) // show inline script code in tiddler output
- wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
- if (lookaheadMatch[2]) { // create a link to an 'onclick' script
- // add a link, define click handler, save code in link (pass 'place'), set link attributes
- var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",lookaheadMatch[2]);
- link.onclick=function(){try{return(eval(this.code))}catch(e){alert(e.description?e.description:e.toString())}}
- link.code="function _out(place){"+lookaheadMatch[4]+"};_out(this);"
- link.setAttribute("href","javascript:;"); link.setAttribute("title",""); link.style.cursor="pointer";
- }
- else { // run inline script code
- var code="function _out(place){"+lookaheadMatch[4]+"};_out(w.output);"
- code=code.replace(/document.write\(/gi,'place.innerHTML+=(');
- try { var out = eval(code); } catch(e) { out = e.description?e.description:e.toString(); }
- if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
- }
- }
- w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
- }
- }
-} )
-//}}}
-
-
-
''[[Lumiera|index.html]]''
-RoadMap
-ToDo
-[[Tasks]]
-[[Admin]]
-<<fullscreen>>
-
-
-
<!--{{{-->
-<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
-<!--}}}-->
-
-<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>My TiddlyWiki</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>
-
-
-
Here we describe the things which are planned and going on.
-
-There is:
- * A RoadMap which gives a coarse overview of the project.
- * A ToDo list, showing whats left to be done on a intermediate to high level
- * A list of detailed [[Tasks]] which are imminent to do.
-
-The goal of this wiki is not to track each and every task, but show whats going on and where possible contributors can pick a job.
-
-
-
-
-
-
<!--{{{-->
-<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
-	<div class='headerShadow'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-	<div class='headerForeground'>
-		<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
-		<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
-	</div>
-</div>
-<!-- horizontal MainMenu -->
-<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
-<!-- original MainMenu menu -->
-<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
-<div id='sidebar'>
-	<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
-	<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
-</div>
-<div id='displayArea'>
-	<div id='messageArea'></div>
-	<div id='tiddlerDisplay'></div>
-</div>
-<!--}}}-->
-
-
-
-
/***
-|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
-|''Version:''|1.0.6 (2006-11-07)|
-|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
-|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
-|''Licence:''|[[BSD open source license]]|
-|''TiddlyWiki:''|2.0|
-|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
-!Table of Content<html><a name="TOC"/></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
-** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
-* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
-!Description<html><a name="Description"/></html>
-With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
-Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts, use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
-
-''Syntax:'' 
-|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
-|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//.|
-|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
-|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
-|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Applications<html><a name="Applications"/></html>
-!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
-Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
-
-Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Citation Index<html><a name="Citation"/></html>
-Create a tiddler "Citations" that contains your "citations". 
-Wrap every citation with a part and a proper name. 
-
-''Example''
-{{{
-<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
-in //Proc. ICSM//, 1998.</part>
-
-<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
-Thesis, Uni Stuttgart, 2002.</part>
-
-<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
-in //Proc. ICSM//, 1999.</part>
-}}}
-
-You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
-You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
-{{{
-* Item 1
-* Item 2
-* Item 3
-}}}
-into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
-
-Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
-
-''Example''
-{{{
-|!Subject|!Items|
-|subject1|<<tiddler ./Cell1>>|
-|subject2|<<tiddler ./Cell2>>|
-
-<part Cell1 hidden>
-* Item 1
-* Item 2
-* Item 3
-</part>
-...
-}}}
-
-Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
-
-BTW: The same approach can be used to create bullet lists with items that contain more than one line.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Creating Tabs<html><a name="Tabs"/></html>
-The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
-
-With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
-
-''Example''
-The standard tabs at the sidebar are defined by the following eight tiddlers:
-* SideBarTabs
-* TabAll
-* TabMore
-* TabMoreMissing
-* TabMoreOrphans
-* TabMoreShadowed
-* TabTags
-* TabTimeline
-
-Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
-{{{
-<<tabs txtMainTab 
- Timeline Timeline SideBarTabs/Timeline 
- All 'All tiddlers' SideBarTabs/All 
- Tags 'All tags' SideBarTabs/Tags 
- More 'More lists' SideBarTabs/More>>
-<part Timeline hidden><<timeline>></part>
-<part All hidden><<list all>></part>
-<part Tags hidden><<allTags>></part>
-<part More hidden><<tabs txtMoreTab 
- Missing 'Missing tiddlers' SideBarTabs/Missing 
- Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
- Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
-<part Missing hidden><<list missing>></part>
-<part Orphans hidden><<list orphans>></part>
-<part Shadowed hidden><<list shadowed>></part>
-}}}
-
-Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
-
-E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
-{{{
-<<forEachTiddler 
- sortBy 'tiddler.modified' descending 
- write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
-}}}
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!!Using Sliders<html><a name="Sliders"/></html>
-Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
-
-''Example''
-In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
-{{{
-...
-<<slider chkAboutDetails About/Details details "Click here to see more details">>
-<part Details hidden>
-To give you a better overview ...
-</part>
-...
-}}}
-
-Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
-
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Revision history<html><a name="Revisions"/></html>
-* v1.0.6 (2006-11-07)
-** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
-* v1.0.5 (2006-03-02)
-** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
-* v1.0.4 (2006-02-28)
-** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
-* v1.0.3 (2006-02-26)
-** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
-* v1.0.2 (2006-02-05)
-** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
-* v1.0.1 (2006-01-27)
-** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
-** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
-* v1.0.0 (2006-01-25)
-** initial version
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Code<html><a name="Code"/></html>
-<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-//{{{
-//============================================================================
-// PartTiddlerPlugin
-
-// Ensure that the PartTiddler Plugin is only installed once.
-//
-if (!version.extensions.PartTiddlerPlugin) {
-
-
-
-version.extensions.PartTiddlerPlugin = {
- major: 1, minor: 0, revision: 6,
- date: new Date(2006, 10, 7), 
- type: 'plugin',
- source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
-};
-
-if (!window.abego) window.abego = {};
-if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
-
-//============================================================================
-// Common Helpers
-
-// Looks for the next newline, starting at the index-th char of text. 
-//
-// If there are only whitespaces between index and the newline 
-// the index behind the newline is returned, 
-// otherwise (or when no newline is found) index is returned.
-//
-var skipEmptyEndOfLine = function(text, index) {
- var re = /(\n|[^\s])/g;
- re.lastIndex = index;
- var result = re.exec(text);
- return (result && text.charAt(result.index) == '\n') 
- ? result.index+1
- : index;
-}
-
-
-//============================================================================
-// Constants
-
-var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
-var partEndTagREString = "<\\/part>";
-var partEndTagString = "</part>";
-
-//============================================================================
-// Plugin Specific Helpers
-
-// Parse the parameters inside a <part ...> tag and return the result.
-//
-// @return [may be null] {partName: ..., isHidden: ...}
-//
-var parseStartTagParams = function(paramText) {
- var params = paramText.readMacroParams();
- if (params.length == 0 || params[0].length == 0) return null;
- 
- var name = params[0];
- var paramsIndex = 1;
- var hidden = false;
- if (paramsIndex < params.length) {
- hidden = params[paramsIndex] == "hidden";
- paramsIndex++;
- }
- 
- return {
- partName: name, 
- isHidden: hidden
- };
-}
-
-// Returns the match to the next (end or start) part tag in the text, 
-// starting the search at startIndex.
-// 
-// When no such tag is found null is returned, otherwise a "Match" is returned:
-// [0]: full match
-// [1]: matched "end" tag (or null when no end tag match)
-// [2]: matched "start" tag (or null when no start tag match)
-// [3]: content of start tag (or null if no start tag match)
-//
-var findNextPartEndOrStartTagMatch = function(text, startIndex) {
- var re = new RegExp(partEndOrStartTagRE);
- re.lastIndex = startIndex;
- var match = re.exec(text);
- return match;
-}
-
-//============================================================================
-// Formatter
-
-// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
-//
-// @return true if a complete part section (including the end tag) could be processed, false otherwise.
-//
-var handlePartSection = function(w) {
- var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
- if (!tagMatch) return false;
- if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
-
- // Parse the start tag parameters
- var arguments = parseStartTagParams(tagMatch[3]);
- if (!arguments) return false;
- 
- // Continue processing
- var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
- var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
- if (endMatch && endMatch[1]) {
- if (!arguments.isHidden) {
- w.nextMatch = startTagEndIndex;
- w.subWikify(w.output,partEndTagREString);
- }
- w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
- 
- return true;
- }
- return false;
-}
-
-config.formatters.push( {
- name: "part",
- match: "<part\\s+[^>]+>",
- 
- handler: function(w) {
- if (!handlePartSection(w)) {
- w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
- }
- }
-} )
-
-//============================================================================
-// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
-// as tiddlers.
-
-var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
-
-// Return the match to the first <part ...> tag of the text that has the
-// requrest partName.
-//
-// @return [may be null]
-//
-var findPartStartTagByName = function(text, partName) {
- var i = 0;
- 
- while (true) {
- var tagMatch = findNextPartEndOrStartTagMatch(text, i);
- if (!tagMatch) return null;
-
- if (tagMatch[2]) {
- // Is start tag
- 
- // Check the name
- var arguments = parseStartTagParams(tagMatch[3]);
- if (arguments && arguments.partName == partName) {
- return tagMatch;
- }
- }
- i += tagMatch[0].length;
- }
-}
-
-// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
-// object, using fullName as the Tiddler's title. 
-//
-// All remaining properties of the new Tiddler (tags etc.) are inherited from 
-// the parentTiddler.
-// 
-// @return [may be null]
-//
-var getPart = function(parentTiddler, partName, fullName) {
- var text = parentTiddler.text;
- var startTag = findPartStartTagByName(text, partName);
- if (!startTag) return null;
- 
- var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
- var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
-
- if (indexOfEndTag >= 0) {
- var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
- var partTiddler = new Tiddler();
- partTiddler.set(
- fullName,
- partTiddlerText,
- parentTiddler.modifier,
- parentTiddler.modified,
- parentTiddler.tags,
- parentTiddler.created);
- partTiddler.abegoIsPartTiddler = true;
- return partTiddler;
- }
- 
- return null;
-}
-
-// Hijack the store.fetchTiddler to recognize the "part" addresses.
-//
-
-var oldFetchTiddler = store.fetchTiddler ;
-store.fetchTiddler = function(title) {
- var result = oldFetchTiddler.apply(this, arguments);
- if (!result && title) {
- var i = title.lastIndexOf('/');
- if (i > 0) {
- var parentName = title.substring(0, i);
- var partName = title.substring(i+1);
- var parent = (parentName == ".") 
- ? currentParent 
- : oldFetchTiddler.apply(this, [parentName]);
- if (parent) {
- return getPart(parent, partName, parent.title+"/"+partName);
- }
- }
- }
- return result; 
-};
-
-
-// The user must not edit a readOnly/partTiddler
-//
-
-config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
-
-Tiddler.prototype.isReadOnly = function() {
- // Tiddler.isReadOnly was introduced with TW 2.0.6.
- // For older version we explicitly check the global readOnly flag
- if (config.commands.editTiddler.oldIsReadOnlyFunction) {
- if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
- } else {
- if (readOnly) return true;
- }
-
- return this.abegoIsPartTiddler;
-}
-
-config.commands.editTiddler.handler = function(event,src,title)
-{
- var t = store.getTiddler(title);
- // Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
- // or the tiddler is not readOnly
- if(!t || !t.abegoIsPartTiddler)
- {
- clearMessage();
- story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
- story.focusTiddler(title,"text");
- return false;
- }
-}
-
-// To allow the "./partName" syntax in macros we need to hijack 
-// the invokeMacro to define the "currentParent" while it is running.
-// 
-var oldInvokeMacro = window.invokeMacro;
-function myInvokeMacro(place,macro,params,wikifier,tiddler) {
- var oldCurrentParent = currentParent;
- if (tiddler) currentParent = tiddler;
- try {
- oldInvokeMacro.apply(this, arguments);
- } finally {
- currentParent = oldCurrentParent;
- }
-}
-window.invokeMacro = myInvokeMacro;
-
-// Scroll the anchor anchorName in the viewer of the given tiddler visible.
-// When no tiddler is defined use the tiddler of the target given event is used.
-window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
- var tiddlerElem = null;
- if (tiddler) {
- tiddlerElem = document.getElementById(story.idPrefix + tiddler);
- }
- if (!tiddlerElem && evt) {
- var target = resolveTarget(evt);
- tiddlerElem = story.findContainingTiddler(target);
- }
- if (!tiddlerElem) return;
-
- var children = tiddlerElem.getElementsByTagName("a");
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var name = child.getAttribute("name");
- if (name == anchorName) {
- var y = findPosY(child);
- window.scrollTo(0,y);
- return;
- }
- }
-}
-
-} // of "install only once"
-//}}}
-
-/***
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-
-!Licence and Copyright
-Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or other
-materials provided with the distribution.
-
-Neither the name of abego Software nor the names of its contributors may be
-used to endorse or promote products derived from this software without specific
-prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
-SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
-***/
-
-
-
/***
-|''Name:''|RSSReaderPlugin|
-|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
-|''Version:''|1.1.1|
-|''Date:''|Apr 21, 2007|
-|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
-|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
-|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
-|''Credit:''|BramChen for RssNewsMacro|
-|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
-|''~CoreVersion:''|2.2.0|
-|''OptionalRequires:''|http://www.tiddlytools.com/#NestedSlidersPlugin|
-***/
-//{{{
-version.extensions.RSSReaderPlugin = {
-	major: 1, minor: 1, revision: 1,
-	date: new Date("Apr 21, 2007"),
-	source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
-	author: "BidiX",
-	coreVersion: '2.2.0'
-};
-
-config.macros.rssReader = {
-	dateFormat: "DDD, DD MMM YYYY",
-	itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed  '@@'+itemStyle+itemText+'@@'
-	msg:{
-		permissionDenied: "Permission to read preferences was denied.",
-		noRSSFeed: "No RSS Feed at this address %0",
-		urlNotAccessible: " Access to %0 is not allowed"
-	},
-	cache: [], 	// url => XMLHttpRequest.responseXML
-	desc: "noDesc",
-	
-	handler: function(place,macroName,params,wikifier,paramString,tiddler) {
-		var desc = params[0];
-		var feedURL = params[1];
-		var toFilter = (params[2] ? true : false);
-		var filterString = (toFilter?(params[2].substr(0,1) == ' '? tiddler.title:params[2]):'');
-		var place = createTiddlyElement(place, "div", "RSSReader");
-		wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
-		if (this.cache[feedURL]) {
-			this.displayRssFeed(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
-		}
-		else {
-			var r = loadRemoteFile(feedURL,config.macros.rssReader.processResponse, [place, desc, toFilter, filterString]);
-			if (typeof r == "string")
-				displayMessage(r);
-		}
-		
-	},
-
-	// callback for loadRemoteFile 
-	// params : [place, desc, toFilter, filterString]
-	processResponse: function(status, params, responseText, url, xhr) { // feedURL, place, desc, toFilter, filterString) {	
-		if (window.netscape){
-			try {
-				if (document.location.protocol.indexOf("http") == -1) {
-					netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-				}
-			}
-			catch (e) { displayMessage(e.description?e.description:e.toString()); }
-		}
-		if (xhr.status == httpStatus.NotFound)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (!status)
-		 {
-			displayMessage(config.macros.rssReader.noRSSFeed.format([url]));
-			return;
-		}
-		if (xhr.responseXML) {
-			// response is interpreted as XML
-			config.macros.rssReader.cache[url] = xhr.responseXML;
-			config.macros.rssReader.displayRssFeed(xhr.responseXML, params[0], url, params[1], params[2], params[3]);
-		}
-		else {
-			if (responseText.substr(0,5) == "<?xml") {
-				// response exists but not return as XML -> try to parse it 
-				var dom = (new DOMParser()).parseFromString(responseText, "text/xml"); 
-				if (dom) {
-					// parsing successful so use it
-					config.macros.rssReader.cache[url] = dom;
-					config.macros.rssReader.displayRssFeed(dom, params[0], url, params[1], params[2], params[3]);
-					return;
-				}
-			}
-			// no XML display as html 
-			wikify("<html>" + responseText + "</html>", params[0]);
-			displayMessage(config.macros.rssReader.msg.noRSSFeed.format([url]));
-		}
-	},
-
-	// explore down the DOM tree
-	displayRssFeed: function(xml, place, feedURL, desc, toFilter, filterString){
-		// Channel
-		var chanelNode = xml.getElementsByTagName('channel').item(0);
-		var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
-		var chanelTitle = "";
-		if ((chanelTitleElement) && (chanelTitleElement.firstChild)) 
-			chanelTitle = chanelTitleElement.firstChild.nodeValue;
-		var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
-		var chanelLink = "";
-		if (chanelLinkElement) 
-			chanelLink = chanelLinkElement.firstChild.nodeValue;
-		var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
-		var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
-		wikify(titleTxt,title);
-		// ItemList
-		var itemList = xml.getElementsByTagName('item');
-		var article = createTiddlyElement(place,"ul",null,null,null);
-		var lastDate;
-		var re;
-		if (toFilter) 
-			re = new RegExp(filterString.escapeRegExp());
-		for (var i=0; i<itemList.length; i++){
-			var titleElm = itemList[i].getElementsByTagName('title').item(0);
-			var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
-			if (toFilter && ! titleText.match(re)) {
-				continue;
-			}
-			var descText = '';
-			descElem = itemList[i].getElementsByTagName('description').item(0);
-			if (descElem){
-				try{
-					for (var ii=0; ii<descElem.childNodes.length; ii++) {
-						descText += descElem.childNodes[ii].nodeValue;
-					}
-				}
-				catch(e){}
-				descText = descText.replace(/<br \/>/g,'\n');
-				if (desc == "asHtml")
-					descText = "<html>"+descText+"</html>";
-			}
-			var linkElm = itemList[i].getElementsByTagName("link").item(0);
-			var linkURL = linkElm.firstChild.nodeValue;
-			var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
-			var pubDate;
-			if (!pubElm) {
-				pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
-				if (pubElm) {
-					pubDate = pubElm.firstChild.nodeValue;
-					pubDate = this.formatDateString(this.dateFormat, pubDate);
-					}
-					else {
-						pubDate = '0';
-					}
-				}
-			else {
-				pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
-				pubDate = this.formatDate(this.dateFormat, pubDate);
-			}
-			titleText = titleText.replace(/\[|\]/g,'');
-			var rssText = '*'+'[[' + titleText + '|' + linkURL + ']]' + '' ;
-			if ((desc != "noDesc") && descText){
-				rssText = rssText.replace(/\n/g,' ');
-				descText = '@@'+this.itemStyle+descText + '@@\n';				
-				if (version.extensions.nestedSliders){
-					descText = '+++[...]' + descText + '===';
-				}
-				rssText = rssText + descText;
-			}
-			var story;
-			if ((lastDate != pubDate) && ( pubDate != '0')) {
-				story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
-				lastDate = pubDate;
-			}
-			else {
-				lastDate = pubDate;
-			}
-			story = createTiddlyElement(article,"div",null,"RSSItem",null);
-			wikify(rssText,story);
-		}
-	},
-	
-	formatDate: function(template, date){
-		var dateString = new Date(date);
-		// template = template.replace(/hh|mm|ss/g,'');
-		return dateString.formatString(template);
-	},
-	
-	formatDateString: function(template, date){
-		var dateString = new Date(date.substr(0,4), date.substr(5,2) - 1, date.substr(8,2)
-			);
-		return dateString.formatString(template);
-	}
-	
-};
-
-config.macros.rssFeedUpdate = {
-	label: "Update",
-	prompt: "Clear the cache and redisplay this RssFeed",
-	handler: function(place,macroName,params) {
-		var feedURL = params[0];
-		var tiddlerTitle = params[1];
-		createTiddlyButton(place, this.label, this.prompt, 
-			function () {
-				if (config.macros.rssReader.cache[feedURL]) {
-					config.macros.rssReader.cache[feedURL] = null; 
-			}
-			story.refreshTiddler(tiddlerTitle,null, true);
-		return false;});
-	}
-};
-
-//}}}
-
-
-
-
//last update: RSSReaderPlugin v 1.1.1//
-
-!Description
-This plugin provides a RSSReader for TiddlyWiki
-* It accesses asynchronously an RSSFeed
-*Depending on the chanel item format, each item could be written as :
-**simple text wikified
-**html
-
-!Usage
-{{{
-<<rssReader noDesc|asHtml|asText rssUrl ['filtering string']>>
-	noDesc: only title of item is printed
-
-	asHtml: if you know that description contain html (links, img ...), 
-		the text is enclosed with <html> </html> tags
-
- 	asText: if the description should not be interpreted as html the 
-		description is wikified
-
-	rssUrl: the rssFeed url that could be accessed. 
-	
-	'filtering string': if present, the rssfeed item title must contained 
-		this string to be displayed. 
-		If 'filering string' contained space characters only, the tiddler 
-		title is used for filtering.
-
-}}}
-
-For security reasons, if the TiddlyWiki is accessed from http, a ProxyService should be used to access an rssFeed from an other site.
-
-!examples
-| !reader | !RSSFeed type | !working from |
-| BidiXTWRSS | Description asHtml | file: or tiddlywiki.bidix.info |
-| [[Le Monde]] | Description asText | file: or tiddlywiki.bidix.info using proxy |
-| YahooNewsSport | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| TiddlyWikiRSS | Description asHtml | file: or tiddlywiki.bidix.info using proxy |
-| [[Libération]] | noDesc | file: or tiddlywiki.bidix.info using proxy |
-| [[TestComment]] | asText and filters | file: or tiddlywiki.bidix.info using proxy |
-see : <<tag RSSFeed>> for the full list.
-
-!Revision history
-* V1.1.0 (2207/04/13)
-**No more import functions
-* V1.0.0 (2006/11/11)
-**refactoring using core loadRemoteFile function
-**import using new tiddlywiki:tiddler element
-**import and presentation preserved without EricShulman's NestedSliderPlugin
-**better display of items 
-* v0.3.0 (24/08/2006)
-** Filter on RSS item title
-** Place to display redefined for asynchronous processing
-* v0.2.2 (22/08/2006)
-**Haloscan feed has no pubDate.
-* v0.2.1 (08/05/2006)
-* v0.2.0 (01/05/2006)
-**Small adapations for del.icio.us feed
-* v0.1.1 (28/04/2006)
-**Bug : Channel without title 
-* v0.1.0 (24/04/2006)
-** initial release
-
-
-
-
-
-
! Foundations, not really a milestone yet
-
-We need following in a basically working / mockup state:
-
-* GUI
-** define a outline (most important views and modes)
-** define the foundations for communicating with the lower layers
-* Proc layer
-** basic assets, create a clip object //(done)//
-** interface/stub for ~ConfigQuery subsystem //(done)//
-** add simple clip to EDL/Session, invoke builder
-** build render nodes for very simple session (partly mockups)
-** preliminary support for composite clips (video+audio)
-* Backend
-** File backend, caching
-** Serializer
-** Scheduler
-* Support Libary
-** Plugin Loader
-** Other tools there
-* Infrastructure
-** Testsuite
-** Website
-
-!!! Next Goals
-
-# Make a [[video player mockup]]
-# Integrate a ''Version 0.1'' able to
-#* load a simple media
-#* put a clip to the EDL
-#* build the corresponding low-level model
-#* run a render process
-
-! Second round
-//all rather brainstorming for now, feel free to modify...//
-
-* save and load session
-* integrate YAP Prolog instead of the mock impl
-* load and wire some plugins
-* send output to a window in GUI
-
-
-
-
-
Roadmap and tasks
-
-
-
ToDo
-
-
-
/***
-
-''Inspired by [[TiddlyPom|http://www.warwick.ac.uk/~tuspam/tiddlypom.html]]''
-
-|Name|SplashScreenPlugin|
-|Created by|SaqImtiaz|
-|Location|http://tw.lewcid.org/#SplashScreenPlugin|
-|Version|0.21 |
-|Requires|~TW2.08+|
-!Description:
-Provides a simple splash screen that is visible while the TW is loading.
-
-!Installation
-Copy the source text of this tiddler to your TW in a new tiddler, tag it with systemConfig and save and reload. The SplashScreen will now be installed and will be visible the next time you reload your TW.
-
-!Customizing
-Once the SplashScreen has been installed and you have reloaded your TW, the splash screen html will be present in the MarkupPreHead tiddler. You can edit it and customize to your needs.
-
-!History
-* 20-07-06 : version 0.21, modified to hide contentWrapper while SplashScreen is displayed.
-* 26-06-06 : version 0.2, first release
-
-!Code
-***/
-//{{{
-var old_lewcid_splash_restart=restart;
-
-restart = function()
-{   if (document.getElementById("SplashScreen"))
-        document.getElementById("SplashScreen").style.display = "none";
-      if (document.getElementById("contentWrapper"))
-        document.getElementById("contentWrapper").style.display = "block";
-    
-    old_lewcid_splash_restart();
-   
-    if (splashScreenInstall)
-       {if(config.options.chkAutoSave)
-			{saveChanges();}
-        displayMessage("TW SplashScreen has been installed, please save and refresh your TW.");
-        }
-}
-
-
-var oldText = store.getTiddlerText("MarkupPreHead");
-if (oldText.indexOf("SplashScreen")==-1)
-   {var siteTitle = store.getTiddlerText("SiteTitle");
-   var splasher='\n\n<style type="text/css">#contentWrapper {display:none;}</style><div id="SplashScreen" style="border: 3px solid #ccc; display: block; text-align: center; width: 320px; margin: 100px auto; padding: 50px; color:#000; font-size: 28px; font-family:Tahoma; background-color:#eee;"><b>'+siteTitle +'</b> is loading<blink> ...</blink><br><br><span style="font-size: 14px; color:red;">Requires Javascript.</span></div>';
-   if (! store.tiddlerExists("MarkupPreHead"))
-       {var myTiddler = store.createTiddler("MarkupPreHead");}
-   else
-      {var myTiddler = store.getTiddler("MarkupPreHead");}
-      myTiddler.set(myTiddler.title,oldText+splasher,config.options.txtUserName,null,null);
-      store.setDirty(true);
-      var splashScreenInstall = true;
-}
-//}}}
-
-
-
/*{{{*/
-/* a contrasting background so I can see where one tiddler ends and the other begins */
-body {
-	background: [[ColorPalette::TertiaryLight]];
-}
-
-/* sexy colours and font for the header */
-.headerForeground {
-	color: [[ColorPalette::PrimaryPale]];
-}
-.headerShadow, .headerShadow a {
-	color: [[ColorPalette::PrimaryMid]];
-}
-.headerForeground, .headerShadow {
-	padding: 1em 1em 0;
-	font-family: 'Trebuchet MS' sans-serif;
-	font-weight:bold;
-}
-.headerForeground .siteSubtitle {
-	color: [[ColorPalette::PrimaryLight]];
-}
-.headerShadow .siteSubtitle {
-	color: [[ColorPalette::PrimaryMid]];
-}
-
-/* make shadow go and down right instead of up and left */
-.headerShadow {
-	left: 2px;
-	top: 3px;
-}
-
-/* prefer monospace for editing */
-.editor textarea {
-	font-family: 'Consolas' monospace;
-}
-
-/* sexy tiddler titles */
-.title {
-	font-size: 250%;
-	color: [[ColorPalette::PrimaryLight]];
-	font-family: 'Trebuchet MS' sans-serif;
-}
-
-/* more subtle tiddler subtitle */
-.subtitle {
-	padding:0px;
-	margin:0px;
-	padding-left:0.5em;
-	font-size: 90%;
-	color: [[ColorPalette::TertiaryMid]];
-}
-.subtitle .tiddlyLink {
-	color: [[ColorPalette::TertiaryMid]];
-}
-
-/* a little bit of extra whitespace */
-.viewer {
-	padding-bottom:3px;
-}
-
-/* don't want any background color for headings */
-h1,h2,h3,h4,h5,h6 {
-	background: [[ColorPalette::Background]];
-	color: [[ColorPalette::Foreground]];
-}
-
-/* give tiddlers 3d style border and explicit background */
-.tiddler {
-	background: [[ColorPalette::Background]];
-	border-right: 2px [[ColorPalette::TertiaryMid]] solid;
-	border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
-	margin-bottom: 1em;
-	padding-bottom: 2em;
-}
-
-/* make options slider look nicer */
-#sidebarOptions .sliderPanel {
-	border:solid 1px [[ColorPalette::PrimaryLight]];
-}
-
-
-/* the borders look wrong with the body background */
-#sidebar .button {
-	border-style: none;
-}
-
-/* displays the list of a tiddler's tags horizontally. used in ViewTemplate */
-.tagglyTagged li.listTitle {
-	display:none
-}
-.tagglyTagged li {
-	display: inline; font-size:90%;
-}
-.tagglyTagged ul {
-	margin:0px; padding:0px;
-}
-
-/* this means you can put line breaks in SidebarOptions for readability */
-#sidebarOptions br {
-	display:none;
-}
-/* undo the above in OptionsPanel */
-#sidebarOptions .sliderPanel br {
-	display:inline;
-}
-
-/* horizontal main menu stuff */
-#displayArea {
-	margin: 1em 15.7em 0em 1em; /* use the freed up space */
-}
-#topMenu br {
-	display: none;
-}
-#topMenu {
-	background: [[ColorPalette::PrimaryMid]];
-	color:[[ColorPalette::PrimaryPale]];
-}
-#topMenu {
-	padding:2px;
-}
-#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
-	margin-left: 0.5em;
-	margin-right: 0.5em;
-	padding-left: 3px;
-	padding-right: 3px;
-	color: [[ColorPalette::PrimaryPale]];
-	font-size: 115%;
-}
-#topMenu .button:hover, #topMenu .tiddlyLink:hover {
-	background: [[ColorPalette::PrimaryDark]];
-}
-
-/* make it print a little cleaner */
-@media print {
-	#topMenu {
-		display: none ! important;
-	}
-	/* not sure if we need all the importants */
-	.tiddler {
-		border-style: none ! important;
-		margin:0px ! important;
-		padding:0px ! important;
-		padding-bottom:2em ! important;
-	}
-	.tagglyTagging .button, .tagglyTagging .hidebutton {
-		display: none ! important;
-	}
-	.headerShadow {
-		visibility: hidden ! important;
-	}
-	.tagglyTagged .quickopentag, .tagged .quickopentag {
-		border-style: none ! important;
-	}
-	.quickopentag a.button, .miniTag {
-		display: none ! important;
-	}
-}
-/*}}}*/
-
-
-
-
<<timeline better:true maxDays:14 maxEntries:20>>
-
-
-
/***
-|Name|TaskMacroPlugin|
-|Author|<<extension TaskMacroPlugin author>>|
-|Location|<<extension TaskMacroPlugin source>>|
-|License|<<extension TaskMacroPlugin license>>|
-|Version|<<extension TaskMacroPlugin versionAndDate>>|
-!Description
-A set of macros to help you keep track of time estimates for tasks.
-
-Macros defined:
-* {{{task}}}: Displays a task description and makes it easy to estimate and track the time spent on the task.
-* {{{taskadder}}}: Displays text entry field to simplify the adding of tasks.
-* {{{tasksum}}}: Displays a summary of tasks sandwiched between two calls to this macro.
-* {{{extension}}}: A simple little macro that displays information about a TiddlyWiki plugin, and that will hopefully someday migrate to the TW core in some form.
-Core overrides:
-* {{{wikify}}}: when wikifying a tiddler's complete text, adds refresh information so the tiddler will be refreshed when it changes
-* {{{config.refreshers}}}: have the built-in refreshers return true; also, add a new refresher ("fullContent") that redisplays a full tiddler whenever it or any nested tiddlers it shows are changed
-* {{{refreshElements}}}: now checks the return value from the refresher and only short-circuits the recursion if the refresher returns true
-!Plugin Information
-***/
-//{{{
-version.extensions.TaskMacroPlugin = {
-	major: 1, minor: 1, revision: 0,
-	date: new Date(2006,5-1,13),
-	author: "LukeBlanshard",
-	source: "http://labwiki.sourceforge.net/#TaskMacroPlugin",
-	license: "http://labwiki.sourceforge.net/#CopyrightAndLicense"
-}
-//}}}
-/***
-A little macro for pulling out extension info.  Use like {{{<<extension PluginName datum>>}}}, where {{{PluginName}}} is the name you used for {{{version.extensions}}} and {{{datum}}} is either {{{versionAndDate}}} or a property of the extension description object, such as {{{source}}}.
-***/
-//{{{
-config.macros.extension = {
-	handler: function( place, macroName, params, wikifier, paramString, tiddler ) {
-		var info  = version.extensions[params[0]]
-		var datum = params[1]
-		switch (params[1]) {
-		case 'versionAndDate':
-			createTiddlyElement( place, "span", null, null,
-				info.major+'.'+info.minor+'.'+info.revision+', '+info.date.formatString('DD MMM YYYY') )
-			break;
-		default:
-			wikify( info[datum], place )
-			break;
-		}
-	}
-}
-//}}}
-/***
-!Core Overrides
-***/
-//{{{
-window.wikify_orig_TaskMacroPlugin = window.wikify
-window.wikify = function(source,output,highlightRegExp,tiddler)
-{
-	if ( tiddler && tiddler.text === source )
-		addDisplayDependency( output, tiddler.title )
-	wikify_orig_TaskMacroPlugin.apply( this, arguments )
-}
-config.refreshers_orig_TaskMacroPlugin = config.refreshers
-config.refreshers = {
-	link: function() {
-		config.refreshers_orig_TaskMacroPlugin.link.apply( this, arguments )
-		return true
-	},
-	content: function() {
-		config.refreshers_orig_TaskMacroPlugin.content.apply( this, arguments )
-		return true
-	},
-	fullContent: function( e, changeList ) {
-		var tiddlers = e.refreshTiddlers
-		if ( changeList == null || tiddlers == null )
-			return false
-		for ( var i=0; i < tiddlers.length; ++i )
-			if ( changeList.find(tiddlers[i]) != null ) {
-				var title = tiddlers[0]
-				story.refreshTiddler( title, null, true )
-				return true
-			}
-		return false
-	}
-}
-function refreshElements(root,changeList)
-{
-	var nodes = root.childNodes;
-	for(var c=0; c<nodes.length; c++)
-		{
-		var e = nodes[c],type;
-		if(e.getAttribute)
-			type = e.getAttribute("refresh");
-		else
-			type = null;
-		var refresher = config.refreshers[type];
-		if ( ! refresher || ! refresher(e, changeList) )
-			{
-			if(e.hasChildNodes())
-				refreshElements(e,changeList);
-			}
-		}
-}
-//}}}
-/***
-!Global Functions
-***/
-//{{{
-// Add the tiddler whose title is given to the list of tiddlers whose
-// changing will cause a refresh of the tiddler containing the given element.
-function addDisplayDependency( element, title ) {
-	while ( element && element.getAttribute ) {
-		var idAttr = element.getAttribute("id"), tiddlerAttr = element.getAttribute("tiddler")
-		if ( idAttr && tiddlerAttr && idAttr == story.idPrefix+tiddlerAttr ) {
-			var list = element.refreshTiddlers
-			if ( list == null ) {
-				list = [tiddlerAttr]
-				element.refreshTiddlers = list
-				element.setAttribute( "refresh", "fullContent" )
-			}
-			list.pushUnique( title )
-			return
-		}
-		element = element.parentNode
-	}
-}
-
-// Lifted from Story.prototype.focusTiddler: just return the field instead of focusing it.
-Story.prototype.findEditField = function( title, field )
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		return e
-		}
-}
-
-// Wraps the given event function in another function that handles the
-// event in a standard way.
-function wrapEventHandler( otherHandler ) {
-	return function(e) {
-		if (!e) var e = window.event
-		e.cancelBubble = true
-		if (e.stopPropagation) e.stopPropagation()
-		return otherHandler( e )
-	}
-}
-//}}}
-/***
-!Task Macro
-Usage:
-> {{{<<task orig cur spent>>description}}}
-All of orig, cur, and spent are optional numbers of hours.  The description goes through the end of the line, and is wikified.
-***/
-//{{{
-config.macros.task = {
-	NASCENT:	0, // Task not yet estimated
-	LIVE:		1, // Estimated but with time remaining
-	DONE:		2, // Completed: no time remaining
-	bullets:	["\u25cb", // nascent (open circle)
-			 "\u25ba", // live (right arrow)
-			 "\u25a0"],// done (black square)
-	styles:		["nascent", "live", "done"],
-
-	// Translatable text:
-	lingo: {
-		spentTooBig:	"Spent time %0 can't exceed current estimate %1",
-		noNegative:	"Times may not be negative numbers",
-		statusTips:	["Not yet estimated", "To do", "Done"], // Array indexed by state (NASCENT/LIVE/DONE)
-		descClickTip:	" -- Double-click to edit task description",
-		statusClickTip:	" -- Double-click to mark task complete",
-		statusDoneTip:	" -- Double-click to adjust the time spent, to revive the task",
-		origTip:	"Original estimate in hours",
-		curTip:		"Current estimate in hours",
-		curTip2:	"Estimate in hours", // For when orig == cur
-		clickTip:	" -- Click to adjust",
-		spentTip:	"Hours spent on this task",
-		remTip:		"Hours remaining",
-		curPrompt:	"Estimate this task in hours, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		spentPrompt:	"Enter the number of hours you've spent on this task, or adjust the current number by starting with + or -.\n\nYou may optionally also set or adjust the time remaining by putting a second number after the first.",
-		remPrompt:	"Enter the number of hours it will take to finish this task, or adjust the current estimate by starting with + or -.\n\nYou may optionally also set or adjust the time spent by putting a second number after the first.",
-		numbersOnly:	"Enter numbers only, please",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before doing this."
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var start = wikifier.matchStart, end = wikifier.nextMatch
-
-		var origStr	= params.length > 0? params.shift() : "?"
-		var orig	= +origStr // as a number
-		var cur		= params.length > 1? +params.shift() : orig
-		var spent	= params.length > 0? +params.shift() : 0
-		if ( spent > cur )
-			throw Error( this.lingo.spentTooBig.format([spent, cur]) )
-		if ( orig < 0 || cur < 0 || spent < 0 )
-			throw Error( this.lingo.noNegative )
-		var rem		= cur - spent
-		var state	= isNaN(orig+rem)? this.NASCENT : rem > 0? this.LIVE : this.DONE
-		var table	= createTiddlyElement( place, "table", null, "task "+this.styles[state] )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status", this.bullets[state] )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-		var origCell	= state==this.NASCENT || orig==cur? null
-				: createTiddlyElement( row, "td", null, "numeric original" )
-		var curCell	= createTiddlyElement( row, "td", null, "numeric current" )
-		var spentCell	= createTiddlyElement( row, "td", null, "numeric spent" )
-		var remCell	= createTiddlyElement( row, "td", null, "numeric remaining" )
-
-		var sums = config.macros.tasksum.tasksums
-		if ( sums && sums.length ) {
-			var summary = [(state == this.NASCENT? NaN : orig), cur, spent]
-			summary.owner = tiddler
-			sums[0].push( summary )
-		}
-
-		// The description goes to the end of the line
-		wikifier.subWikify( descCell, "$\\n?" )
-		var descEnd = wikifier.nextMatch
-
-		statusCell.setAttribute( "title", this.lingo.statusTips[state] )
-		descCell.setAttribute(   "title", this.lingo.statusTips[state]+this.lingo.descClickTip )
-		if (origCell) {
-			createTiddlyElement( origCell, "div", null, null, orig )
-			origCell.setAttribute( "title", this.lingo.origTip )
-			curCell.setAttribute( "title", this.lingo.curTip )
-		}
-		else {
-			curCell.setAttribute( "title", this.lingo.curTip2 )
-		}
-		var curDivContents = (state==this.NASCENT)? "?" : cur
-		var curDiv = createTiddlyElement( curCell, "div", null, null, curDivContents )
-		spentCell.setAttribute( "title", this.lingo.spentTip )
-		var spentDiv = createTiddlyElement( spentCell, "div", null, null, spent )
-		remCell.setAttribute( "title", this.lingo.remTip )
-		var remDiv = createTiddlyElement( remCell, "div", null, null, rem )
-
-		// Handle double-click on the description by going
-		// into edit mode and selecting the description
-		descCell.ondblclick = this.editDescription( tiddler, end, descEnd )
-
-		function appTitle( el, suffix ) {
-			el.setAttribute( "title", el.getAttribute("title")+suffix )
-		}
-
-		// For incomplete tasks, handle double-click on the bullet by marking the task complete
-		if ( state != this.DONE ) {
-			appTitle( statusCell, this.lingo.statusClickTip )
-			statusCell.ondblclick = this.markTaskComplete( tiddler, start, end, macroName, orig, cur, state )
-		}
-		// For complete ones, handle double-click on the bullet by letting you adjust the time spent
-		else {
-			appTitle( statusCell, this.lingo.statusDoneTip )
-			statusCell.ondblclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		}
-
-		// Add click handlers for the numeric cells.
-		if ( state != this.DONE ) {
-			appTitle( curCell, this.lingo.clickTip )
-			curDiv.className = "adjustable"
-			curDiv.onclick = this.adjustCurrentEstimate( tiddler, start, end, macroName,
-				orig, cur, spent, curDivContents )
-		}
-		appTitle( spentCell, this.lingo.clickTip )
-		spentDiv.className = "adjustable"
-		spentDiv.onclick = this.adjustTimeSpent( tiddler, start, end, macroName, orig, cur, spent )
-		if ( state == this.LIVE ) {
-			appTitle( remCell, this.lingo.clickTip )
-			remDiv.className = "adjustable"
-			remDiv.onclick = this.adjustTimeRemaining( tiddler, start, end, macroName, orig, cur, spent )
-		}
-	},
-
-	// Puts the tiddler into edit mode, and selects the range of characters
-	// defined by start and end.  Separated for leak prevention in IE.
-	editDescription: function( tiddler, start, end ) {
-		return wrapEventHandler( function(e) {
-			story.displayTiddler( null, tiddler.title, DEFAULT_EDIT_TEMPLATE )
-			var tiddlerElement = document.getElementById( story.idPrefix + tiddler.title )
-			window.scrollTo( 0, ensureVisible(tiddlerElement) )
-			var element = story.findEditField( tiddler.title, "text" )
-			if ( element && element.tagName.toLowerCase() == "textarea" ) {
-				// Back up one char if the last char's a newline
-				if ( tiddler.text[end-1] == '\n' )
-					--end
-				element.focus()
-				if ( element.setSelectionRange != undefined ) { // Mozilla
-					element.setSelectionRange( start, end )
-					// Damn mozilla doesn't scroll to visible.  Approximate.
-					var max = 0.0 + element.scrollHeight
-					var len = element.textLength
-					var top = max*start/len, bot = max*end/len
-					element.scrollTop = Math.min( top, (bot+top-element.clientHeight)/2 )
-				}
-				else if ( element.createTextRange != undefined ) { // IE
-					var range = element.createTextRange()
-					range.collapse()
-					range.moveEnd("character", end)
-					range.moveStart("character", start)
-					range.select()
-				}
-				else // Other? Too bad, just select the whole thing.
-					element.select()
-				return false
-			}
-			else
-				return true
-		} )
-	},
-
-	// Modifies a task macro call such that the task appears complete.
-	markTaskComplete: function( tiddler, start, end, macroName, orig, cur, state ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			if ( state == macro.NASCENT )
-				orig = cur = 0
-			// The second "cur" in the call below bumps up the time spent
-			// to match the current estimate.
-			macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, cur )
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the current estimate, modifies the macro call accordingly.
-	adjustCurrentEstimate: function( tiddler, start, end, macroName, orig, cur, spent, curDivContents ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.curPrompt, curDivContents )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				cur = macro.offset( cur, a[0] )
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time spent, modifies the macro call accordingly.
-	adjustTimeSpent: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this, text = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.spentPrompt, spent )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				spent = macro.offset( spent, a[0] )
-				var rem = cur - spent
-				if ( a.length > 1 ) {
-					rem = macro.offset( rem, a[1] )
-					cur = spent + rem
-				}
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Asks the user for an adjustment to the time remaining, modifies the macro call accordingly.
-	adjustTimeRemaining: function( tiddler, start, end, macroName, orig, cur, spent ) {
-		var macro = this
-		var text  = tiddler.text
-		var rem   = cur - spent
-		return wrapEventHandler( function(e) {
-			if ( text !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var txt = prompt( macro.lingo.remPrompt, rem )
-			if ( txt != null ) {
-				var a = macro.breakInput( txt )
-				var newRem = macro.offset( rem, a[0] )
-				if ( newRem > rem || a.length > 1 )
-					cur += (newRem - rem)
-				else
-					spent += (rem - newRem)
-				if ( a.length > 1 )
-					spent = macro.offset( spent, a[1] )
-				macro.replaceMacroCall( tiddler, start, end, macroName, orig, cur, spent )
-			}
-			return false
-		} )
-	},
-
-	// Breaks input at spaces & commas, returns array
-	breakInput: function( txt ) {
-		var a = txt.trim().split( /[\s,]+/ )
-		if ( a.length == 0 )
-			a = [NaN]
-		return a
-	},
-
-	// Adds to, subtracts from, or replaces a numeric value
-	offset: function( num, txt ) {
-		if ( txt == "" || typeof(txt) != "string" )
-			return NaN
-		if ( txt.match(/^[+-]/) )
-			return num + (+txt)
-		return +txt
-	},
-
-	// Does some error checking, then replaces the indicated macro
-	// call within the text of the given tiddler.
-	replaceMacroCall: function( tiddler, start, end, macroName, orig, cur, spent )
-	{
-		if ( isNaN(cur+spent) ) {
-			alert( this.lingo.numbersOnly )
-			return
-		}
-		if ( spent < 0 || cur < 0 ) {
-			alert( this.lingo.noNegative )
-			return
-		}
-		if ( isNaN(orig) )
-			orig = cur
-		if ( spent > cur )
-			cur = spent
-		var text = tiddler.text.substring(0,start) + "<<" + macroName + " " +
-			orig + " " + cur + " " + spent + ">>" + tiddler.text.substring(end)
-		var title = tiddler.title
-		store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-		//story.refreshTiddler( title, null, true )
-		if ( config.options.chkAutoSave )
-			saveChanges()
-	}
-}
-//}}}
-/***
-!Tasksum Macro
-Usage:
-> {{{<<tasksum "start" ["here" [intro]]>>}}}
-or:
-> {{{<<tasksum "end" [intro]>>}}}
-Put one of the {{{<<tasksum start>>}}} lines before the tasks you want to summarize, and an {{{end}}} line after them.  By default, the summary goes at the end; if you include {{{here}}} in the start line, the summary will go at the top.  The intro argument, if supplied, replaces the default text introducing the summary.
-***/
-//{{{
-config.macros.tasksum = {
-
-	// Translatable text:
-	lingo: {
-		unrecVerb:	"<<%0>> requires 'start' or 'end' as its first argument",
-		mustMatch:	"<<%0 end>> must match a preceding <<%0 start>>",
-		defIntro:	"Task summary:",
-		nascentSum:	"''%0 not estimated''",
-		doneSum:	"%0 complete (in %1 hours)",
-		liveSum:	"%0 ongoing (%1 hours so far, ''%2 hours remaining'')",
-		overSum:	"Total overestimate: %0%.",
-		underSum:	"Total underestimate: %0%.",
-		descPattern:	"%0 %1. %2",
-                origTip:	"Total original estimates in hours",
-		curTip:		"Total current estimates in hours",
-		spentTip:	"Total hours spent on tasks",
-		remTip:		"Total hours remaining"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var sums = this.tasksums
-		if ( params[0] == "start" ) {
-			sums.unshift([])
-			if ( params[1] == "here" ) {
-				sums[0].intro = params[2] || this.lingo.defIntro
-				sums[0].place = place
-				sums[0].placement = place.childNodes.length
-			}
-		}
-		else if ( params[0] == "end" ) {
-			if ( ! sums.length )
-				throw Error( this.lingo.mustMatch.format([macroName]) )
-			var list = sums.shift()
-			var intro = list.intro || params[1] || this.lingo.defIntro
-			var nNascent=0, nLive=0, nDone=0, nMine=0
-			var totLiveSpent=0, totDoneSpent=0
-			var totOrig=0, totCur=0, totSpent=0
-			for ( var i=0; i < list.length; ++i ) {
-				var a = list[i]
-				if ( a.length > 3 ) {
-					nNascent 	+= a[0]
-					nLive 		+= a[1]
-					nDone 		+= a[2]
-					totLiveSpent 	+= a[3]
-					totDoneSpent 	+= a[4]
-					totOrig 	+= a[5]
-					totCur 		+= a[6]
-					totSpent 	+= a[7]
-					if ( a.owner == tiddler )
-						nMine	+= a[8]
-				}
-				else {
-					if ( a.owner == tiddler )
-						++nMine
-					if ( isNaN(a[0]) ) {
-						++nNascent
-					}
-					else {
-						if ( a[1] > a[2] ) {
-							++nLive
-							totLiveSpent += a[2]
-						}
-						else {
-							++nDone
-							totDoneSpent += a[2]
-						}
-						totOrig  += a[0]
-						totCur   += a[1]
-						totSpent += a[2]
-					}
-				}
-			}
-
-			// If we're nested, push a summary outward
-                        if ( sums.length ) {
-				var summary = [nNascent, nLive, nDone, totLiveSpent, totDoneSpent,
-						totOrig, totCur, totSpent, nMine]
-				summary.owner = tiddler
-				sums[0].push( summary )
-			}
-
-			var descs = [], styles = []
-			if ( nNascent > 0 ) {
-				descs.push( this.lingo.nascentSum.format([nNascent]) )
-				styles.push( "nascent" )
-			}
-			if ( nDone > 0 )
-				descs.push( this.lingo.doneSum.format([nDone, totDoneSpent]) )
-			if ( nLive > 0 ) {
-				descs.push( this.lingo.liveSum.format([nLive, totLiveSpent, totCur-totSpent]) )
-				styles.push( "live" )
-			}
-			else
-				styles.push( "done" )
-			var off = ""
-			if ( totOrig > totCur )
-				off = this.lingo.overSum.format( [Math.round(100.0*(totOrig-totCur)/totCur)] )
-			else if ( totCur > totOrig )
-				off = this.lingo.underSum.format( [Math.round(100.0*(totCur-totOrig)/totOrig)] )
-
-			var top		= (list.intro != undefined)
-			var table	= createTiddlyElement( null, "table", null, "tasksum "+(top?"top":"bottom") )
-			var tbody	= createTiddlyElement( table, "tbody" )
-			var row		= createTiddlyElement( tbody, "tr", null, styles.join(" ") )
-			var descCell	= createTiddlyElement( row,   "td", null, "description" )
-
-			var description = this.lingo.descPattern.format( [intro, descs.join(", "), off] )
-			wikify( description, descCell, null, tiddler )
-
-			var origCell	= totOrig == totCur? null
-					: createTiddlyElement( row, "td", null, "numeric original", totOrig )
-			var curCell	= createTiddlyElement( row, "td", null, "numeric current", totCur )
-			var spentCell	= createTiddlyElement( row, "td", null, "numeric spent", totSpent )
-			var remCell	= createTiddlyElement( row, "td", null, "numeric remaining", totCur-totSpent )
-
-			if ( origCell )
-				origCell.setAttribute( "title", this.lingo.origTip )
-			curCell  .setAttribute( "title", this.lingo.curTip )
-			spentCell.setAttribute( "title", this.lingo.spentTip )
-			remCell  .setAttribute( "title", this.lingo.remTip )
-
-			// Discard the table if there are no tasks
-			if ( list.length > 0 ) {
-				var place = top? list.place : place
-				var placement = top? list.placement : place.childNodes.length
-				if ( placement >= place.childNodes.length )
-					place.appendChild( table )
-				else
-					place.insertBefore( table, place.childNodes[placement] )
-			}
-		}
-		else
-			throw Error( this.lingo.unrecVerb.format([macroName]) )
-
-		// If we're wikifying, and are followed by end-of-line, swallow the newline.
-		if ( wikifier && wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-	},
-
-	// This is the stack of pending summaries
-	tasksums: []
-}
-//}}}
-/***
-!Taskadder Macro
-Usage:
-> {{{<<taskadder ["above"|"below"|"focus"|"nofocus"]...>>}}}
-Creates a line with text entry fields for a description and an estimate.  By default, puts focus in the description field and adds tasks above the entry fields.  Use {{{nofocus}}} to not put focus in the description field.  Use {{{below}}} to add tasks below the entry fields.
-***/
-//{{{
-config.macros.taskadder = {
-
-	// Translatable text:
-	lingo: {
-		unrecParam:	"<<%0>> doesn't recognize '%1' as a parameter",
-		descTip:	"Describe a new task",
-		curTip:		"Estimate how long in hours the task will take",
-		buttonText:	"add task",
-		buttonTip:	"Add a new task with the description and estimate as entered",
-		notCurrent:	"The tiddler has been modified since it was displayed, please redisplay it before adding a task this way.",
-
-		eol:		"eol"
-	},
-
-	// The macro handler
-	handler: function( place, macroName, params, wikifier, paramString, tiddler )
-	{
-		var above = true
-		var focus = false
-
-		while ( params.length > 0 ) {
-			var p = params.shift()
-			switch (p) {
-			case "above": 	above = true;  break
-			case "below": 	above = false; break
-			case "focus": 	focus = true;  break
-			case "nofocus":	focus = false; break
-			default:	throw Error( this.lingo.unrecParam.format([macroName, p]) )
-			}
-		}
-
-		// If we're followed by end-of-line, swallow the newline.
-		if ( wikifier.source.charAt(wikifier.nextMatch) == "\n" )
-			++wikifier.nextMatch
-
-		var where	= above? wikifier.matchStart : wikifier.nextMatch
-
-		var table	= createTiddlyElement( place, "table", null, "task" )
-		var tbody	= createTiddlyElement( table, "tbody" )
-		var row		= createTiddlyElement( tbody, "tr" )
-		var statusCell	= createTiddlyElement( row,   "td", null, "status" )
-		var descCell	= createTiddlyElement( row,   "td", null, "description" )
-		var curCell	= createTiddlyElement( row,   "td", null, "numeric" )
-		var addCell	= createTiddlyElement( row,   "td", null, "addtask" )
-
-		var descId	= this.generateId()
-		var curId	= this.generateId()
-		var descInput	= createTiddlyElement( descCell, "input", descId )
-		var curInput	= createTiddlyElement( curCell,  "input", curId  )
-
-		descInput.setAttribute( "type", "text" )
-		curInput .setAttribute( "type", "text" )
-		descInput.setAttribute( "size", "40")
-		curInput .setAttribute( "size", "6" )
-		descInput.setAttribute( "autocomplete", "off" );
-		curInput .setAttribute( "autocomplete", "off" );
-		descInput.setAttribute( "title", this.lingo.descTip );
-		curInput .setAttribute( "title", this.lingo.curTip  );
-
-		var addAction	= this.addTask( tiddler, where, descId, curId, above )
-		var addButton	= createTiddlyButton( addCell, this.lingo.buttonText, this.lingo.buttonTip, addAction )
-
-		descInput.onkeypress = this.handleEnter(addAction)
-		curInput .onkeypress = descInput.onkeypress
-		addButton.onkeypress = this.handleSpace(addAction)
-		if ( focus || tiddler.taskadderLocation == where ) {
-			descInput.focus()
-			descInput.select()
-		}
-	},
-
-	// Returns a function that inserts a new task macro into the tiddler.
-	addTask: function( tiddler, where, descId, curId, above ) {
-		var macro = this, oldText = tiddler.text
-		return wrapEventHandler( function(e) {
-			if ( oldText !== tiddler.text ) {
-				alert( macro.lingo.notCurrent )
-				return false
-			}
-			var desc	= document.getElementById(descId).value
-			var cur		= document.getElementById(curId) .value
-			var init	= tiddler.text.substring(0,where) + "<<task " + cur + ">> " + desc + "\n"
-			var text	= init + tiddler.text.substring(where)
-			var title	= tiddler.title
-			tiddler.taskadderLocation = (above? init.length : where)
-			try {
-				store.saveTiddler( title, title, text, config.options.txtUserName, new Date(), undefined )
-				//story.refreshTiddler( title, null, true )
-			}
-			finally {
-				delete tiddler.taskadderLocation
-			}
-			if ( config.options.chkAutoSave )
-				saveChanges()
-		} )
-	},
-
-	// Returns an event handler that delegates to two other functions: "matches" to decide
-	// whether to consume the event, and "addTask" to actually perform the work.
-	handleGeneric: function( addTask, matches ) {
-		return function(e) {
-			if (!e) var e = window.event
-			var consume = false
-			if ( matches(e) ) {
-				consume = true
-				addTask( e )
-			}
-			e.cancelBubble = consume;
-			if ( consume && e.stopPropagation ) e.stopPropagation();
-			return !consume;
-		}
-	},
-
-	// Returns an event handler that handles enter keys by calling another event handler
-	handleEnter: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return e.keyCode == 13 || e.keyCode == 10} ) // Different codes for Enter
-	},
-
-	// Returns an event handler that handles the space key by calling another event handler
-	handleSpace: function( addTask ) {
-		return this.handleGeneric( addTask, function(e){return (e.charCode||e.keyCode) == 32} )
-	},
-
-	counter: 0,
-	generateId: function() {
-		return "taskadder:" + String(this.counter++)
-	}
-}
-//}}}
-/***
-!Stylesheet
-***/
-//{{{
-var stylesheet = '\
-.viewer table.task, table.tasksum {\
-	width: 100%;\
-	padding: 0;\
-	border-collapse: collapse;\
-}\
-.viewer table.task {\
-	border: none;\
-	margin: 0;\
-}\
-table.tasksum, .viewer table.tasksum {\
-	border: solid 2px #999;\
-	margin: 3px 0;\
-}\
-table.tasksum td {\
-	text-align: center;\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	vertical-align: middle;\
-	margin: 0;\
-	padding: 0;\
-}\
-.viewer table.task tr {\
-	border: none;\
-}\
-.viewer table.task td {\
-	text-align: center;\
-	vertical-align: baseline;\
-	border: 1px solid #fff;\
-	background-color: inherit;\
-	margin: 0;\
-	padding: 0;\
-}\
-td.numeric {\
-	width: 3em;\
-}\
-table.task td.numeric div {\
-	border: 1px solid #ddd;\
-	background-color: #ffc;\
-	margin: 1px 0;\
-	padding: 0;\
-}\
-table.task td.original div {\
-	background-color: #fdd;\
-}\
-table.tasksum td.original {\
-	background-color: #fdd;\
-}\
-table.tasksum td.description {\
-	background-color: #e8e8e8;\
-}\
-table.task td.status {\
-	width: 1.5em;\
-	cursor: default;\
-}\
-table.task td.description, table.tasksum td.description {\
-	width: auto;\
-	text-align: left;\
-	padding: 0 3px;\
-}\
-table.task.done td.status,table.task.done td.description {\
-	color: #ccc;\
-}\
-table.task.done td.current, table.task.done td.remaining {\
-	visibility: hidden;\
-}\
-table.task.done td.spent div, table.tasksum tr.done td.current,\
-table.tasksum tr.done td.spent, table.tasksum tr.done td.remaining {\
-	background-color: #eee;\
-	color: #aaa;\
-}\
-table.task.nascent td.description {\
-	color: #844;\
-}\
-table.task.nascent td.current div, table.tasksum tr.nascent td.numeric.current {\
-	font-weight: bold;\
-	color: #c00;\
-	background-color: #def;\
-}\
-table.task.nascent td.spent, table.task.nascent td.remaining {\
-	visibility: hidden;\
-}\
-td.remaining {\
-	font-weight: bold;\
-}\
-.adjustable {\
-	cursor: pointer; \
-}\
-table.task input {\
-	display: block;\
-	width: 100%;\
-	font: inherit;\
-	margin: 2px 0;\
-	padding: 0;\
-	border: 1px inset #999;\
-}\
-table.task td.numeric input {\
-	background-color: #ffc;\
-	text-align: center;\
-}\
-table.task td.addtask {\
-	width: 6em;\
-	border-left: 2px solid white;\
-	vertical-align: middle;\
-}\
-'
-setStylesheet( stylesheet, "TaskMacroPluginStylesheet" )
-//}}}
-
-
-
-
!!Changes in 1.1.0
-* Made the macros work in nested tiddlers (ie when one tiddler includes another using {{{<<tiddler>>}}} or something similar):
-** Task summaries in the outer tiddler include the tasks from the inner one
-** Using the editing shortcuts on the tasks as displayed in the outer tiddler correctly changes the inner tiddler and also redisplays the outer one
-** Added sanity checks to the editing shortcuts so they will refuse to work if the tiddler has been modified behind their backs
-* Made some small usability fixes:
-** The "add task" button now responds to the Space key (hat tip: Daniel Baird)
-** Double-clicking on a completed task's bullet now does the same thing as clicking on the elapsed time: it lets you adjust the time spent, giving you the option of resurrecting the task (hat tip: ~JackF)
-** Reworked the focus handling of the taskadder macro so it works more intuitively, by refocusing on the same adder you just used
-
-
-
-
The task macro provided by the TaskMacroPlugin is for planning, estimating, and tracking detailed tasks such as those required for writing software.  It is inspired by [[Joel Spolsky|http://www.joelonsoftware.com/articles/fog0000000245.html]]'s method for scheduling software development, also popularized by [[Voo2do|http://voo2do.com]] and [[XPlanner|http://xplanner.org]].
-
-For changes since the previous version, see the TaskMacroReleaseNotes.
-
-This tutorial leads you through the use of the task macro itself, and supporting macros that summarize lists of tasks and simplify the adding of tasks to a list.  Follow along by clicking the links below.  Or click the little down-arrow next to this tiddler's title, above, and choose "Open all" to have all the tutorial sections displayed at once.
-
-
-
-
-
<!---
-Includes portions of [[TagglyTaggingViewTemplate|http://simonbaird.com/mptw/#TagglyTaggingViewTemplate]], v1.2 (16-Jan-2006).
-Also adds a pair of tasksum macros around the tiddler, to summarize any contained tasks at the top.  Removes the "-" in front of closeTiddler, which can easily bite you if you have a focusable element in a tiddler, such as a taskadder entry field.
-Portions written by Luke Blanshard are hereby released into the public domain.
---->
-<!--{{{-->
-<div class="toolbar" macro="toolbar closeTiddler closeOthers +editTiddler permalink references jump newHere"></div>
-<div class="tagglyTagged" macro="tags"></div>
-<div><span class="title" macro="view title"></span><span class="miniTag" macro="miniTag"></span></div>
-<div macro="tasksum start here"></div>
-<div class="viewer" macro="view text wikified"></div>
-<div macro="tasksum end"></div>
-<div class="tagglyTagging" macro="tagglyListWithSort"></div>
-<!--}}}-->
-
-
-
-
! Things which are imminent to do
-
-<<task 1 1 0.25>> Testsuite: Valgrind suppression file generation, glibc/pthreads leak some memory at the end of the applications, this is ok, we have to integrate this as suppressions into the testsuite, initial work done by cehteh
-<<task >> Testsuite: expected 'out:' and 'err:' should be shell glob or regex lines not exact strings. The output shall be matched against the current 'out:' or 'err:' line, on success the check continues with the actual expected glob, on fail the next glob is tried. When that fails the test is counted as failure.
-<<task >> Testsuite: when no expected 'err:' statements are in a test then stderr, including valgrind reports gets ignored, this is a bug. stderr should be filtered for nobug logging events but everything else should be checked for unexpected output.
-<<task >> Website: asciidoc switchover, git integration, rebuild website on push
-<<taskadder>>
-
-
-
-
/***
-''TextAreaPlugin for TiddlyWiki version 2.0''
-^^author: Eric Shulman - ELS Design Studios
-source: http://www.elsdesign.com/tiddlywiki/#TextAreaPlugin
-license: [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]^^
-
-This plugin 'hijacks' the TW core function, ''Story.prototype.focusTiddler()'', so it can add special 'keyDown' handlers to adjust several behaviors associated with the textarea control used in the tiddler editor.  Specifically, it:
-* Adds text search INSIDE of edit fields.^^
-Use ~CTRL-F for "Find" (prompts for search text), and ~CTRL-G for "Find Next" (uses previous search text)^^
-* Enables TAB characters to be entered into field content^^
-(instead of moving to next field)^^
-* Option to set cursor at top of edit field instead of auto-selecting contents^^
-(see configuration section for checkbox)^^
-!!!!!Configuration
-<<<
-<<option chkDisableAutoSelect>> place cursor at start of textarea instead of pre-selecting content
-<<option chkTextAreaExtensions>> add control-f (find), control-g (find again) and allow TABs as input in textarea
-<<<
-!!!!!Installation
-<<<
-Import (or copy/paste) the following tiddlers into your document:
-''TextAreaPlugin'' (tagged with <<tag systemConfig>>)
-<<<
-!!!!!Revision History
-<<<
-''2006.01.22 [1.0.1]''
-only add extra key processing for TEXTAREA elements (not other edit fields).
-added option to enable/disable textarea keydown extensions (default is "standard keys" only)
-''2006.01.22 [1.0.0]''
-Moved from temporary "System Tweaks" tiddler into 'real' TextAreaPlugin tiddler.
-<<<
-!!!!!Code
-***/
-//{{{
-version.extensions.textAreaPlugin= {major: 1, minor: 0, revision: 1, date: new Date(2006,1,23)};
-//}}}
-
-//{{{
-if (!config.options.chkDisableAutoSelect) config.options.chkDisableAutoSelect=false; // default to standard action
-if (!config.options.chkTextAreaExtensions) config.options.chkTextAreaExtensions=false; // default to standard action
-
-// Focus a specified tiddler. Attempts to focus the specified field, otherwise the first edit field it finds
-Story.prototype.focusTiddler = function(title,field)
-{
-	var tiddler = document.getElementById(this.idPrefix + title);
-	if(tiddler != null)
-		{
-		var children = tiddler.getElementsByTagName("*")
-		var e = null;
-		for (var t=0; t<children.length; t++)
-			{
-			var c = children[t];
-			if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea")
-				{
-				if(!e)
-					e = c;
-				if(c.getAttribute("edit") == field)
-					e = c;
-				}
-			}
-		if(e)
-			{
-			e.focus();
-			e.select(); // select entire contents
-
-			// TWEAK: add TAB and "find" key handlers
-			if (config.options.chkTextAreaExtensions) // add extra key handlers
-				addKeyDownHandlers(e);
-
-			// TWEAK: option to NOT autoselect contents
-			if (config.options.chkDisableAutoSelect) // set cursor to start of field content
-				if (e.setSelectionRange) e.setSelectionRange(0,0); // for FF
-				else if (e.createTextRange) { var r=e.createTextRange(); r.collapse(true); r.select(); } // for IE
-
-			}
-		}
-}
-//}}}
-
-//{{{
-function addKeyDownHandlers(e)
-{
-	// exit if not textarea or element doesn't allow selections
-	if (e.tagName.toLowerCase()!="textarea" || !e.setSelectionRange) return;
-
-	// utility function: exits keydown handler and prevents browser from processing the keystroke
-	var processed=function(ev) { ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false; }
-
-	// capture keypress in edit field
-	e.onkeydown = function(ev) { if (!ev) var ev=window.event;
-
-		// process TAB
-		if (!ev.shiftKey && ev.keyCode==9) { 
-			// replace current selection with a TAB character
-			var start=e.selectionStart; var end=e.selectionEnd;
-			e.value=e.value.substr(0,start)+String.fromCharCode(9)+e.value.substr(end);
-			// update insertion point, scroll it into view
-			e.setSelectionRange(start+1,start+1);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length-1;
-			e.scrollTop=Math.floor((thisline-e.rows/2)*e.scrollHeight/linecount);
-			return processed(ev);
-		}
-
-		// process CTRL-F (find matching text) or CTRL-G (find next match)
-		if (ev.ctrlKey && (ev.keyCode==70||ev.keyCode==71)) {
-			// if ctrl-f or no previous search, prompt for search text (default to previous text or current selection)... if no search text, exit
-			if (ev.keyCode==70||!e.find||!e.find.length)
-				{ var f=prompt("find:",e.find?e.find:e.value.substring(e.selectionStart,e.selectionEnd)); e.focus(); e.find=f?f:e.find; }
-			if (!e.find||!e.find.length) return processed(ev);
-			// do case-insensitive match with 'wraparound'...  if not found, alert and exit 
-			var newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase(),e.selectionStart+1);
-			if (newstart==-1) newstart=e.value.toLowerCase().indexOf(e.find.toLowerCase());
-			if (newstart==-1) { alert("'"+e.find+"' not found"); e.focus(); return processed(ev); }
-			// set new selection, scroll it into view, and report line position in status bar
-			e.setSelectionRange(newstart,newstart+e.find.length);
-			var linecount=e.value.split('\n').length;
-			var thisline=e.value.substr(0,e.selectionStart).split('\n').length;
-			e.scrollTop=Math.floor((thisline-1-e.rows/2)*e.scrollHeight/linecount);
-			window.status="line: "+thisline+"/"+linecount;
-			return processed(ev);
-		}
-	}
-}
-//}}}
-
-
-
! Proc layer
- by ichtho
-//the following were migrated to the lumiera-work Mailinglist as of 9/08 //
-* Asset Manager, Asset and ~MObject
-* Support for Media Bins, high-level Resource manager
-* Session, EDL, edit operations
-* Framework for storing/loading objects
-* Defaults Manager ~ConfigQuery subsystem
-* integrating YAP Prolog or another resolution system
-* create Fixture from all ~EDLs
-* create Partitioning of Fixture
-* Automation handling
-
-
-!! Builder core
-support framework done. Design/planning of node creater tool partially done, implementation just started
-!! Render nodes
-Interface and outline of the "operaton protocol" is done as of 8/08. Details of the implementation in work.
-
-! Backend
- by cehteh
-!! File handling
-Opening and accessing huge files for reading and writing
-!!! Manageing File opening, desciptors and handles
-In progress
-!!! Mmaping backend, caching
-Design finished, nothing implemented yet.
-!! Serializer
-Some ideas, nothing done yet.
-!! Scheduler
-Design ideas, nothing done yet.
-
-! Support Libary
-by cehteh and ichthyo
-
-Ongoing efforts as needed, some things already deprecated ;)
-!! Plugin Loader
-Basic implementation finished, not final yet.
-
-! Infrastructure
-!! Testsuite
-Needs some improvements, see [[Tasks]]
-!! Website
-A mockup website is online, raffa experimenting with asciidoc which looks good so far. See [[Tasks]]
-!! git / mob repos
-gitweb is up, git repos are established, mob repos are not yet up.
-!! automatic generation and checkout of tiddlywikis and doxygen docs on the server
-currently only a manual rsync script in admin/
-!! automatic builds/test on server, status page
-will be done on the 'devel' vserver. nothing done yet.
-
-
-
-
Use the backend, gmerlin/avdecoder and a Player widget to playback videos. This should be a small application which can open and playback many videos from on instance in parallel. Later on scrubbing for this videos might be supported.
-
-The idea here is to show and measure backend and scheduler performance and beeing a proof of concept for the design. It will run independent of the Proc layer but needs quite some work on the backend to be done.
-
-
- - - - - - - - - -