changeset 344:3be1612afcba

unpacked twikidraw sources, simpler build.xml imported from: moin--main--1.5--patch-348
author Thomas Waldmann <tw@waldmann-edv.de>
date Tue, 27 Dec 2005 23:09:56 +0000
parents 2299e10caf8b
children c954897d7ac4
files contrib/TWikiDrawPlugin/README contrib/TWikiDrawPlugin/build.xml contrib/TWikiDrawPlugin/packages/Acme/IntHashtable.java contrib/TWikiDrawPlugin/packages/Acme/JPM/Encoders/GifEncoder.java contrib/TWikiDrawPlugin/packages/Acme/JPM/Encoders/ImageEncoder.java contrib/TWikiDrawPlugin/packages/com/keypoint/PngEncoder.java contrib/TWikiDrawPlugin/packages/com/keypoint/PngEncoderIndexed.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/appframe/Application.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/appframe/DrawFrame.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/appframe/LightweightDrawApplet.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/applet/DrawApplet.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/applet/TestApplet.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/ChopPolygonConnector.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/DiamondFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/PolygonFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/PolygonHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/PolygonScaleHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/PolygonTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/TriangleFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/TriangleRotationHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ArrowTip.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/AttributeFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/BorderDecorator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/BorderTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ChopEllipseConnector.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ConnectedTextTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ElbowConnection.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ElbowHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ElbowTextLocator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/EllipseFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/FontSizeHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/GroupCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/GroupFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/GroupHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ImageFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/InsertImageCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/LineConnection.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/LineDecoration.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/LineFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/NumberTextFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/PolyLineConnector.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/PolyLineFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/PolyLineHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/PolyLineLocator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/RadiusHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/RectangleFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/RoundRectangleFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ScribbleTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ShortestDistanceConnector.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/TextFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/TextTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/URLTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/UngroupCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/ConnectionFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Connector.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Drawing.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/DrawingChangeEvent.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/DrawingChangeListener.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/DrawingEditor.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/DrawingView.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Figure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/FigureChangeEvent.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/FigureChangeListener.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/FigureEnumeration.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/FigureSelection.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/HJDError.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Handle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Locator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Painter.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/PointConstrainer.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Tool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/images/StaticImages.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/AbstractConnector.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/AbstractFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/AbstractHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/AbstractLocator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/AbstractTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ActionTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/AlignCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/BoxHandleKit.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/BringToFrontCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/BufferedUpdateStrategy.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ChangeAttributeCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ChangeConnectionEndHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ChangeConnectionHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ChangeConnectionStartHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ChopBoxConnector.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/CompositeFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ConnectionHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ConnectionTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/CopyCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/CreationTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/CutCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/DecoratorFigure.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/DeleteCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/DragTracker.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/DuplicateCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/FigureAttributes.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/FigureChangeEventMulticaster.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/FigureEnumerator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/FigureTransferCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/GridConstrainer.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/HandleTracker.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/LocatorConnector.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/LocatorHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/NullHandle.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/OffsetLocator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/PasteCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/RelativeLocator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ReverseFigureEnumerator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/SelectAreaTracker.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/SelectionTool.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/SendToBackCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/SimpleUpdateStrategy.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/StandardDrawing.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/StandardDrawingView.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/TextHolder.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ToggleGridCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ToggleGuidesCommand.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/standard/ToolButton.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/twiki/TWikiDraw.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/twiki/TWikiFrame.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/twiki/TestFrame.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/Animatable.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/Clipboard.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/ColorMap.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/Command.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/CommandButton.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/CommandChoice.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/CommandMenu.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/Filler.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/FloatingTextField.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/Geom.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/Iconkit.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/MenuAdapterButton.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/PaletteButton.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/PaletteIcon.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/PaletteLayout.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/PaletteListener.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/ReverseVectorEnumerator.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/Storable.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/StorableInput.java contrib/TWikiDrawPlugin/src/CH/ifa/draw/util/StorableOutput.java contrib/TWikiDrawPlugin/twikidraw-sources.zip
diffstat 144 files changed, 18692 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/TWikiDrawPlugin/README	Sun Dec 25 22:54:33 2005 +0000
+++ b/contrib/TWikiDrawPlugin/README	Tue Dec 27 23:09:56 2005 +0000
@@ -12,16 +12,25 @@
  - xhtml conform image maps
  - corrects object stacking order for the map
 
+To build this plugin from sources you need the following:
+ * JDK 1.2 or later version (http://java.sun.com/downloads)
+ * Ant 1.5.4 or later version (http://ant.apache.org/)
+
+To build just run change current directory to /contrib/TWikiDrawPlugin
+and run Ant. Ant automatically loads build.xml from current directory 
+and creates a twikidraw.jar in /contrib/TWikiDrawPlugin/build directory.
+
 TODO:
  * look at http://moinmoin.wikiwikiweb.de/MoinMoinTodo/Release_1.5 - see
    the "Unfinished" section for more todo stuff relating to TWikiDraw
- * build.xml must NOT download any stuff, but use the source archive we have
-   already in this directory.
  * make some bash script that invokes the build process and puts the resulting
    applet into wiki/htdocs/applets/TWikiDrawPlugin
  * document build requirements
  * test with some Java enabled browser if it works :)
 
+Applet tested on:
+ * Windows XP SP 1 / Sun JDK 1.4.2 / IE 6.0 and Mozilla Firefox 1.5
+
 References:
  - Original site: http://twiki.org/cgi-bin/view/Plugins/TWikiDrawPlugin
  - Moin changes: http://moinmoin.wikiwikiweb.de/OliverGraf/TWikiDrawPlugin
--- a/contrib/TWikiDrawPlugin/build.xml	Sun Dec 25 22:54:33 2005 +0000
+++ b/contrib/TWikiDrawPlugin/build.xml	Tue Dec 27 23:09:56 2005 +0000
@@ -1,88 +1,30 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
 
 <project name="TWikiDrawPlugin" default="build" basedir=".">
-
-    <property name="repository.home" value="repository"/>
+    <property name="src.home" value="."/>
     <property name="build.home" value="build"/>
-
-    <property name="TWikiDrawPlugin.loc" 
-        value="http://twiki.org/p/pub/Plugins/TWikiDrawPlugin/"/>
-    <property name="TWikiDrawPlugin.patched.loc" 
-        value="http://moinmoin.wikiwikiweb.de/OliverGraf/TWikiDrawPlugin?action=AttachFile&amp;do=get&amp;target="/>
+    <property name="dest.home" value="build"/>
 
     <target name="-init">
-        <mkdir dir="${repository.home}"/>
+        <mkdir dir="${build.home}"/>
         <mkdir dir="${build.home}/classes"/>
-    </target>
-
-    <target name="-checkfile">
-        <echo message="Checking ${file}"/>
-        <available file="${repository.home}/${file}" property="havefile"/>
-    </target>
-
-    <target name="getfile" unless="havefile" depends="-checkfile">
-        <get src="${url}${file}" dest="${repository.home}/${file}" />
+        <mkdir dir="${dest.home}"/>
     </target>
 
-    <target name="download" depends="-init">
-        <antcall target="getfile">
-            <param name="url" value="${TWikiDrawPlugin.loc}"/>
-            <param name="file" value="TWikiDrawPlugin.zip"/>
-        </antcall>
-        <antcall target="getfile">
-            <param name="url" value="${TWikiDrawPlugin.patched.loc}"/>
-            <param name="file" value="twikidraw-sources.zip"/>
-        </antcall>
-        <antcall target="getfile">
-            <param name="url" value="${TWikiDrawPlugin.patched.loc}"/>
-            <param name="file" value="twikidraw-full.diff"/>
-        </antcall>
-        <antcall target="getfile">
-            <param name="url" value="${TWikiDrawPlugin.patched.loc}"/>
-            <param name="file" value="twikidraw-png.zip"/>
-        </antcall>
-
-        <unzip dest="${build.home}/patched"
-            src="${repository.home}/twikidraw-sources.zip"/>
-
-        <unzip src="${repository.home}/TWikiDrawPlugin.zip" dest="${build.home}">
-            <patternset>
-                <include name="lib/**/source.zip"/>
-            </patternset>
-        </unzip>
-        <unzip dest="${build.home}/current">
-            <fileset dir="${build.home}">
-                <include name="lib/**/source.zip"/>
-            </fileset>
-        </unzip>
-        <unzip dest="${build.home}/current"
-            src="${repository.home}/twikidraw-png.zip"/>
-
-        <patch patchfile="${repository.home}/twikidraw-full.diff" 
-            strip="1" dir="${build.home}/current"/>
-    </target>
-
-    <target name="build" depends="-init, download"
-            description="Build TWikiDrawPlugin JAR">
-        <javac destdir="${build.home}/classes" 
-                source="1.3" target="1.1">
-            <src path="${build.home}/patched/src"/>
-            <src path="${build.home}/patched/packages"/>
+    <target name="build" depends="-init" description="Build TWikiDrawPlugin JAR">
+        <javac destdir="${build.home}/classes" source="1.3" target="1.1">
+            <src path="${src.home}/packages"/>
+            <src path="${src.home}/src"/>
         </javac>
 
-        <jar destfile="${build.home}/twikidraw.jar">
+        <jar destfile="${dest.home}/twikidraw.jar">
             <fileset dir="${build.home}/classes"/>
         </jar>
     </target>
 
-    <target name="clean"
-            description="Remove generated files">
+    <target name="clean" description="Remove generated files">
         <delete dir="${build.home}"/>
     </target>
 
-    <target name="clean-all" depends="clean"
-            description="Remove ALL intermediate files">
-        <delete dir="${repository.home}"/>
-    </target>
-
+	<target name="rebuild" depends="clean, build" description="Rebuild TWikiDrawPlugin JAR"/>
 </project> 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/packages/Acme/IntHashtable.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,397 @@
+// IntHashtable - a Hashtable that uses ints as the keys
+// http://www.acme.com/java/software/Acme.IntHashtable.html
+//
+// This is 90% based on JavaSoft's java.util.Hashtable.
+//
+// Visit the ACME Labs Java page for up-to-date versions of this and other
+// fine Java utilities: http://www.acme.com/java/
+
+package Acme;
+
+import java.util.*;
+
+/// A Hashtable that uses ints as the keys.
+// <P>
+// Use just like java.util.Hashtable, except that the keys must be ints.
+// This is much faster than creating a new Integer for each access.
+// <P>
+// <A HREF="/resources/classes/Acme/IntHashtable.java">Fetch the software.</A><BR>
+// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
+// <P>
+// @see java.util.Hashtable
+
+public class IntHashtable extends Dictionary implements Cloneable
+    {
+    /// The hash table data.
+    private IntHashtableEntry table[];
+
+    /// The total number of entries in the hash table.
+    private int count;
+
+    /// Rehashes the table when count exceeds this threshold.
+    private int threshold;
+
+    /// The load factor for the hashtable.
+    private float loadFactor;
+
+    /// Constructs a new, empty hashtable with the specified initial 
+    // capacity and the specified load factor.
+    // @param initialCapacity the initial number of buckets
+    // @param loadFactor a number between 0.0 and 1.0, it defines
+    //		the threshold for rehashing the hashtable into
+    //		a bigger one.
+    // @exception IllegalArgumentException If the initial capacity
+    // is less than or equal to zero.
+    // @exception IllegalArgumentException If the load factor is
+    // less than or equal to zero.
+    public IntHashtable( int initialCapacity, float loadFactor )
+	{
+	if ( initialCapacity <= 0 || loadFactor <= 0.0 )
+	    throw new IllegalArgumentException();
+	this.loadFactor = loadFactor;
+	table = new IntHashtableEntry[initialCapacity];
+	threshold = (int) ( initialCapacity * loadFactor );
+	}
+
+    /// Constructs a new, empty hashtable with the specified initial 
+    // capacity.
+    // @param initialCapacity the initial number of buckets
+    public IntHashtable( int initialCapacity )
+	{
+	this( initialCapacity, 0.75f );
+	}
+
+    /// Constructs a new, empty hashtable. A default capacity and load factor
+    // is used. Note that the hashtable will automatically grow when it gets
+    // full.
+    public IntHashtable()
+	{
+	this( 101, 0.75f );
+	}
+
+    /// Returns the number of elements contained in the hashtable. 
+    public int size()
+	{
+	return count;
+	}
+
+    /// Returns true if the hashtable contains no elements.
+    public boolean isEmpty()
+	{
+	return count == 0;
+	}
+
+    /// Returns an enumeration of the hashtable's keys.
+    // @see IntHashtable#elements
+    public synchronized Enumeration keys()
+	{
+	return new IntHashtableEnumerator( table, true );
+	}
+
+    /// Returns an enumeration of the elements. Use the Enumeration methods 
+    // on the returned object to fetch the elements sequentially.
+    // @see IntHashtable#keys
+    public synchronized Enumeration elements()
+	{
+	return new IntHashtableEnumerator( table, false );
+	}
+
+    /// Returns true if the specified object is an element of the hashtable.
+    // This operation is more expensive than the containsKey() method.
+    // @param value the value that we are looking for
+    // @exception NullPointerException If the value being searched 
+    // for is equal to null.
+    // @see IntHashtable#containsKey
+    public synchronized boolean contains( Object value )
+	{
+	if ( value == null )
+	    throw new NullPointerException();
+	IntHashtableEntry tab[] = table;
+	for ( int i = tab.length ; i-- > 0 ; )
+	    {
+	    for ( IntHashtableEntry e = tab[i] ; e != null ; e = e.next )
+		{
+		if ( e.value.equals( value ) )
+		    return true;
+		}
+	    }
+	return false;
+	}
+
+    /// Returns true if the collection contains an element for the key.
+    // @param key the key that we are looking for
+    // @see IntHashtable#contains
+    public synchronized boolean containsKey( int key )
+	{
+	IntHashtableEntry tab[] = table;
+	int hash = key;
+	int index = ( hash & 0x7FFFFFFF ) % tab.length;
+	for ( IntHashtableEntry e = tab[index] ; e != null ; e = e.next )
+	    {
+	    if ( e.hash == hash && e.key == key )
+		return true;
+	    }
+	return false;
+	}
+
+    /// Gets the object associated with the specified key in the 
+    // hashtable.
+    // @param key the specified key
+    // @returns the element for the key or null if the key
+    // 		is not defined in the hash table.
+    // @see IntHashtable#put
+    public synchronized Object get( int key )
+	{
+	IntHashtableEntry tab[] = table;
+	int hash = key;
+	int index = ( hash & 0x7FFFFFFF ) % tab.length;
+	for ( IntHashtableEntry e = tab[index] ; e != null ; e = e.next )
+	    {
+	    if ( e.hash == hash && e.key == key )
+		return e.value;
+	    }
+	return null;
+	}
+
+    /// A get method that takes an Object, for compatibility with
+    // java.util.Dictionary.  The Object must be an Integer.
+    public Object get( Object okey )
+	{
+	if ( ! ( okey instanceof Integer ) )
+	    throw new InternalError( "key is not an Integer" );
+	Integer ikey = (Integer) okey;
+	int key = ikey.intValue();
+	return get( key );
+	}
+
+    /// Rehashes the content of the table into a bigger table.
+    // This method is called automatically when the hashtable's
+    // size exceeds the threshold.
+    protected void rehash()
+	{
+	int oldCapacity = table.length;
+	IntHashtableEntry oldTable[] = table;
+
+	int newCapacity = oldCapacity * 2 + 1;
+	IntHashtableEntry newTable[] = new IntHashtableEntry[newCapacity];
+
+	threshold = (int) ( newCapacity * loadFactor );
+	table = newTable;
+
+	for ( int i = oldCapacity ; i-- > 0 ; )
+	    {
+	    for ( IntHashtableEntry old = oldTable[i] ; old != null ; )
+		{
+		IntHashtableEntry e = old;
+		old = old.next;
+
+		int index = ( e.hash & 0x7FFFFFFF ) % newCapacity;
+		e.next = newTable[index];
+		newTable[index] = e;
+		}
+	    }
+	}
+
+    /// Puts the specified element into the hashtable, using the specified
+    // key.  The element may be retrieved by doing a get() with the same key.
+    // The key and the element cannot be null. 
+    // @param key the specified key in the hashtable
+    // @param value the specified element
+    // @exception NullPointerException If the value of the element 
+    // is equal to null.
+    // @see IntHashtable#get
+    // @return the old value of the key, or null if it did not have one.
+    public synchronized Object put( int key, Object value )
+	{
+	// Make sure the value is not null.
+	if ( value == null )
+	    throw new NullPointerException();
+
+	// Makes sure the key is not already in the hashtable.
+	IntHashtableEntry tab[] = table;
+	int hash = key;
+	int index = ( hash & 0x7FFFFFFF ) % tab.length;
+	for ( IntHashtableEntry e = tab[index] ; e != null ; e = e.next )
+	    {
+	    if ( e.hash == hash && e.key == key )
+		{
+		Object old = e.value;
+		e.value = value;
+		return old;
+		}
+	    }
+
+	if ( count >= threshold )
+	    {
+	    // Rehash the table if the threshold is exceeded.
+	    rehash();
+	    return put( key, value );
+	    } 
+
+	// Creates the new entry.
+	IntHashtableEntry e = new IntHashtableEntry();
+	e.hash = hash;
+	e.key = key;
+	e.value = value;
+	e.next = tab[index];
+	tab[index] = e;
+	++count;
+	return null;
+	}
+
+    /// A put method that takes an Object, for compatibility with
+    // java.util.Dictionary.  The Object must be an Integer.
+    public Object put( Object okey, Object value )
+	{
+	if ( ! ( okey instanceof Integer ) )
+	    throw new InternalError( "key is not an Integer" );
+	Integer ikey = (Integer) okey;
+	int key = ikey.intValue();
+	return put( key, value );
+	}
+
+    /// Removes the element corresponding to the key. Does nothing if the
+    // key is not present.
+    // @param key the key that needs to be removed
+    // @return the value of key, or null if the key was not found.
+    public synchronized Object remove( int key )
+	{
+	IntHashtableEntry tab[] = table;
+	int hash = key;
+	int index = ( hash & 0x7FFFFFFF ) % tab.length;
+	for ( IntHashtableEntry e = tab[index], prev = null ; e != null ; prev = e, e = e.next )
+	    {
+	    if ( e.hash == hash && e.key == key )
+		{
+		if ( prev != null )
+		    prev.next = e.next;
+		else
+		    tab[index] = e.next;
+		--count;
+		return e.value;
+		}
+	    }
+	return null;
+	}
+
+    /// A remove method that takes an Object, for compatibility with
+    // java.util.Dictionary.  The Object must be an Integer.
+    public Object remove( Object okey )
+	{
+	if ( ! ( okey instanceof Integer ) )
+	    throw new InternalError( "key is not an Integer" );
+	Integer ikey = (Integer) okey;
+	int key = ikey.intValue();
+	return remove( key );
+	}
+
+    /// Clears the hash table so that it has no more elements in it.
+    public synchronized void clear()
+	{
+	IntHashtableEntry tab[] = table;
+	for ( int index = tab.length; --index >= 0; )
+	    tab[index] = null;
+	count = 0;
+	}
+
+    /// Creates a clone of the hashtable. A shallow copy is made,
+    // the keys and elements themselves are NOT cloned. This is a
+    // relatively expensive operation.
+    public synchronized Object clone()
+	{
+	try
+	    {
+	    IntHashtable t = (IntHashtable) super.clone();
+	    t.table = new IntHashtableEntry[table.length];
+	    for ( int i = table.length ; i-- > 0 ; )
+		t.table[i] = ( table[i] != null ) ?
+		    (IntHashtableEntry) table[i].clone() : null;
+	    return t;
+	    }
+	catch ( CloneNotSupportedException e)
+	    {
+	    // This shouldn't happen, since we are Cloneable.
+	    throw new InternalError();
+	    }
+	}
+
+    /// Converts to a rather lengthy String.
+    public synchronized String toString()
+	{
+	int max = size() - 1;
+	StringBuffer buf = new StringBuffer();
+	Enumeration k = keys();
+	Enumeration e = elements();
+	buf.append( "{" );
+
+	for ( int i = 0; i <= max; ++i )
+	    {
+	    String s1 = k.nextElement().toString();
+	    String s2 = e.nextElement().toString();
+	    buf.append( s1 + "=" + s2 );
+	    if ( i < max )
+		buf.append( ", " );
+	    }
+	buf.append( "}" );
+	return buf.toString();
+	}
+    }
+
+
+class IntHashtableEntry
+    {
+    int hash;
+    int key;
+    Object value;
+    IntHashtableEntry next;
+
+    protected Object clone()
+	{
+	IntHashtableEntry entry = new IntHashtableEntry();
+	entry.hash = hash;
+	entry.key = key;
+	entry.value = value;
+	entry.next = ( next != null ) ? (IntHashtableEntry) next.clone() : null;
+	return entry;
+	}
+    }
+
+
+class IntHashtableEnumerator implements Enumeration
+    {
+    boolean keys;
+    int index;
+    IntHashtableEntry table[];
+    IntHashtableEntry entry;
+
+    IntHashtableEnumerator( IntHashtableEntry table[], boolean keys )
+	{
+	this.table = table;
+	this.keys = keys;
+	this.index = table.length;
+	}
+	
+    public boolean hasMoreElements()
+	{
+	if ( entry != null )
+	    return true;
+	while ( index-- > 0 )
+	    if ( ( entry = table[index] ) != null )
+		return true;
+	return false;
+	}
+
+    public Object nextElement()
+	{
+	if ( entry == null )
+	    while ( ( index-- > 0 ) && ( ( entry = table[index] ) == null ) )
+		;
+	if ( entry != null )
+	    {
+	    IntHashtableEntry e = entry;
+	    entry = e.next;
+	    return keys ? new Integer( e.key ) : e.value;
+	    }
+	throw new NoSuchElementException( "IntHashtableEnumerator" );
+	}
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/packages/Acme/JPM/Encoders/GifEncoder.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,692 @@
+// GifEncoder - write out an image as a GIF
+// http://www.acme.com/java/software/Acme.JPM.Encoders.GifEncoder.html
+//
+// Transparency handling and variable bit size courtesy of Jack Palevich.
+//
+// Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. 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.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+//
+// Visit the ACME Labs Java page for up-to-date versions of this and other
+// fine Java utilities: http://www.acme.com/java/
+
+package Acme.JPM.Encoders;
+
+import java.util.*;
+import java.io.*;
+import java.awt.Image;
+import java.awt.image.*;
+
+/// Write out an image as a GIF.
+// <P>
+// <A HREF="/resources/classes/Acme/JPM/Encoders/GifEncoder.java">Fetch the software.</A><BR>
+// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
+// <P>
+// @see ToGif
+
+public class GifEncoder extends ImageEncoder
+    {
+
+    private boolean interlace = false;
+
+    /// Constructor from Image.
+    // @param img The image to encode.
+    // @param out The stream to write the GIF to.
+    public GifEncoder( Image img, OutputStream out ) throws IOException
+	{
+	super( img, out );
+	}
+
+    /// Constructor from Image with interlace setting.
+    // @param img The image to encode.
+    // @param out The stream to write the GIF to.
+    // @param interlace Whether to interlace.
+    public GifEncoder( Image img, OutputStream out, boolean interlace ) throws IOException
+	{
+	super( img, out );
+	this.interlace = interlace;
+	}
+
+    /// Constructor from ImageProducer.
+    // @param prod The ImageProducer to encode.
+    // @param out The stream to write the GIF to.
+    public GifEncoder( ImageProducer prod, OutputStream out ) throws IOException
+	{
+	super( prod, out );
+	}
+
+    /// Constructor from ImageProducer with interlace setting.
+    // @param prod The ImageProducer to encode.
+    // @param out The stream to write the GIF to.
+    public GifEncoder( ImageProducer prod, OutputStream out, boolean interlace ) throws IOException
+	{
+	super( prod, out );
+	this.interlace = interlace;
+	}
+
+
+    int width, height;
+    int[][] rgbPixels;
+
+    void encodeStart( int width, int height ) throws IOException
+	{
+	this.width = width;
+	this.height = height;
+	rgbPixels = new int[height][width];
+	}
+
+    void encodePixels(
+	int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
+	throws IOException
+	{
+	// Save the pixels.
+	for ( int row = 0; row < h; ++row )
+	    System.arraycopy(
+		rgbPixels, row * scansize + off,
+		this.rgbPixels[y + row], x, w );
+
+	}
+
+    Acme.IntHashtable colorHash;
+
+    void encodeDone() throws IOException
+	{
+	int transparentIndex = -1;
+	int transparentRgb = -1;
+        // Put all the pixels into a hash table.
+        colorHash = new Acme.IntHashtable();
+	int index = 0;
+        for ( int row = 0; row < height; ++row )
+            {
+            int rowOffset = row * width;
+            for ( int col = 0; col < width; ++col )
+                {
+                int rgb = rgbPixels[row][col];
+		boolean isTransparent = ( ( rgb >>> 24 ) < 0x80 );
+		if ( isTransparent )
+		    {
+		    if ( transparentIndex < 0 )
+			{
+			// First transparent color; remember it.
+			transparentIndex = index;
+			transparentRgb = rgb;
+			}
+		    else if ( rgb != transparentRgb )
+			{
+			// A second transparent color; replace it with
+			// the first one.
+			rgbPixels[row][col] = rgb = transparentRgb;
+			}
+		    }
+                GifEncoderHashitem item =
+		    (GifEncoderHashitem) colorHash.get( rgb );
+                if ( item == null )
+		    {
+		    if ( index >= 256 )
+			throw new IOException( "too many colors for a GIF" );
+                    item = new GifEncoderHashitem(
+			rgb, 1, index, isTransparent );
+		    ++index;
+		    colorHash.put( rgb, item );
+		    }
+                else
+                    ++item.count;
+                }
+            }
+
+	// Figure out how many bits to use.
+	int logColors;
+	if ( index <= 2 )
+	    logColors = 1;
+	else if ( index <= 4 )
+	    logColors = 2;
+	else if ( index <= 16 )
+	    logColors = 4;
+	else
+	    logColors = 8;
+
+	// Turn colors into colormap entries.
+	int mapSize = 1 << logColors;
+	byte[] reds = new byte[mapSize];
+	byte[] grns = new byte[mapSize];
+	byte[] blus = new byte[mapSize];
+	for ( Enumeration e = colorHash.elements(); e.hasMoreElements(); )
+	    {
+	    GifEncoderHashitem item = (GifEncoderHashitem) e.nextElement();
+	    reds[item.index] = (byte) ( ( item.rgb >> 16 ) & 0xff );
+	    grns[item.index] = (byte) ( ( item.rgb >>  8 ) & 0xff );
+	    blus[item.index] = (byte) (   item.rgb         & 0xff );
+	    }
+
+	GIFEncode(
+	    out, width, height, interlace, (byte) 0, transparentIndex,
+	    logColors, reds, grns, blus );
+	}
+
+    byte GetPixel( int x, int y ) throws IOException
+	{
+	GifEncoderHashitem item =
+	    (GifEncoderHashitem) colorHash.get( rgbPixels[y][x] );
+	if ( item == null )
+	    throw new IOException( "color not found" );
+	return (byte) item.index;
+	}
+
+    static void writeString( OutputStream out, String str ) throws IOException
+        {
+        byte[] buf = str.getBytes();
+        out.write( buf );
+        }
+
+    // Adapted from ppmtogif, which is based on GIFENCOD by David
+    // Rowley <mgardi@watdscu.waterloo.edu>.  Lempel-Zim compression
+    // based on "compress".
+
+    int Width, Height;
+    boolean Interlace;
+    int curx, cury;
+    int CountDown;
+    int Pass = 0;
+
+    void GIFEncode(
+	OutputStream outs, int Width, int Height, boolean Interlace, byte Background, int Transparent, int BitsPerPixel, byte[] Red, byte[] Green, byte[] Blue )
+	throws IOException
+	{
+	byte B;
+	int LeftOfs, TopOfs;
+	int ColorMapSize;
+	int InitCodeSize;
+	int i;
+
+	this.Width = Width;
+	this.Height = Height;
+	this.Interlace = Interlace;
+	ColorMapSize = 1 << BitsPerPixel;
+	LeftOfs = TopOfs = 0;
+
+	// Calculate number of bits we are expecting
+	CountDown = Width * Height;
+
+	// Indicate which pass we are on (if interlace)
+	Pass = 0;
+
+	// The initial code size
+	if ( BitsPerPixel <= 1 )
+	    InitCodeSize = 2;
+	else
+	    InitCodeSize = BitsPerPixel;
+
+	// Set up the current x and y position
+	curx = 0;
+	cury = 0;
+
+	// Write the Magic header
+	writeString( outs, "GIF89a" );
+
+	// Write out the screen width and height
+	Putword( Width, outs );
+	Putword( Height, outs );
+
+	// Indicate that there is a global colour map
+	B = (byte) 0x80;		// Yes, there is a color map
+	// OR in the resolution
+	B |= (byte) ( ( 8 - 1 ) << 4 );
+	// Not sorted
+	// OR in the Bits per Pixel
+	B |= (byte) ( ( BitsPerPixel - 1 ) );
+
+	// Write it out
+	Putbyte( B, outs );
+
+	// Write out the Background colour
+	Putbyte( Background, outs );
+
+	// Pixel aspect ratio - 1:1.
+	//Putbyte( (byte) 49, outs );
+	// Java's GIF reader currently has a bug, if the aspect ratio byte is
+	// not zero it throws an ImageFormatException.  It doesn't know that
+	// 49 means a 1:1 aspect ratio.  Well, whatever, zero works with all
+	// the other decoders I've tried so it probably doesn't hurt.
+	Putbyte( (byte) 0, outs );
+
+	// Write out the Global Colour Map
+	for ( i = 0; i < ColorMapSize; ++i )
+	    {
+	    Putbyte( Red[i], outs );
+	    Putbyte( Green[i], outs );
+	    Putbyte( Blue[i], outs );
+	    }
+
+	// Write out extension for transparent colour index, if necessary.
+	if ( Transparent != -1 )
+	    {
+	    Putbyte( (byte) '!', outs );
+	    Putbyte( (byte) 0xf9, outs );
+	    Putbyte( (byte) 4, outs );
+	    Putbyte( (byte) 1, outs );
+	    Putbyte( (byte) 0, outs );
+	    Putbyte( (byte) 0, outs );
+	    Putbyte( (byte) Transparent, outs );
+	    Putbyte( (byte) 0, outs );
+	    }
+
+	// Write an Image separator
+	Putbyte( (byte) ',', outs );
+
+	// Write the Image header
+	Putword( LeftOfs, outs );
+	Putword( TopOfs, outs );
+	Putword( Width, outs );
+	Putword( Height, outs );
+
+	// Write out whether or not the image is interlaced
+	if ( Interlace )
+	    Putbyte( (byte) 0x40, outs );
+	else
+	    Putbyte( (byte) 0x00, outs );
+
+	// Write out the initial code size
+	Putbyte( (byte) InitCodeSize, outs );
+
+	// Go and actually compress the data
+	compress( InitCodeSize+1, outs );
+
+	// Write out a Zero-length packet (to end the series)
+	Putbyte( (byte) 0, outs );
+
+	// Write the GIF file terminator
+	Putbyte( (byte) ';', outs );
+	}
+
+    // Bump the 'curx' and 'cury' to point to the next pixel
+    void BumpPixel()
+	{
+	// Bump the current X position
+	++curx;
+
+	// If we are at the end of a scan line, set curx back to the beginning
+	// If we are interlaced, bump the cury to the appropriate spot,
+	// otherwise, just increment it.
+	if ( curx == Width )
+	    {
+	    curx = 0;
+
+	    if ( ! Interlace )
+		++cury;
+	    else
+		{
+		switch( Pass )
+		    {
+		    case 0:
+		    cury += 8;
+		    if ( cury >= Height )
+			{
+			++Pass;
+			cury = 4;
+			}
+		    break;
+
+		    case 1:
+		    cury += 8;
+		    if ( cury >= Height )
+			{
+			++Pass;
+			cury = 2;
+			}
+		    break;
+
+		    case 2:
+		    cury += 4;
+		    if ( cury >= Height )
+			{
+			++Pass;
+			cury = 1;
+			}
+		    break;
+
+		    case 3:
+		    cury += 2;
+		    break;
+		    }
+		}
+	    }
+	}
+
+    static final int EOF = -1;
+
+    // Return the next pixel from the image
+    int GIFNextPixel() throws IOException
+	{
+	byte r;
+
+	if ( CountDown == 0 )
+	    return EOF;
+
+	--CountDown;
+
+	r = GetPixel( curx, cury );
+
+	BumpPixel();
+
+	return r & 0xff;
+	}
+
+    // Write out a word to the GIF file
+    void Putword( int w, OutputStream outs ) throws IOException
+	{
+	Putbyte( (byte) ( w & 0xff ), outs );
+	Putbyte( (byte) ( ( w >> 8 ) & 0xff ), outs );
+	}
+
+    // Write out a byte to the GIF file
+    void Putbyte( byte b, OutputStream outs ) throws IOException
+	{
+	outs.write( b );
+	}
+
+
+    // GIFCOMPR.C       - GIF Image compression routines
+    //
+    // Lempel-Ziv compression based on 'compress'.  GIF modifications by
+    // David Rowley (mgardi@watdcsu.waterloo.edu)
+
+    // General DEFINEs
+
+    static final int BITS = 12;
+
+    static final int HSIZE = 5003;		// 80% occupancy
+
+    // GIF Image compression - modified 'compress'
+    //
+    // Based on: compress.c - File compression ala IEEE Computer, June 1984.
+    //
+    // By Authors:  Spencer W. Thomas      (decvax!harpo!utah-cs!utah-gr!thomas)
+    //              Jim McKie              (decvax!mcvax!jim)
+    //              Steve Davies           (decvax!vax135!petsd!peora!srd)
+    //              Ken Turkowski          (decvax!decwrl!turtlevax!ken)
+    //              James A. Woods         (decvax!ihnp4!ames!jaw)
+    //              Joe Orost              (decvax!vax135!petsd!joe)
+
+    int n_bits;				// number of bits/code
+    int maxbits = BITS;			// user settable max # bits/code
+    int maxcode;			// maximum code, given n_bits
+    int maxmaxcode = 1 << BITS; // should NEVER generate this code
+
+    final int MAXCODE( int n_bits )
+	{
+	return ( 1 << n_bits ) - 1;
+	}
+
+    int[] htab = new int[HSIZE];
+    int[] codetab = new int[HSIZE];
+
+    int hsize = HSIZE;		// for dynamic table sizing
+
+    int free_ent = 0;			// first unused entry
+
+    // block compression parameters -- after all codes are used up,
+    // and compression rate changes, start over.
+    boolean clear_flg = false;
+
+    // Algorithm:  use open addressing double hashing (no chaining) on the
+    // prefix code / next character combination.  We do a variant of Knuth's
+    // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+    // secondary probe.  Here, the modular division first probe is gives way
+    // to a faster exclusive-or manipulation.  Also do block compression with
+    // an adaptive reset, whereby the code table is cleared when the compression
+    // ratio decreases, but after the table fills.  The variable-length output
+    // codes are re-sized at this point, and a special CLEAR code is generated
+    // for the decompressor.  Late addition:  construct the table according to
+    // file size for noticeable speed improvement on small files.  Please direct
+    // questions about this implementation to ames!jaw.
+
+    int g_init_bits;
+
+    int ClearCode;
+    int EOFCode;
+
+    void compress( int init_bits, OutputStream outs ) throws IOException
+	{
+	int fcode;
+	int i /* = 0 */;
+	int c;
+	int ent;
+	int disp;
+	int hsize_reg;
+	int hshift;
+
+	// Set up the globals:  g_init_bits - initial number of bits
+	g_init_bits = init_bits;
+
+	// Set up the necessary values
+	clear_flg = false;
+	n_bits = g_init_bits;
+	maxcode = MAXCODE( n_bits );
+
+	ClearCode = 1 << ( init_bits - 1 );
+	EOFCode = ClearCode + 1;
+	free_ent = ClearCode + 2;
+
+	char_init();
+
+	ent = GIFNextPixel();
+
+	hshift = 0;
+	for ( fcode = hsize; fcode < 65536; fcode *= 2 )
+	    ++hshift;
+	hshift = 8 - hshift;			// set hash code range bound
+
+	hsize_reg = hsize;
+	cl_hash( hsize_reg );	// clear hash table
+
+	output( ClearCode, outs );
+
+	outer_loop:
+	while ( (c = GIFNextPixel()) != EOF )
+	    {
+	    fcode = ( c << maxbits ) + ent;
+	    i = ( c << hshift ) ^ ent;		// xor hashing
+
+	    if ( htab[i] == fcode )
+		{
+		ent = codetab[i];
+		continue;
+		}
+	    else if ( htab[i] >= 0 )	// non-empty slot
+		{
+		disp = hsize_reg - i;	// secondary hash (after G. Knott)
+		if ( i == 0 )
+		    disp = 1;
+		do
+		    {
+		    if ( (i -= disp) < 0 )
+			i += hsize_reg;
+
+		    if ( htab[i] == fcode )
+			{
+			ent = codetab[i];
+			continue outer_loop;
+			}
+		    }
+		while ( htab[i] >= 0 );
+		}
+	    output( ent, outs );
+	    ent = c;
+	    if ( free_ent < maxmaxcode )
+		{
+		codetab[i] = free_ent++;	// code -> hashtable
+		htab[i] = fcode;
+		}
+	    else
+		cl_block( outs );
+	    }
+	// Put out the final code.
+	output( ent, outs );
+	output( EOFCode, outs );
+	}
+
+    // output
+    //
+    // Output the given code.
+    // Inputs:
+    //      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
+    //              that n_bits =< wordsize - 1.
+    // Outputs:
+    //      Outputs code to the file.
+    // Assumptions:
+    //      Chars are 8 bits long.
+    // Algorithm:
+    //      Maintain a BITS character long buffer (so that 8 codes will
+    // fit in it exactly).  Use the VAX insv instruction to insert each
+    // code in turn.  When the buffer fills up empty it and start over.
+
+    int cur_accum = 0;
+    int cur_bits = 0;
+
+    int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
+		    0x001F, 0x003F, 0x007F, 0x00FF,
+		    0x01FF, 0x03FF, 0x07FF, 0x0FFF,
+		    0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
+
+    void output( int code, OutputStream outs ) throws IOException
+	{
+	cur_accum &= masks[cur_bits];
+
+	if ( cur_bits > 0 )
+	    cur_accum |= ( code << cur_bits );
+	else
+	    cur_accum = code;
+
+	cur_bits += n_bits;
+
+	while ( cur_bits >= 8 )
+	    {
+	    char_out( (byte) ( cur_accum & 0xff ), outs );
+	    cur_accum >>= 8;
+	    cur_bits -= 8;
+	    }
+
+	// If the next entry is going to be too big for the code size,
+	// then increase it, if possible.
+       if ( free_ent > maxcode || clear_flg )
+	    {
+	    if ( clear_flg )
+		{
+		maxcode = MAXCODE(n_bits = g_init_bits);
+		clear_flg = false;
+		}
+	    else
+		{
+		++n_bits;
+		if ( n_bits == maxbits )
+		    maxcode = maxmaxcode;
+		else
+		    maxcode = MAXCODE(n_bits);
+		}
+	    }
+
+	if ( code == EOFCode )
+	    {
+	    // At EOF, write the rest of the buffer.
+	    while ( cur_bits > 0 )
+		{
+		char_out( (byte) ( cur_accum & 0xff ), outs );
+		cur_accum >>= 8;
+		cur_bits -= 8;
+		}
+
+	    flush_char( outs );
+	    }
+	}
+
+    // Clear out the hash table
+
+    // table clear for block compress
+    void cl_block( OutputStream outs ) throws IOException
+	{
+	cl_hash( hsize );
+	free_ent = ClearCode + 2;
+	clear_flg = true;
+
+	output( ClearCode, outs );
+	}
+
+    // reset code table
+    void cl_hash( int hsize )
+	{
+	for ( int i = 0; i < hsize; ++i )
+	    htab[i] = -1;
+	}
+
+    // GIF Specific routines
+
+    // Number of characters so far in this 'packet'
+    int a_count;
+
+    // Set up the 'byte output' routine
+    void char_init()
+	{
+	a_count = 0;
+	}
+
+    // Define the storage for the packet accumulator
+    byte[] accum = new byte[256];
+
+    // Add a character to the end of the current packet, and if it is 254
+    // characters, flush the packet to disk.
+    void char_out( byte c, OutputStream outs ) throws IOException
+	{
+	accum[a_count++] = c;
+	if ( a_count >= 254 )
+	    flush_char( outs );
+	}
+
+    // Flush the packet to disk, and reset the accumulator
+    void flush_char( OutputStream outs ) throws IOException
+	{
+	if ( a_count > 0 )
+	    {
+	    outs.write( a_count );
+	    outs.write( accum, 0, a_count );
+	    a_count = 0;
+	    }
+	}
+
+    }
+
+class GifEncoderHashitem
+    {
+
+    public int rgb;
+    public int count;
+    public int index;
+    public boolean isTransparent;
+
+    public GifEncoderHashitem( int rgb, int count, int index, boolean isTransparent )
+	{
+	this.rgb = rgb;
+	this.count = count;
+	this.index = index;
+	this.isTransparent = isTransparent;
+	}
+
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/packages/Acme/JPM/Encoders/ImageEncoder.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,272 @@
+// ImageEncoder - abstract class for writing out an image
+// http://www.acme.com/java/software/Acme.JPM.Encoders.ImageEncoder.html
+//
+// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. 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.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+//
+// Visit the ACME Labs Java page for up-to-date versions of this and other
+// fine Java utilities: http://www.acme.com/java/
+
+package Acme.JPM.Encoders;
+
+import java.util.*;
+import java.io.*;
+import java.awt.Image;
+import java.awt.image.*;
+
+/// Abstract class for writing out an image.
+// <P>
+// A framework for classes that encode and write out an image in
+// a particular file format.
+// <P>
+// This provides a simplified rendition of the ImageConsumer interface.
+// It always delivers the pixels as ints in the RGBdefault color model.
+// It always provides them in top-down left-right order.
+// If you want more flexibility you can always implement ImageConsumer
+// directly.
+// <P>
+// <A HREF="/resources/classes/Acme/JPM/Encoders/ImageEncoder.java">Fetch the software.</A><BR>
+// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
+// <P>
+// @see GifEncoder
+// @see PpmEncoder
+// @see Acme.JPM.Decoders.ImageDecoder
+
+public abstract class ImageEncoder implements ImageConsumer
+    {
+
+    protected OutputStream out;
+
+    private ImageProducer producer;
+    private int width = -1;
+    private int height = -1;
+    private int hintflags = 0;
+    private boolean started = false;
+    private boolean encoding;
+    private IOException iox;
+    private static final ColorModel rgbModel = ColorModel.getRGBdefault();
+    private Hashtable props = null;
+
+    /// Constructor.
+    // @param img The image to encode.
+    // @param out The stream to write the bytes to.
+    public ImageEncoder( Image img, OutputStream out ) throws IOException
+	{
+	this( img.getSource(), out );
+	}
+
+    /// Constructor.
+    // @param producer The ImageProducer to encode.
+    // @param out The stream to write the bytes to.
+    public ImageEncoder( ImageProducer producer, OutputStream out ) throws IOException
+	{
+	this.producer = producer;
+	this.out = out;
+	}
+
+
+    // Methods that subclasses implement.
+
+    /// Subclasses implement this to initialize an encoding.
+    abstract void encodeStart( int w, int h ) throws IOException;
+
+    /// Subclasses implement this to actually write out some bits.  They
+    // are guaranteed to be delivered in top-down-left-right order.
+    // One int per pixel, index is row * scansize + off + col,
+    // RGBdefault (AARRGGBB) color model.
+    abstract void encodePixels(
+	int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
+	throws IOException;
+
+    /// Subclasses implement this to finish an encoding.
+    abstract void encodeDone() throws IOException;
+
+
+    // Our own methods.
+
+    /// Call this after initialization to get things going.
+    public synchronized void encode() throws IOException
+	{
+	encoding = true;
+	iox = null;
+	producer.startProduction( this );
+	while ( encoding )
+	    try
+		{
+		wait();
+		}
+	    catch ( InterruptedException e ) {}
+	if ( iox != null )
+	    throw iox;
+	}
+
+    private boolean accumulate = false;
+    private int[] accumulator;
+
+    private void encodePixelsWrapper(
+	int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
+	throws IOException
+	{
+	if ( ! started )
+	    {
+	    started = true;
+	    encodeStart( width, height );
+	    if ( ( hintflags & TOPDOWNLEFTRIGHT ) == 0 )
+		{
+		accumulate = true;
+		accumulator = new int[width * height];
+		}
+	    }
+	if ( accumulate )
+	    for ( int row = 0; row < h; ++row )
+		System.arraycopy(
+		    rgbPixels, row * scansize + off,
+		    accumulator, ( y + row ) * width + x,
+		    w );
+	else
+	    encodePixels( x, y, w, h, rgbPixels, off, scansize );
+	}
+
+    private void encodeFinish() throws IOException
+	{
+	if ( accumulate )
+	    {
+	    encodePixels( 0, 0, width, height, accumulator, 0, width );
+	    accumulator = null;
+	    accumulate = false;
+	    }
+	}
+
+    private synchronized void stop()
+	{
+	encoding = false;
+	notifyAll();
+	}
+
+
+    // Methods from ImageConsumer.
+
+    public void setDimensions( int width, int height )
+	{
+	this.width = width;
+	this.height = height;
+	}
+
+    public void setProperties( Hashtable props )
+	{
+	this.props = props;
+	}
+
+    public void setColorModel( ColorModel model )
+	{
+	// Ignore.
+	}
+
+    public void setHints( int hintflags )
+	{
+	this.hintflags = hintflags;
+	}
+
+    public void setPixels(
+	int x, int y, int w, int h, ColorModel model, byte[] pixels,
+	int off, int scansize )
+	{
+	int[] rgbPixels = new int[w];
+	for ( int row = 0; row < h; ++row )
+	    {
+	    int rowOff = off + row * scansize;
+	    for ( int col = 0; col < w; ++col )
+		rgbPixels[col] = model.getRGB( pixels[rowOff + col] & 0xff );
+	    try
+		{
+		encodePixelsWrapper( x, y + row, w, 1, rgbPixels, 0, w );
+		}
+	    catch ( IOException e )
+		{
+		iox = e;
+		stop();
+		return;
+		}
+	    }
+	}
+
+    public void setPixels(
+	int x, int y, int w, int h, ColorModel model, int[] pixels,
+	int off, int scansize )
+	{
+	if ( model == rgbModel )
+	    {
+	    try
+		{
+		encodePixelsWrapper( x, y, w, h, pixels, off, scansize );
+		}
+	    catch ( IOException e )
+		{
+		iox = e;
+		stop();
+		return;
+		}
+	    }
+	else
+	    {
+	    int[] rgbPixels = new int[w];
+            for ( int row = 0; row < h; ++row )
+		{
+		int rowOff = off + row * scansize;
+                for ( int col = 0; col < w; ++col )
+                    rgbPixels[col] = model.getRGB( pixels[rowOff + col] );
+		try
+		    {
+		    encodePixelsWrapper( x, y + row, w, 1, rgbPixels, 0, w );
+		    }
+		catch ( IOException e )
+		    {
+		    iox = e;
+		    stop();
+		    return;
+		    }
+		}
+	    }
+	}
+
+    public void imageComplete( int status )
+	{
+	producer.removeConsumer( this );
+	if ( status == ImageConsumer.IMAGEABORTED )
+	    iox = new IOException( "image aborted" );
+	else
+	    {
+	    try
+		{
+		encodeFinish();
+		encodeDone();
+		}
+	    catch ( IOException e )
+		{
+		iox = e;
+		}
+	    }
+	stop();
+	}
+
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/packages/com/keypoint/PngEncoder.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,600 @@
+package com.keypoint;
+
+import java.awt.Image;
+import java.awt.image.ImageObserver;
+import java.awt.image.PixelGrabber;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+/**
+ * PngEncoder takes a Java Image object and creates a byte string which can be saved as a PNG file.
+ * The Image is presumed to use the DirectColorModel.
+ *
+ * <p>Thanks to Jay Denny at KeyPoint Software
+ *    http://www.keypoint.com/
+ * who let me develop this code on company time.</p>
+ *
+ * <p>You may contact me with (probably very-much-needed) improvements,
+ * comments, and bug fixes at:</p>
+ *
+ *   <p><code>david@catcode.com</code></p>
+ *
+ * <p>This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.</p>
+ *
+ * <p>This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.</p>
+ *
+ * <p>You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * A copy of the GNU LGPL may be found at
+ * <code>http://www.gnu.org/copyleft/lesser.html</code></p>
+ *
+ * @author J. David Eisenberg
+ * @version 1.5, 19 Oct 2003
+ *
+ * CHANGES:
+ * --------
+ * 19-Nov-2002 : CODING STYLE CHANGES ONLY (by David Gilbert for Object Refinery Limited);
+ * 19-Sep-2003 : Fix for platforms using EBCDIC (contributed by Paulo Soares);
+ * 19-Oct-2003 : Change private fields to protected fields so that
+ *               PngEncoderB can inherit them (JDE)
+ *				 Fixed bug with calculation of nRows
+ */
+
+public class PngEncoder extends Object {
+
+    /** Constant specifying that alpha channel should be encoded. */
+    public static final boolean ENCODE_ALPHA = true;
+
+    /** Constant specifying that alpha channel should not be encoded. */
+    public static final boolean NO_ALPHA = false;
+
+    /** Constants for filter (NONE) */
+    public static final int FILTER_NONE = 0;
+
+    /** Constants for filter (SUB) */
+    public static final int FILTER_SUB = 1;
+
+    /** Constants for filter (UP) */
+    public static final int FILTER_UP = 2;
+
+    /** Constants for filter (LAST) */
+    public static final int FILTER_LAST = 2;
+    
+    /** IHDR tag. */
+    protected static final byte IHDR[] = {73, 72, 68, 82};
+    
+    /** IDAT tag. */
+    protected static final byte IDAT[] = {73, 68, 65, 84};
+    
+    /** IEND tag. */
+    protected static final byte IEND[] = {73, 69, 78, 68};
+
+    /** The png bytes. */
+    protected byte[] pngBytes;
+
+    /** The prior row. */
+    protected byte[] priorRow;
+
+    /** The left bytes. */
+    protected byte[] leftBytes;
+
+    /** The image. */
+    protected Image image;
+
+    /** The width. */
+    protected int width, height;
+
+    /** The byte position. */
+    protected int bytePos, maxPos;
+
+    /** CRC. */
+    protected CRC32 crc = new CRC32();
+
+    /** The CRC value. */
+    protected long crcValue;
+
+    /** Encode alpha? */
+    protected boolean encodeAlpha;
+
+    /** The filter type. */
+    protected int filter;
+
+    /** The bytes-per-pixel. */
+    protected int bytesPerPixel;
+
+    /** The compression level. */
+    protected int compressionLevel;
+
+    /**
+     * Class constructor
+     */
+    public PngEncoder() {
+        this(null, false, FILTER_NONE, 0);
+    }
+
+    /**
+     * Class constructor specifying Image to encode, with no alpha channel encoding.
+     *
+     * @param image A Java Image object which uses the DirectColorModel
+     * @see java.awt.Image
+     */
+    public PngEncoder(Image image) {
+        this(image, false, FILTER_NONE, 0);
+    }
+
+    /**
+     * Class constructor specifying Image to encode, and whether to encode alpha.
+     *
+     * @param image A Java Image object which uses the DirectColorModel
+     * @param encodeAlpha Encode the alpha channel? false=no; true=yes
+     * @see java.awt.Image
+     */
+    public PngEncoder(Image image, boolean encodeAlpha) {
+        this(image, encodeAlpha, FILTER_NONE, 0);
+    }
+
+    /**
+     * Class constructor specifying Image to encode, whether to encode alpha, and filter to use.
+     *
+     * @param image A Java Image object which uses the DirectColorModel
+     * @param encodeAlpha Encode the alpha channel? false=no; true=yes
+     * @param whichFilter 0=none, 1=sub, 2=up
+     * @see java.awt.Image
+     */
+    public PngEncoder(Image image, boolean encodeAlpha, int whichFilter) {
+        this(image, encodeAlpha, whichFilter, 0);
+    }
+
+
+    /**
+     * Class constructor specifying Image source to encode, whether to encode alpha, filter to use,
+     * and compression level.
+     *
+     * @param image A Java Image object
+     * @param encodeAlpha Encode the alpha channel? false=no; true=yes
+     * @param whichFilter 0=none, 1=sub, 2=up
+     * @param compLevel 0..9
+     * @see java.awt.Image
+     */
+    public PngEncoder(Image image, boolean encodeAlpha, int whichFilter, int compLevel) {
+        this.image = image;
+        this.encodeAlpha = encodeAlpha;
+        setFilter(whichFilter);
+        if (compLevel >= 0 && compLevel <= 9) {
+            this.compressionLevel = compLevel;
+        }
+    }
+
+    /**
+     * Set the image to be encoded
+     *
+     * @param image A Java Image object which uses the DirectColorModel
+     * @see java.awt.Image
+     * @see java.awt.image.DirectColorModel
+     */
+    public void setImage(Image image) {
+        this.image = image;
+        pngBytes = null;
+    }
+
+    /**
+     * Creates an array of bytes that is the PNG equivalent of the current image, specifying
+     * whether to encode alpha or not.
+     *
+     * @param encodeAlpha boolean false=no alpha, true=encode alpha
+     * @return an array of bytes, or null if there was a problem
+     */
+    public byte[] pngEncode(boolean encodeAlpha) {
+        byte[]  pngIdBytes = {-119, 80, 78, 71, 13, 10, 26, 10};
+
+        if (image == null) {
+            return null;
+        }
+        width = image.getWidth(null);
+        height = image.getHeight(null);
+
+        /*
+         * start with an array that is big enough to hold all the pixels
+         * (plus filter bytes), and an extra 200 bytes for header info
+         */
+        pngBytes = new byte[((width + 1) * height * 3) + 200];
+
+        /*
+         * keep track of largest byte written to the array
+         */
+        maxPos = 0;
+
+        bytePos = writeBytes(pngIdBytes, 0);
+        //hdrPos = bytePos;
+        writeHeader();
+        //dataPos = bytePos;
+        if (writeImageData()) {
+            writeEnd();
+            pngBytes = resizeByteArray(pngBytes, maxPos);
+        }
+        else {
+            pngBytes = null;
+        }
+        return pngBytes;
+    }
+
+    /**
+     * Creates an array of bytes that is the PNG equivalent of the current image.
+     * Alpha encoding is determined by its setting in the constructor.
+     *
+     * @return an array of bytes, or null if there was a problem
+     */
+    public byte[] pngEncode() {
+        return pngEncode(encodeAlpha);
+    }
+
+    /**
+     * Set the alpha encoding on or off.
+     *
+     * @param encodeAlpha  false=no, true=yes
+     */
+    public void setEncodeAlpha(boolean encodeAlpha) {
+        this.encodeAlpha = encodeAlpha;
+    }
+
+    /**
+     * Retrieve alpha encoding status.
+     *
+     * @return boolean false=no, true=yes
+     */
+    public boolean getEncodeAlpha() {
+        return encodeAlpha;
+    }
+
+    /**
+     * Set the filter to use
+     *
+     * @param whichFilter from constant list
+     */
+    public void setFilter(int whichFilter) {
+        this.filter = FILTER_NONE;
+        if (whichFilter <= FILTER_LAST) {
+            this.filter = whichFilter;
+        }
+    }
+
+    /**
+     * Retrieve filtering scheme
+     *
+     * @return int (see constant list)
+     */
+    public int getFilter() {
+        return filter;
+    }
+
+    /**
+     * Set the compression level to use
+     *
+     * @param level 0 through 9
+     */
+    public void setCompressionLevel(int level) {
+        if (level >= 0 && level <= 9) {
+            this.compressionLevel = level;
+        }
+    }
+
+    /**
+     * Retrieve compression level
+     *
+     * @return int in range 0-9
+     */
+    public int getCompressionLevel() {
+        return compressionLevel;
+    }
+
+    /**
+     * Increase or decrease the length of a byte array.
+     *
+     * @param array The original array.
+     * @param newLength The length you wish the new array to have.
+     * @return Array of newly desired length. If shorter than the
+     *         original, the trailing elements are truncated.
+     */
+    protected byte[] resizeByteArray(byte[] array, int newLength) {
+        byte[]  newArray = new byte[newLength];
+        int     oldLength = array.length;
+
+        System.arraycopy(array, 0, newArray, 0, Math.min(oldLength, newLength));
+        return newArray;
+    }
+
+    /**
+     * Write an array of bytes into the pngBytes array.
+     * Note: This routine has the side effect of updating
+     * maxPos, the largest element written in the array.
+     * The array is resized by 1000 bytes or the length
+     * of the data to be written, whichever is larger.
+     *
+     * @param data The data to be written into pngBytes.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeBytes(byte[] data, int offset) {
+        maxPos = Math.max(maxPos, offset + data.length);
+        if (data.length + offset > pngBytes.length) {
+            pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, data.length));
+        }
+        System.arraycopy(data, 0, pngBytes, offset, data.length);
+        return offset + data.length;
+    }
+
+    /**
+     * Write an array of bytes into the pngBytes array, specifying number of bytes to write.
+     * Note: This routine has the side effect of updating
+     * maxPos, the largest element written in the array.
+     * The array is resized by 1000 bytes or the length
+     * of the data to be written, whichever is larger.
+     *
+     * @param data The data to be written into pngBytes.
+     * @param nBytes The number of bytes to be written.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeBytes(byte[] data, int nBytes, int offset) {
+        maxPos = Math.max(maxPos, offset + nBytes);
+        if (nBytes + offset > pngBytes.length) {
+            pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, nBytes));
+        }
+        System.arraycopy(data, 0, pngBytes, offset, nBytes);
+        return offset + nBytes;
+    }
+
+    /**
+     * Write a two-byte integer into the pngBytes array at a given position.
+     *
+     * @param n The integer to be written into pngBytes.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeInt2(int n, int offset) {
+        byte[] temp = {(byte) ((n >> 8) & 0xff), (byte) (n & 0xff)};
+        return writeBytes(temp, offset);
+    }
+
+    /**
+     * Write a four-byte integer into the pngBytes array at a given position.
+     *
+     * @param n The integer to be written into pngBytes.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeInt4(int n, int offset) {
+        byte[] temp = {(byte) ((n >> 24) & 0xff),
+                       (byte) ((n >> 16) & 0xff),
+                       (byte) ((n >> 8) & 0xff),
+                       (byte) (n & 0xff)};
+        return writeBytes(temp, offset);
+    }
+
+    /**
+     * Write a single byte into the pngBytes array at a given position.
+     *
+     * @param b The integer to be written into pngBytes.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeByte(int b, int offset) {
+        byte[] temp = {(byte) b};
+        return writeBytes(temp, offset);
+    }
+
+    /**
+     * Write a PNG "IHDR" chunk into the pngBytes array.
+     */
+    protected void writeHeader() {
+        int startPos;
+
+        startPos = bytePos = writeInt4(13, bytePos);
+        bytePos = writeBytes(IHDR, bytePos);
+        width = image.getWidth(null);
+        height = image.getHeight(null);
+        bytePos = writeInt4(width, bytePos);
+        bytePos = writeInt4(height, bytePos);
+        bytePos = writeByte(8, bytePos); // bit depth
+        bytePos = writeByte((encodeAlpha) ? 6 : 2, bytePos); // direct model
+        bytePos = writeByte(0, bytePos); // compression method
+        bytePos = writeByte(0, bytePos); // filter method
+        bytePos = writeByte(0, bytePos); // no interlace
+        crc.reset();
+        crc.update(pngBytes, startPos, bytePos - startPos);
+        crcValue = crc.getValue();
+        bytePos = writeInt4((int) crcValue, bytePos);
+    }
+
+    /**
+     * Perform "sub" filtering on the given row.
+     * Uses temporary array leftBytes to store the original values
+     * of the previous pixels.  The array is 16 bytes long, which
+     * will easily hold two-byte samples plus two-byte alpha.
+     *
+     * @param pixels The array holding the scan lines being built
+     * @param startPos Starting position within pixels of bytes to be filtered.
+     * @param width Width of a scanline in pixels.
+     */
+    protected void filterSub(byte[] pixels, int startPos, int width) {
+        int i;
+        int offset = bytesPerPixel;
+        int actualStart = startPos + offset;
+        int nBytes = width * bytesPerPixel;
+        int leftInsert = offset;
+        int leftExtract = 0;
+
+        for (i = actualStart; i < startPos + nBytes; i++) {
+            leftBytes[leftInsert] =  pixels[i];
+            pixels[i] = (byte) ((pixels[i] - leftBytes[leftExtract]) % 256);
+            leftInsert = (leftInsert + 1) % 0x0f;
+            leftExtract = (leftExtract + 1) % 0x0f;
+        }
+    }
+
+    /**
+     * Perform "up" filtering on the given row.
+     * Side effect: refills the prior row with current row
+     *
+     * @param pixels The array holding the scan lines being built
+     * @param startPos Starting position within pixels of bytes to be filtered.
+     * @param width Width of a scanline in pixels.
+     */
+    protected void filterUp(byte[] pixels, int startPos, int width) {
+        int     i, nBytes;
+        byte    currentByte;
+
+        nBytes = width * bytesPerPixel;
+
+        for (i = 0; i < nBytes; i++) {
+            currentByte = pixels[startPos + i];
+            pixels[startPos + i] = (byte) ((pixels[startPos  + i] - priorRow[i]) % 256);
+            priorRow[i] = currentByte;
+        }
+    }
+
+    /**
+     * Write the image data into the pngBytes array.
+     * This will write one or more PNG "IDAT" chunks. In order
+     * to conserve memory, this method grabs as many rows as will
+     * fit into 32K bytes, or the whole image; whichever is less.
+     *
+     *
+     * @return true if no errors; false if error grabbing pixels
+     */
+    protected boolean writeImageData() {
+        int rowsLeft = height;  // number of rows remaining to write
+        int startRow = 0;       // starting row to process this time through
+        int nRows;              // how many rows to grab at a time
+
+        byte[] scanLines;       // the scan lines to be compressed
+        int scanPos;            // where we are in the scan lines
+        int startPos;           // where this line's actual pixels start (used for filtering)
+
+        byte[] compressedLines; // the resultant compressed lines
+        int nCompressed;        // how big is the compressed area?
+
+        //int depth;              // color depth ( handle only 8 or 32 )
+
+        PixelGrabber pg;
+
+        bytesPerPixel = (encodeAlpha) ? 4 : 3;
+
+        Deflater scrunch = new Deflater(compressionLevel);
+        ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);
+
+        DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes, scrunch);
+        try {
+            while (rowsLeft > 0) {
+                nRows = Math.min(32767 / (width * (bytesPerPixel + 1)), rowsLeft);
+                nRows = Math.max( nRows, 1 );
+
+                int[] pixels = new int[width * nRows];
+
+                pg = new PixelGrabber(image, 0, startRow,
+                    width, nRows, pixels, 0, width);
+                try {
+                    pg.grabPixels();
+                }
+                catch (Exception e) {
+                    System.err.println("interrupted waiting for pixels!");
+                    return false;
+                }
+                if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
+                    System.err.println("image fetch aborted or errored");
+                    return false;
+                }
+
+                /*
+                 * Create a data chunk. scanLines adds "nRows" for
+                 * the filter bytes.
+                 */
+                scanLines = new byte[width * nRows * bytesPerPixel +  nRows];
+
+                if (filter == FILTER_SUB) {
+                    leftBytes = new byte[16];
+                }
+                if (filter == FILTER_UP) {
+                    priorRow = new byte[width * bytesPerPixel];
+                }
+
+                scanPos = 0;
+                startPos = 1;
+                for (int i = 0; i < width * nRows; i++) {
+                    if (i % width == 0) {
+                        scanLines[scanPos++] = (byte) filter;
+                        startPos = scanPos;
+                    }
+                    scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff);
+                    scanLines[scanPos++] = (byte) ((pixels[i] >>  8) & 0xff);
+                    scanLines[scanPos++] = (byte) ((pixels[i]) & 0xff);
+                    if (encodeAlpha) {
+                        scanLines[scanPos++] = (byte) ((pixels[i] >> 24) & 0xff);
+                    }
+                    if ((i % width == width - 1) && (filter != FILTER_NONE)) {
+                        if (filter == FILTER_SUB) {
+                            filterSub(scanLines, startPos, width);
+                        }
+                        if (filter == FILTER_UP) {
+                            filterUp(scanLines, startPos, width);
+                        }
+                    }
+                }
+
+                /*
+                 * Write these lines to the output area
+                 */
+                compBytes.write(scanLines, 0, scanPos);
+
+                startRow += nRows;
+                rowsLeft -= nRows;
+            }
+            compBytes.close();
+
+            /*
+             * Write the compressed bytes
+             */
+            compressedLines = outBytes.toByteArray();
+            nCompressed = compressedLines.length;
+
+            crc.reset();
+            bytePos = writeInt4(nCompressed, bytePos);
+            bytePos = writeBytes(IDAT, bytePos);
+            crc.update(IDAT);
+            bytePos = writeBytes(compressedLines, nCompressed, bytePos);
+            crc.update(compressedLines, 0, nCompressed);
+
+            crcValue = crc.getValue();
+            bytePos = writeInt4((int) crcValue, bytePos);
+            scrunch.finish();
+            return true;
+        }
+        catch (IOException e) {
+            System.err.println(e.toString());
+            return false;
+        }
+    }
+
+    /**
+     * Write a PNG "IEND" chunk into the pngBytes array.
+     */
+    protected void writeEnd() {
+        bytePos = writeInt4(0, bytePos);
+        bytePos = writeBytes(IEND, bytePos);
+        crc.reset();
+        crc.update(IEND);
+        crcValue = crc.getValue();
+        bytePos = writeInt4((int) crcValue, bytePos);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/packages/com/keypoint/PngEncoderIndexed.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,522 @@
+package com.keypoint;
+
+import java.awt.Image;
+import java.awt.image.ImageObserver;
+import java.awt.image.PixelGrabber;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.*;
+import java.io.*;
+
+/**
+ * PngEncoder takes a Java Image object and creates a byte string which can be saved as a PNG file.
+ * The Image is presumed to use the DirectColorModel.
+ *
+ * <p>Thanks to Jay Denny at KeyPoint Software
+ *    http://www.keypoint.com/
+ * who let me develop this code on company time.</p>
+ *
+ * <p>You may contact me with (probably very-much-needed) improvements,
+ * comments, and bug fixes at:</p>
+ *
+ *   <p><code>david@catcode.com</code></p>
+ *
+ * <p>This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.</p>
+ *
+ * <p>This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.</p>
+ *
+ * <p>You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * A copy of the GNU LGPL may be found at
+ * <code>http://www.gnu.org/copyleft/lesser.html</code></p>
+ *
+ * @author J. David Eisenberg
+ * @version 1.5, 19 Oct 2003
+ *
+ * CHANGES:
+ * --------
+ * 19-Nov-2002 : CODING STYLE CHANGES ONLY (by David Gilbert for Object Refinery Limited);
+ * 19-Sep-2003 : Fix for platforms using EBCDIC (contributed by Paulo Soares);
+ * 19-Oct-2003 : Change private fields to protected fields so that
+ *               PngEncoderB can inherit them (JDE)
+ *				 Fixed bug with calculation of nRows
+ */
+
+public class PngEncoderIndexed extends Object {
+
+    /** IHDR tag. */
+    protected static final byte IHDR[] = {73, 72, 68, 82};
+    
+    /** PLTE tag. */
+    protected static final byte PLTE[] = {80, 76, 84, 69};
+
+    /** tRNS tag. */
+    protected static final byte tRNS[] = {116, 82, 78, 83};
+
+    /** IDAT tag. */
+    protected static final byte IDAT[] = {73, 68, 65, 84};
+    
+    /** IEND tag. */
+    protected static final byte IEND[] = {73, 69, 78, 68};
+
+	protected static final int DEFAULT_COMPRESSION = 5;
+
+    /** The png bytes. */
+    protected byte[] pngBytes;
+
+    /** The image. */
+    protected Image image;
+
+    /** The width. */
+    protected int width, height;
+
+    /** The byte position. */
+    protected int bytePos, maxPos;
+
+    /** CRC. */
+    protected CRC32 crc = new CRC32();
+
+    /** The CRC value. */
+    protected long crcValue;
+
+    /** The compression level. */
+    protected int compressionLevel;
+
+    /**
+     * Class constructor
+     */
+    public PngEncoderIndexed() {
+        this(null, DEFAULT_COMPRESSION);
+    }
+
+    /**
+     * Class constructor specifying Image to encode, with no alpha channel encoding.
+     *
+     * @param image A Java Image object which uses the DirectColorModel
+     * @see java.awt.Image
+     */
+    public PngEncoderIndexed(Image image) {
+        this(image, DEFAULT_COMPRESSION);
+    }
+
+    /**
+     * Class constructor specifying Image source to encode, 
+	 * and compression level.
+     *
+     * @param image A Java Image object
+     * @param compLevel 0..9
+     * @see java.awt.Image
+     */
+    public PngEncoderIndexed(Image image, int compLevel) {
+        this.image = image;
+        if (compLevel >= 0 && compLevel <= 9) {
+            this.compressionLevel = compLevel;
+        }
+    }
+
+    /**
+     * Set the image to be encoded
+     *
+     * @param image A Java Image object which uses the DirectColorModel
+     * @see java.awt.Image
+     * @see java.awt.image.DirectColorModel
+     */
+    public void setImage(Image image) {
+        this.image = image;
+        pngBytes = null;
+    }
+
+    /**
+     * Creates an array of bytes that is the PNG equivalent of the current image
+     *
+     * @return an array of bytes, or null if there was a problem
+     */
+    public byte[] pngEncode() {
+        byte[]  pngIdBytes = {-119, 80, 78, 71, 13, 10, 26, 10};
+
+        if (image == null) {
+            return null;
+        }
+        width = image.getWidth(null);
+        height = image.getHeight(null);
+
+        /*
+         * start with an array that is big enough to hold all the pixels
+         * (plus filter bytes), and an extra 200 bytes for header info
+         */
+        pngBytes = new byte[((width + 1) * height * 3) + 200];
+
+        /*
+         * keep track of largest byte written to the array
+         */
+        maxPos = 0;
+
+        bytePos = writeBytes(pngIdBytes, 0);
+        //hdrPos = bytePos;
+        writeHeader();
+        //dataPos = bytePos;
+        if (writeImageData()) {
+            writeEnd();
+            pngBytes = resizeByteArray(pngBytes, maxPos);
+        }
+        else {
+            pngBytes = null;
+        }
+        return pngBytes;
+    }
+
+    /**
+     * Set the compression level to use
+     *
+     * @param level 0 through 9
+     */
+    public void setCompressionLevel(int level) {
+        if (level >= 0 && level <= 9) {
+            this.compressionLevel = level;
+        }
+    }
+
+    /**
+     * Retrieve compression level
+     *
+     * @return int in range 0-9
+     */
+    public int getCompressionLevel() {
+        return compressionLevel;
+    }
+
+    /**
+     * Increase or decrease the length of a byte array.
+     *
+     * @param array The original array.
+     * @param newLength The length you wish the new array to have.
+     * @return Array of newly desired length. If shorter than the
+     *         original, the trailing elements are truncated.
+     */
+    protected byte[] resizeByteArray(byte[] array, int newLength) {
+        byte[]  newArray = new byte[newLength];
+        int     oldLength = array.length;
+
+        System.arraycopy(array, 0, newArray, 0, Math.min(oldLength, newLength));
+        return newArray;
+    }
+
+    /**
+     * Write an array of bytes into the pngBytes array.
+     * Note: This routine has the side effect of updating
+     * maxPos, the largest element written in the array.
+     * The array is resized by 1000 bytes or the length
+     * of the data to be written, whichever is larger.
+     *
+     * @param data The data to be written into pngBytes.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeBytes(byte[] data, int offset) {
+        maxPos = Math.max(maxPos, offset + data.length);
+        if (data.length + offset > pngBytes.length) {
+            pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, data.length));
+        }
+        System.arraycopy(data, 0, pngBytes, offset, data.length);
+        return offset + data.length;
+    }
+
+    /**
+     * Write an array of bytes into the pngBytes array, specifying number of bytes to write.
+     * Note: This routine has the side effect of updating
+     * maxPos, the largest element written in the array.
+     * The array is resized by 1000 bytes or the length
+     * of the data to be written, whichever is larger.
+     *
+     * @param data The data to be written into pngBytes.
+     * @param nBytes The number of bytes to be written.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeBytes(byte[] data, int nBytes, int offset) {
+        maxPos = Math.max(maxPos, offset + nBytes);
+        if (nBytes + offset > pngBytes.length) {
+            pngBytes = resizeByteArray(pngBytes, pngBytes.length + Math.max(1000, nBytes));
+        }
+        System.arraycopy(data, 0, pngBytes, offset, nBytes);
+        return offset + nBytes;
+    }
+
+    /**
+     * Write a two-byte integer into the pngBytes array at a given position.
+     *
+     * @param n The integer to be written into pngBytes.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeInt2(int n, int offset) {
+        byte[] temp = {(byte) ((n >> 8) & 0xff), (byte) (n & 0xff)};
+        return writeBytes(temp, offset);
+    }
+
+    /**
+     * Write a four-byte integer into the pngBytes array at a given position.
+     *
+     * @param n The integer to be written into pngBytes.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeInt4(int n, int offset) {
+        byte[] temp = {(byte) ((n >> 24) & 0xff),
+                       (byte) ((n >> 16) & 0xff),
+                       (byte) ((n >> 8) & 0xff),
+                       (byte) (n & 0xff)};
+        return writeBytes(temp, offset);
+    }
+
+    /**
+     * Write a single byte into the pngBytes array at a given position.
+     *
+     * @param b The integer to be written into pngBytes.
+     * @param offset The starting point to write to.
+     * @return The next place to be written to in the pngBytes array.
+     */
+    protected int writeByte(int b, int offset) {
+        byte[] temp = {(byte) b};
+        return writeBytes(temp, offset);
+    }
+
+    /**
+     * Write a PNG "IHDR" chunk into the pngBytes array.
+     */
+    protected void writeHeader() {
+        int startPos;
+
+        startPos = bytePos = writeInt4(13, bytePos);
+        bytePos = writeBytes(IHDR, bytePos);
+        width = image.getWidth(null);
+        height = image.getHeight(null);
+        bytePos = writeInt4(width, bytePos);
+        bytePos = writeInt4(height, bytePos);
+        bytePos = writeByte(8, bytePos); // bit depth
+        bytePos = writeByte(3, bytePos); // palette
+        bytePos = writeByte(0, bytePos); // compression method
+        bytePos = writeByte(0, bytePos); // filter method
+        bytePos = writeByte(0, bytePos); // no interlace
+        crc.reset();
+        crc.update(pngBytes, startPos, bytePos - startPos);
+        crcValue = crc.getValue();
+        bytePos = writeInt4((int) crcValue, bytePos);
+    }
+
+    /**
+     * Write the image data into the pngBytes array.
+     * This will write one or more PNG "IDAT" chunks. In order
+     * to conserve memory, this method grabs as many rows as will
+     * fit into 32K bytes, or the whole image; whichever is less.
+     *
+     *
+     * @return true if no errors; false if error grabbing pixels
+     */
+    protected boolean writeImageData() {
+        int rowsLeft;  // number of rows remaining to write
+        int startRow;       // starting row to process this time through
+        int nRows;              // how many rows to grab at a time
+
+        byte[] scanLines;       // the scan lines to be compressed
+        int scanPos;            // where we are in the scan lines
+        int startPos;           // where this line's actual pixels start (used for filtering)
+
+        byte[] compressedLines; // the resultant compressed lines
+        int nCompressed;        // how big is the compressed area?
+
+        //int depth;              // color depth ( handle only 8 or 32 )
+
+		// palette without transparency (TWikiDrawPlugin does not need it)
+		Acme.IntHashtable palette = new Acme.IntHashtable();
+		PngEncoderHashitem item;
+		int paletteIndex = 0, transIndex = -1, transRGBA = 0;
+
+        PixelGrabber pg;
+
+        Deflater scrunch = new Deflater(compressionLevel);
+        ByteArrayOutputStream outBytes = new ByteArrayOutputStream(1024);
+
+        DeflaterOutputStream compBytes = new DeflaterOutputStream(outBytes, scrunch);
+        try {
+			/*
+			 * create palette for image
+			 */
+			rowsLeft = height;
+			startRow = 0;
+            while (rowsLeft > 0) {
+                nRows = Math.min(64000 / (width * 4), rowsLeft);
+                nRows = Math.max(nRows, 1);
+
+                int[] pixels = new int[width * nRows];
+
+                pg = new PixelGrabber(image, 0, startRow, width, nRows,
+									  pixels, 0, width);
+                try {
+                    pg.grabPixels();
+                }
+                catch (Exception e) {
+                    System.err.println("interrupted waiting for pixels!");
+                    return false;
+                }
+                if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
+                    System.err.println("image fetch aborted or errored");
+                    return false;
+                }
+
+                scanPos = 0;
+                startPos = 1;
+                for (int i = 0; i < width * nRows; i++) {
+					int rgba = pixels[i];
+					item = (PngEncoderHashitem) palette.get( rgba );
+					if (item == null) {
+						if (paletteIndex >= 256)
+							throw new IOException("too many colors for a PNG");
+						item = new PngEncoderHashitem(rgba, 1, paletteIndex, false);
+						palette.put( rgba, item );
+						++paletteIndex;
+					} else
+						++item.count;
+                }
+
+                startRow += nRows;
+                rowsLeft -= nRows;
+            }
+
+			/*
+			 * write PLTE chunk
+			 */
+            crc.reset();
+            bytePos = writeInt4(paletteIndex * 3, bytePos); // length
+            bytePos = writeBytes(PLTE, bytePos);            // magic
+            crc.update(PLTE);
+			// write palette data
+			byte[] pltedata = new byte[paletteIndex * 3];
+			for (Enumeration e = palette.elements(); e.hasMoreElements();) {
+				item = (PngEncoderHashitem) e.nextElement();
+				pltedata[item.index*3]=(byte) ((item.rgba>>16)&0xff);
+				pltedata[item.index*3+1]=(byte) ((item.rgba>> 8)&0xff);
+				pltedata[item.index*3+2]=(byte) ( item.rgba     &0xff);
+			}
+			bytePos = writeBytes(pltedata, bytePos);
+			crc.update(pltedata);
+			// add crc
+            crcValue = crc.getValue();
+            bytePos = writeInt4((int) crcValue, bytePos);
+			
+			/*
+			 * create scanline with palette indices
+			 */
+			rowsLeft = height;
+			startRow = 0;
+            while (rowsLeft > 0) {
+                nRows = Math.min(64000 / (width * 4), rowsLeft);
+                nRows = Math.max(nRows, 1);
+
+                int[] pixels = new int[width * nRows];
+
+                pg = new PixelGrabber(image, 0, startRow,
+                    width, nRows, pixels, 0, width);
+                try {
+                    pg.grabPixels();
+                }
+                catch (Exception e) {
+                    System.err.println("interrupted waiting for pixels!");
+                    return false;
+                }
+                if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
+                    System.err.println("image fetch aborted or errored");
+                    return false;
+                }
+
+                /*
+                 * Create a data chunk. scanLines adds "nRows" for
+                 * the filter bytes.
+                 */
+                scanLines = new byte[width * nRows + nRows];
+				
+                scanPos = 0;
+                startPos = 1;
+                for (int i = 0; i < width * nRows; i++) {
+                    if (i % width == 0) {
+                        scanLines[scanPos++] = (byte) 0; /* no filter */
+                        startPos = scanPos;
+                    }
+					item = (PngEncoderHashitem) palette.get( pixels[i] );
+					if (item == null)
+						throw new IOException( "color not found" );
+					scanLines[scanPos++] = (byte) item.index;
+                }
+				
+                /*
+                 * Write these lines to the output area
+                 */
+                compBytes.write(scanLines, 0, scanPos);
+				
+                startRow += nRows;
+                rowsLeft -= nRows;
+            }
+            compBytes.close();
+			
+            /*
+             * Write the compressed bytes
+             */
+            compressedLines = outBytes.toByteArray();
+            nCompressed = compressedLines.length;
+
+            crc.reset();
+            bytePos = writeInt4(nCompressed, bytePos);
+            bytePos = writeBytes(IDAT, bytePos);
+            crc.update(IDAT);
+            bytePos = writeBytes(compressedLines, nCompressed, bytePos);
+            crc.update(compressedLines, 0, nCompressed);
+
+            crcValue = crc.getValue();
+            bytePos = writeInt4((int) crcValue, bytePos);
+            scrunch.finish();
+            return true;
+        }
+        catch (IOException e) {
+            System.err.println(e.toString());
+            return false;
+        }
+    }
+
+    /**
+     * Write a PNG "IEND" chunk into the pngBytes array.
+     */
+    protected void writeEnd() {
+        bytePos = writeInt4(0, bytePos);
+        bytePos = writeBytes(IEND, bytePos);
+        crc.reset();
+        crc.update(IEND);
+        crcValue = crc.getValue();
+        bytePos = writeInt4((int) crcValue, bytePos);
+    }
+
+}
+
+class PngEncoderHashitem {
+	
+    public int rgba;
+    public int count;
+    public int index;
+    public boolean isTrans;
+	
+    public PngEncoderHashitem(int rgba, int count, int index, boolean isTrans) {
+		this.rgba = rgba;
+		this.count = count;
+		this.index = index;
+		this.isTrans = isTrans;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/appframe/Application.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,20 @@
+package CH.ifa.draw.appframe;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.io.IOException;
+
+/**
+ * Interface to controlling application, either an applet or a java
+ * application. Makes a DrawFrame independent of it's context.
+ */
+public interface Application {
+    /** Show status string, eg in applet area */
+    void showStatus(String s);
+    /** Get command-line or applet parameter */
+    String getParameter(String name);
+    /** Get URL relative to the codebase of the app */
+    URL getURL(String relURL) throws MalformedURLException;
+    /** Popup a URL in a new frame */
+    void popupFrame(URL url, String title);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/appframe/DrawFrame.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2001 Motorola - All rights reserved
+ *
+ * Portions Copyright 2000 by Peter Thoeny, Peter@Thoeny.com.
+ * It is hereby granted that this software can be used, copied, 
+ * modified, and distributed without fee provided that this 
+ * copyright notice appears in all copies.
+ * Portions Copyright (C) 2001 Motorola - All rights reserved
+ */
+
+package CH.ifa.draw.appframe;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.figures.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * Base class of drawing editor frames.
+ * Provides support for drawing editors such that they are created in
+ * a Frame, so that all the facilities of AWT (such as Menus) are
+ * available. Provides basically the same facilities as a DrawApplet
+ * and is designed to be overridden to add extra menus and buttons.
+ */
+public class DrawFrame extends Frame
+    implements DrawingEditor, PaletteListener {
+
+    transient private Drawing         fDrawing;
+    transient private Tool            fTool;
+
+    transient private StandardDrawingView fView;
+    transient private ToolButton      fDefaultToolButton;
+    transient private ToolButton      fSelectedToolButton;
+
+    transient private boolean         fSimpleUpdate;
+    transient private Button          fUpdateButton;
+
+    transient private Panel	      fPanel;
+
+    private Iconkit                   fIconkit;
+
+    static String                     fgUntitled = "untitled";
+
+    private static final String       fgDrawPath = "/CH/ifa/draw/";
+    public static final String        IMAGES = fgDrawPath+"images/";
+
+    private Application       fApplication;
+
+    public DrawFrame(String title, Application application) {
+	super(title);
+
+	fApplication = application;
+
+        fIconkit = new Iconkit(this);
+
+	setLayout(new BorderLayout());
+
+        fView = createDrawingView();
+
+	MenuBar mb = new MenuBar();
+        populateMenuBar(mb);
+	setMenuBar(mb);
+
+	Panel panel = createSouthPanel();
+	if (panel != null) {
+	    populateSouthPanel(panel);
+	    add("South", panel);
+	}
+
+	panel = createWestPanel();
+	if (panel != null) {
+	    populateWestPanel(panel);
+	    add("West", panel);
+	}
+
+	panel = createEastPanel();
+	if (panel != null) {
+	    populateEastPanel(panel);
+	    add("East", panel);
+	}
+
+	panel = createNorthPanel();
+	if (panel != null) {
+	    populateNorthPanel(panel);
+	    add("North", panel);
+	}
+
+	ScrollPane sp = new ScrollPane();
+	sp.add(fView);
+	add("Center", sp);
+
+        initDrawing();
+        setBufferedDisplayUpdate();
+        //setupAttributes();
+    }
+
+    public Application getApplication() {
+	return fApplication;
+    }
+
+    public void showStatus(String s) {
+	fApplication.showStatus(s);
+    }
+
+    protected void populateMenuBar(MenuBar mb) {
+    }
+
+    /**
+     * Creates the buttons panel.
+     */
+    protected Panel createSouthPanel() {
+        Panel panel = new Panel();
+        panel.setLayout(new PaletteLayout(2, new Point(2,2), false));
+        return panel;
+    }
+
+    /**
+     * Creates the buttons shown in the buttons panel. Override to
+     * add additional/alternative buttons.
+     * @param panel the buttons panel.
+     */
+    protected void populateSouthPanel(Panel panel) {
+        panel.add(new Filler(24,20));
+
+        Choice drawingChoice = new Choice();
+        drawingChoice.addItem(fgUntitled);
+
+	String param = fApplication.getParameter("DRAWINGS");
+	if (param == null)
+	    param = "";
+       	StringTokenizer st = new StringTokenizer(param);
+        while (st.hasMoreTokens())
+            drawingChoice.addItem(st.nextToken());
+        // offer choice only if more than one
+        if (drawingChoice.getItemCount() > 1)
+            panel.add(drawingChoice);
+        else
+            panel.add(new Label(fgUntitled));
+
+	drawingChoice.addItemListener(
+	    new ItemListener() {
+		    public void itemStateChanged(ItemEvent e) {
+			if (e.getStateChange() == ItemEvent.SELECTED) {
+			    loadDrawing((String)e.getItem());
+			}
+		    }
+		}
+	    );
+
+        panel.add(new Filler(6,20));
+
+        Button button;
+        button = new CommandButton(new DeleteCommand("Delete", fView));
+        panel.add(button);
+
+        button = new CommandButton(new DuplicateCommand("Duplicate", fView));
+        panel.add(button);
+
+        button = new CommandButton(new GroupCommand("Group", fView));
+        panel.add(button);
+
+        button = new CommandButton(new UngroupCommand("Ungroup", fView));
+        panel.add(button);
+
+        button = new Button("Help");
+	button.addActionListener(
+	    new ActionListener() {
+		    public void actionPerformed(ActionEvent event) {
+			showHelp();
+		    }
+		}
+	    );
+        panel.add(button);
+
+	fUpdateButton = new Button("Simple Update");
+	fUpdateButton.addActionListener(
+	    new ActionListener() {
+		    public void actionPerformed(ActionEvent event) {
+			if (fSimpleUpdate)
+			    setBufferedDisplayUpdate();
+			else
+			    setSimpleDisplayUpdate();
+		    }
+		}
+	    );
+	
+	panel.add(fUpdateButton);
+    }
+
+    /**
+     * Creates the color choice for the given attribute.
+     */
+    protected CommandMenu createColorMenu(String attribute) {
+        CommandMenu menu = new CommandMenu("Colour");
+	ColorMap map = ColorMap.getColorMap();
+        for (int i = 0; i < map.size(); i++)
+            menu.add(
+                new ChangeAttributeCommand(
+                    map.name(i), attribute,
+                    map.color(i), fView));
+        return menu;
+    }
+
+    /**
+     * Creates the font choice. The choice is filled with
+     * all the fonts supported by the toolkit.
+     */
+    protected CommandMenu createFontMenu() {
+        CommandMenu menu = new CommandMenu("Font");
+
+	/** If we were able to assume 1.2 or greater, we would get
+	 * the fonts list like this:
+        String[] font =
+	    GraphicsEnvironment.getLocalGraphicsEnvironment().
+	    getAvailableFontFamilyNames();
+	*/
+	
+        String fonts[] = Toolkit.getDefaultToolkit().getFontList();
+
+        for (int i = 0; i < fonts.length; i++)
+            menu.add(
+		new ChangeAttributeCommand(
+		    fonts[i], "FontName", fonts[i],  fView));
+        return menu;
+    }
+
+    protected Panel createEastPanel() {
+	return null;
+    }
+
+    protected void populateEastPanel(Panel panel) {
+    }
+
+    protected Panel createNorthPanel() {
+	return null;
+    }
+
+    protected void populateNorthPanel(Panel panel) {
+    }
+
+    /**
+     * Creates the tools palette.
+     */
+    protected Panel createWestPanel() {
+        Panel palette = new Panel();
+        palette.setLayout(new PaletteLayout(2,new Point(2,2)));
+        return palette;
+    }
+
+    /**
+     * Creates the tools. By default only the selection tool is added.
+     * Override this method to add additional tools.
+     * Call the inherited method to include the selection tool.
+     * @param palette the palette where the tools are added.
+     */
+    protected void populateWestPanel(Panel palette) {
+        Tool tool = createSelectionTool();
+
+        fDefaultToolButton = createToolButton(
+	    IMAGES+"SEL", "Selection Tool", tool);
+        palette.add(fDefaultToolButton);
+    }
+
+    /**
+     * Creates the selection tool used in this editor. Override to use
+     * a custom selection tool.
+     */
+    protected Tool createSelectionTool() {
+        return new SelectionTool(fView);
+    }
+
+    /**
+     * Creates a tool button with the given image, tool, and text
+     */
+    protected ToolButton createToolButton(String iconName,
+					  String toolName, Tool tool) {
+        return new ToolButton(this, iconName, toolName, tool);
+    }
+
+    /**
+     * Creates the drawing used in this application.
+     * You need to override this method to use a Drawing
+     * subclass in your application. By default a standard
+     * Drawing is returned.
+     */
+    protected Drawing createDrawing() {
+        return new StandardDrawing();
+    }
+
+    /**
+     * Creates the drawing view used in this application.
+     * You need to override this method to use a DrawingView
+     * subclass in your application. By default a standard
+     * DrawingView is returned.
+     */
+    protected StandardDrawingView createDrawingView() {
+        return new StandardDrawingView(this, 410, 370);
+    }
+
+    /**
+     * Handles a user selection in the palette.
+     * @see PaletteListener
+     */
+    public void paletteUserSelected(PaletteButton button) {
+        ToolButton toolButton = (ToolButton) button;
+        setTool(toolButton.tool(), toolButton.name());
+        setSelected(toolButton);
+    }
+
+    /**
+     * Handles when the mouse enters or leaves a palette button.
+     * @see PaletteListener
+     */
+    public void paletteUserOver(PaletteButton button, boolean inside) {
+        if (inside)
+            showStatus(((ToolButton) button).name());
+        else
+            showStatus(fSelectedToolButton.name());
+    }
+
+    /**
+     * Gets the current drawing.
+     * @see DrawingEditor
+     */
+    public Drawing drawing() {
+        return fDrawing;
+    }
+
+    /**
+     * Gets the current tool.
+     * @see DrawingEditor
+     */
+    public Tool tool() {
+        return fTool;
+    }
+
+    /**
+     * Gets the current drawing view.
+     * @see DrawingEditor
+     */
+    public DrawingView view() {
+        return fView;
+    }
+
+    /**
+     * Sets the default tool of the editor.
+     * @see DrawingEditor
+     */
+    public void toolDone() {
+        setTool(fDefaultToolButton.tool(), fDefaultToolButton.name());
+        setSelected(fDefaultToolButton);
+    }
+
+    /**
+     * Handles a change of the current selection. Updates all
+     * menu items that are selection sensitive.
+     * @see DrawingEditor
+     */
+    public void selectionChanged(DrawingView view) {
+        //setupAttributes();
+    }
+
+    private void initDrawing() {
+        fDrawing = createDrawing();
+        fView.setDrawing(fDrawing);
+        toolDone();
+    }
+
+    private void setTool(Tool t, String name) {
+        if (fTool != null)
+            fTool.deactivate();
+        fTool = t;
+        if (fTool != null) {
+            showStatus(name);
+            fTool.activate();
+        }
+    }
+
+    private void setSelected(ToolButton button) {
+        if (fSelectedToolButton != null)
+            fSelectedToolButton.reset();
+        fSelectedToolButton = button;
+        if (fSelectedToolButton != null)
+            fSelectedToolButton.select();
+    }
+
+    protected void loadDrawing(String param) {
+        if (param == fgUntitled) {
+            fDrawing.release();
+            initDrawing();
+            return;
+        }
+
+        String filename = fApplication.getParameter(param);
+        if (filename != null) {
+	    readDrawing(filename);
+	}
+    }
+
+    private void readDrawing(String filename) {
+        toolDone();
+        String type = guessType(filename);
+        if (type.equals("storable"))
+            readFromStorableInput(filename);
+        else if (type.equals("serialized"))
+            readFromObjectInput(filename);
+        else
+            showStatus("Unknown file type");
+    }
+
+    private void readFromStorableInput(String filename) {
+        try {
+            URL url = fApplication.getURL(filename);
+            InputStream stream = url.openStream();
+            StorableInput input = new StorableInput(stream);
+            fDrawing.release();
+
+            fDrawing = (Drawing)input.readStorable();
+            fView.setDrawing(fDrawing);
+        } catch (IOException e) {
+            initDrawing();
+            showStatus("Error reading " + filename + ": "+e);
+	    e.printStackTrace();
+        }
+    }
+
+    private void readFromObjectInput(String filename) {
+        try {
+            URL url = fApplication.getURL(filename);
+            InputStream stream = url.openStream();
+            ObjectInput input = new ObjectInputStream(stream);
+            fDrawing.release();
+            fDrawing = (Drawing)input.readObject();
+            fView.setDrawing(fDrawing);
+        } catch (IOException e) {
+            initDrawing();
+            showStatus("Error reading (OI) " + filename + ": " + e);
+	    e.printStackTrace();
+        } catch (ClassNotFoundException e) {
+            initDrawing();
+            showStatus("Class not found: " + e);
+        }
+    }
+
+    private String guessType(String file) {
+        if (file.endsWith(".draw"))
+            return "storable";
+        if (file.endsWith(".ser"))
+            return "serialized";
+        return "unknown";
+    }
+
+    protected void setSimpleDisplayUpdate() {
+        fView.setDisplayUpdate(new SimpleUpdateStrategy());
+        fUpdateButton.setLabel("Simple Update");
+        fSimpleUpdate = true;
+    }
+
+    protected void setBufferedDisplayUpdate() {
+        fView.setDisplayUpdate(new BufferedUpdateStrategy());
+        fUpdateButton.setLabel("Buffered Update");
+        fSimpleUpdate = false;
+    }
+
+    /**
+     * Shows a help page for the application. The URL of the help
+     * page is derived as follows: codeBase+applicationClassname+Help.html"
+     */
+    protected void showHelp() {
+        try {
+            String helpPath = getClass().getName().replace('.', '/');
+            URL url = fApplication.getURL(helpPath + "Help.html");
+            fApplication.popupFrame(url, "Help");
+        } catch (IOException e) {
+            showStatus("Help file not found");
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/appframe/LightweightDrawApplet.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,60 @@
+// Copyright (C) 2001 Motorola - All rights reserved
+package CH.ifa.draw.appframe;
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.figures.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.applet.*;
+import CH.ifa.draw.contrib.*;
+
+public  class LightweightDrawApplet extends Applet implements Application {
+
+    private Frame frame;
+
+    public void	init() {
+        init(new DrawFrame("LightweightDrawApplet", this));
+    }
+
+    protected void init(Frame f) {
+	frame = f;
+	frame.pack();
+	frame.show();
+    }
+
+    public void stop() {
+	frame.hide();
+	frame.dispose();
+    }
+
+    public void start() {
+	frame.show();
+    }
+
+    /** Implement Application */
+    public void showStatus(String s) {
+	super.showStatus(s);
+    }
+
+    /** Implement Application */
+    public String getParameter(String name) {
+	return super.getParameter(name);
+    }
+
+    /** Implement Application */
+    public URL getURL(String relURL) throws MalformedURLException {
+	return new URL(getCodeBase(), relURL);
+    }
+
+    /** Implement Application */
+    public void popupFrame(URL url, String title) {
+	getAppletContext().showDocument(url, title);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/applet/DrawApplet.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,552 @@
+/*
+ * @(#)DrawApplet.java 5.1
+ *
+ */
+
+package CH.ifa.draw.applet;
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.figures.*;
+import CH.ifa.draw.util.*;
+
+
+/**
+ * DrawApplication defines a standard presentation for
+ * a drawing editor that is run as an applet. The presentation is
+ * customized in subclasses.<p>
+ * Supported applet parameters: <br>
+ * <i>DRAWINGS</i>: a blank separated list of drawing names that is
+ *           shown in the drawings choice.
+ */
+
+public class DrawApplet
+        extends Applet
+	    implements DrawingEditor, PaletteListener {
+
+    transient private Drawing         fDrawing;
+    transient private Tool            fTool;
+
+    transient private StandardDrawingView fView;
+    transient private ToolButton      fDefaultToolButton;
+    transient private ToolButton      fSelectedToolButton;
+
+    transient private boolean         fSimpleUpdate;
+    transient private Button          fUpdateButton;
+
+    transient private Choice          fFrameColor;
+    transient private Choice          fFillColor;
+    //transient private Choice          fTextColor;
+    transient private Choice          fArrowChoice;
+    transient private Choice          fFontChoice;
+
+    transient private Thread          fSleeper;
+    private Iconkit                   fIconkit;
+
+    static String                     fgUntitled = "untitled";
+
+    private static final String       fgDrawPath = "/CH/ifa/draw/";
+    public static final String        IMAGES = fgDrawPath+"images/";
+
+    /**
+     * Initializes the applet and creates its contents.
+     */
+    public void init() {
+        fIconkit = new Iconkit(this);
+
+		setLayout(new BorderLayout());
+
+        fView = createDrawingView();
+
+        Panel attributes = createAttributesPanel();
+        createAttributeChoices(attributes);
+        add("North", attributes);
+
+        Panel toolPanel = createToolPalette();
+        createTools(toolPanel);
+        add("West", toolPanel);
+
+        add("Center", fView);
+        Panel buttonPalette = createButtonPanel();
+        createButtons(buttonPalette);
+        add("South", buttonPalette);
+
+        initDrawing();
+        setBufferedDisplayUpdate();
+        setupAttributes();
+    }
+
+    /*
+     * Gets the iconkit to be used in the applet.
+     */
+
+     /**** not sure whether this will still be needed on 1.1 enabled browsers
+     protected Iconkit iconkit() {
+        if (fIconkit == null) {
+
+            startSleeper();
+            loadAllImages(this); // blocks until images loaded
+            stopSleeper();
+        }
+        return fIconkit;
+    } */
+
+    /**
+     * Creates the attributes panel.
+     */
+    protected Panel createAttributesPanel() {
+        Panel panel = new Panel();
+        panel.setLayout(new PaletteLayout(2, new Point(2,2), false));
+        return panel;
+    }
+
+    /**
+     * Creates the attribute choices. Override to add additional
+     * choices.
+     */
+    protected void createAttributeChoices(Panel panel) {
+        panel.add(new Label("Fill"));
+        fFillColor = createColorChoice("FillColor");
+        panel.add(fFillColor);
+
+        //panel.add(new Label("Text"));
+        //fTextColor = createColorChoice("TextColor");
+        //panel.add(fTextColor);
+
+        panel.add(new Label("Pen"));
+        fFrameColor = createColorChoice("FrameColor");
+        panel.add(fFrameColor);
+
+        panel.add(new Label("Arrow"));
+        CommandChoice choice = new CommandChoice();
+        fArrowChoice = choice;
+        choice.addItem(new ChangeAttributeCommand("none",     "ArrowMode", new Integer(PolyLineFigure.ARROW_TIP_NONE),  fView));
+        choice.addItem(new ChangeAttributeCommand("at Start", "ArrowMode", new Integer(PolyLineFigure.ARROW_TIP_START), fView));
+        choice.addItem(new ChangeAttributeCommand("at End",   "ArrowMode", new Integer(PolyLineFigure.ARROW_TIP_END),   fView));
+        choice.addItem(new ChangeAttributeCommand("at Both",  "ArrowMode", new Integer(PolyLineFigure.ARROW_TIP_BOTH),  fView));
+        panel.add(fArrowChoice);
+
+        panel.add(new Label("Font"));
+        fFontChoice = createFontChoice();
+        panel.add(fFontChoice);
+    }
+
+    /**
+     * Creates the color choice for the given attribute.
+     */
+    protected Choice createColorChoice(String attribute) {
+        CommandChoice choice = new CommandChoice();
+	ColorMap map = ColorMap.getColorMap();
+        for (int i=0; i < map.size(); i++)
+            choice.addItem(
+                new ChangeAttributeCommand(
+                    map.name(i), attribute,
+                    map.color(i),
+                    fView
+                )
+            );
+        return choice;
+    }
+
+    /**
+     * Creates the font choice. The choice is filled with
+     * all the fonts supported by the toolkit.
+     */
+    protected Choice createFontChoice() {
+        CommandChoice choice = new CommandChoice();
+        String fonts[] = Toolkit.getDefaultToolkit().getFontList();
+        for (int i = 0; i < fonts.length; i++)
+            choice.addItem(new ChangeAttributeCommand(fonts[i], "FontName", fonts[i],  fView));
+        return choice;
+    }
+
+    /**
+     * Creates the buttons panel.
+     */
+    protected Panel createButtonPanel() {
+        Panel panel = new Panel();
+        panel.setLayout(new PaletteLayout(2, new Point(2,2), false));
+        return panel;
+    }
+
+    /**
+     * Creates the buttons shown in the buttons panel. Override to
+     * add additional buttons.
+     * @param panel the buttons panel.
+     */
+    protected void createButtons(Panel panel) {
+        panel.add(new Filler(24,20));
+
+        Choice drawingChoice = new Choice();
+        drawingChoice.addItem(fgUntitled);
+
+	    String param = getParameter("DRAWINGS");
+	    if (param == null)
+	        param = "";
+       	StringTokenizer st = new StringTokenizer(param);
+        while (st.hasMoreTokens())
+            drawingChoice.addItem(st.nextToken());
+        // offer choice only if more than one
+        if (drawingChoice.getItemCount() > 1)
+            panel.add(drawingChoice);
+        else
+            panel.add(new Label(fgUntitled));
+
+		drawingChoice.addItemListener(
+		    new ItemListener() {
+		        public void itemStateChanged(ItemEvent e) {
+		            if (e.getStateChange() == ItemEvent.SELECTED) {
+		                loadDrawing((String)e.getItem());
+		            }
+		        }
+		    }
+		);
+
+        panel.add(new Filler(6,20));
+
+        Button button;
+        button = new CommandButton(new DeleteCommand("Delete", fView));
+        panel.add(button);
+
+        button = new CommandButton(new DuplicateCommand("Duplicate", fView));
+        panel.add(button);
+
+        button = new CommandButton(new GroupCommand("Group", fView));
+        panel.add(button);
+
+        button = new CommandButton(new UngroupCommand("Ungroup", fView));
+        panel.add(button);
+
+        button = new Button("Help");
+		button.addActionListener(
+		    new ActionListener() {
+		        public void actionPerformed(ActionEvent event) {
+		            showHelp();
+		        }
+		    }
+		);
+        panel.add(button);
+
+        fUpdateButton = new Button("Simple Update");
+		fUpdateButton.addActionListener(
+		    new ActionListener() {
+		        public void actionPerformed(ActionEvent event) {
+                    if (fSimpleUpdate)
+                        setBufferedDisplayUpdate();
+                    else
+                        setSimpleDisplayUpdate();
+		        }
+		    }
+		);
+
+        // panel.add(fUpdateButton); // not shown currently
+    }
+
+    /**
+     * Creates the tools palette.
+     */
+    protected Panel createToolPalette() {
+        Panel palette = new Panel();
+        palette.setLayout(new PaletteLayout(2,new Point(2,2)));
+        return palette;
+    }
+
+    /**
+     * Creates the tools. By default only the selection tool is added.
+     * Override this method to add additional tools.
+     * Call the inherited method to include the selection tool.
+     * @param palette the palette where the tools are added.
+     */
+    protected void createTools(Panel palette) {
+        Tool tool = createSelectionTool();
+
+        fDefaultToolButton = createToolButton(IMAGES+"SEL", "Selection Tool", tool);
+        palette.add(fDefaultToolButton);
+    }
+
+    /**
+     * Creates the selection tool used in this editor. Override to use
+     * a custom selection tool.
+     */
+    protected Tool createSelectionTool() {
+        return new SelectionTool(view());
+    }
+
+    /**
+     * Creates a tool button with the given image, tool, and text
+     */
+    protected ToolButton createToolButton(String iconName, String toolName, Tool tool) {
+        return new ToolButton(this, iconName, toolName, tool);
+    }
+
+    /**
+     * Creates the drawing used in this application.
+     * You need to override this method to use a Drawing
+     * subclass in your application. By default a standard
+     * Drawing is returned.
+     */
+    protected Drawing createDrawing() {
+        return new StandardDrawing();
+    }
+
+    /**
+     * Creates the drawing view used in this application.
+     * You need to override this method to use a DrawingView
+     * subclass in your application. By default a standard
+     * DrawingView is returned.
+     */
+    protected StandardDrawingView createDrawingView() {
+        return new StandardDrawingView(this, 410, 370);
+    }
+
+    /**
+     * Handles a user selection in the palette.
+     * @see PaletteListener
+     */
+    public void paletteUserSelected(PaletteButton button) {
+        ToolButton toolButton = (ToolButton) button;
+        setTool(toolButton.tool(), toolButton.name());
+        setSelected(toolButton);
+    }
+
+    /**
+     * Handles when the mouse enters or leaves a palette button.
+     * @see PaletteListener
+     */
+    public void paletteUserOver(PaletteButton button, boolean inside) {
+        if (inside)
+            showStatus(((ToolButton) button).name());
+        else
+            showStatus(fSelectedToolButton.name());
+    }
+
+    /**
+     * Gets the current drawing.
+     * @see DrawingEditor
+     */
+    public Drawing drawing() {
+        return fDrawing;
+    }
+
+    /**
+     * Gets the current tool.
+     * @see DrawingEditor
+     */
+    public Tool tool() {
+        return fTool;
+    }
+
+    /**
+     * Gets the current drawing view.
+     * @see DrawingEditor
+     */
+    public DrawingView view() {
+        return fView;
+    }
+
+    /**
+     * Sets the default tool of the editor.
+     * @see DrawingEditor
+     */
+    public void toolDone() {
+        setTool(fDefaultToolButton.tool(), fDefaultToolButton.name());
+        setSelected(fDefaultToolButton);
+    }
+
+    /**
+     * Handles a change of the current selection. Updates all
+     * menu items that are selection sensitive.
+     * @see DrawingEditor
+     */
+    public void selectionChanged(DrawingView view) {
+        setupAttributes();
+    }
+
+    private void initDrawing() {
+        fDrawing = createDrawing();
+        fView.setDrawing(fDrawing);
+        toolDone();
+    }
+
+    private void setTool(Tool t, String name) {
+        if (fTool != null)
+            fTool.deactivate();
+        fTool = t;
+        if (fTool != null) {
+            showStatus(name);
+            fTool.activate();
+        }
+    }
+
+    private void setSelected(ToolButton button) {
+        if (fSelectedToolButton != null)
+            fSelectedToolButton.reset();
+        fSelectedToolButton = button;
+        if (fSelectedToolButton != null)
+            fSelectedToolButton.select();
+    }
+
+    protected void loadDrawing(String param) {
+        if (param == fgUntitled) {
+            fDrawing.release();
+            initDrawing();
+            return;
+        }
+
+        String filename = getParameter(param);
+        if (filename != null)
+            readDrawing(filename);
+    }
+
+    private void readDrawing(String filename) {
+        toolDone();
+        String type = guessType(filename);
+        if (type.equals("storable"))
+            readFromStorableInput(filename);
+        else if (type.equals("serialized"))
+            readFromObjectInput(filename);
+        else
+            showStatus("Unknown file type");
+    }
+
+    private void readFromStorableInput(String filename) {
+        try {
+            URL url = new URL(getCodeBase(), filename);
+            InputStream stream = url.openStream();
+            StorableInput input = new StorableInput(stream);
+            fDrawing.release();
+
+            fDrawing = (Drawing)input.readStorable();
+            fView.setDrawing(fDrawing);
+        } catch (IOException e) {
+            initDrawing();
+            showStatus("Error:"+e);
+        }
+    }
+
+    private void readFromObjectInput(String filename) {
+        try {
+            URL url = new URL(getCodeBase(), filename);
+            InputStream stream = url.openStream();
+            ObjectInput input = new ObjectInputStream(stream);
+            fDrawing.release();
+            fDrawing = (Drawing)input.readObject();
+            fView.setDrawing(fDrawing);
+        } catch (IOException e) {
+            initDrawing();
+            showStatus("Error: " + e);
+        } catch (ClassNotFoundException e) {
+            initDrawing();
+            showStatus("Class not found: " + e);
+        }
+    }
+
+    private String guessType(String file) {
+        if (file.endsWith(".draw"))
+            return "storable";
+        if (file.endsWith(".ser"))
+            return "serialized";
+        return "unknown";
+    }
+
+    private void setupAttributes() {
+        Color frameColor =
+	    (Color)AttributeFigure.getDefaultAttribute("FrameColor");
+        Color fillColor =
+	    (Color)AttributeFigure.getDefaultAttribute("FillColor");
+        Color textColor =
+	    (Color)AttributeFigure.getDefaultAttribute("TextColor");
+        Integer arrowMode =
+	    (Integer)AttributeFigure.getDefaultAttribute("ArrowMode");
+        String fontName =
+	    (String)AttributeFigure.getDefaultAttribute("FontName");
+
+        FigureEnumeration k = fView.selectionElements();
+        while (k.hasMoreElements()) {
+            Figure f = k.nextFigure();
+            frameColor = (Color) f.getAttribute("FrameColor");
+            fillColor  = (Color) f.getAttribute("FillColor");
+            textColor  = (Color) f.getAttribute("TextColor");
+            arrowMode  = (Integer) f.getAttribute("ArrowMode");
+            fontName   = (String) f.getAttribute("FontName");
+        }
+
+        fFrameColor.select(ColorMap.getColorMap().colorIndex(frameColor));
+        fFillColor.select(ColorMap.getColorMap().colorIndex(fillColor));
+        //fTextColor.select(ColorMap.colorIndex(textColor));
+        if (arrowMode != null)
+            fArrowChoice.select(arrowMode.intValue());
+        if (fontName != null)
+            fFontChoice.select(fontName);
+    }
+
+    protected void setSimpleDisplayUpdate() {
+        fView.setDisplayUpdate(new SimpleUpdateStrategy());
+        fUpdateButton.setLabel("Simple Update");
+        fSimpleUpdate = true;
+    }
+
+    protected void setBufferedDisplayUpdate() {
+        fView.setDisplayUpdate(new BufferedUpdateStrategy());
+        fUpdateButton.setLabel("Buffered Update");
+        fSimpleUpdate = false;
+    }
+
+    /**
+     * Shows a help page for the applet. The URL of the help
+     * page is derived as follows: codeBase+appletClassname+Help.html"
+     */
+    protected void showHelp() {
+        try {
+            String appletPath = getClass().getName().replace('.', '/');
+            URL url = new URL(getCodeBase(), appletPath+"Help.html");
+            getAppletContext().showDocument(url, "Help");
+        } catch (IOException e) {
+            showStatus("Help file not found");
+        }
+
+    }
+
+    /**
+     * *** netscape browser work around ***
+     */
+    private void startSleeper() {
+        if (fSleeper == null)
+            fSleeper = new SleeperThread(this);
+        fSleeper.start();
+    }
+
+    private void stopSleeper() {
+        if (fSleeper != null)
+            fSleeper.stop();
+    }
+}
+
+
+class SleeperThread extends Thread {
+
+    Applet  fApplet;
+
+    SleeperThread(Applet applet) {
+        fApplet = applet;
+    }
+
+    public void run() {
+        try {
+            for (;;) {
+                fApplet.showStatus("loading icons...");
+                sleep(50);
+            }
+        } catch (InterruptedException e) {
+            return;
+        }
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/applet/TestApplet.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,64 @@
+/*
+ * @(#)TestApplet.java 5.1
+ *
+ */
+
+package CH.ifa.draw.applet;
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.figures.*;
+import CH.ifa.draw.util.*;
+
+//import javax.swing.*;
+
+public class TestApplet
+        extends Applet
+	    implements PaletteListener {
+
+    transient private ToolButton      fDefaultToolButton;
+
+    private Iconkit                   fIconkit;
+
+    private static final String       fgDrawPath = "/CH/ifa/draw/";
+    public static final String        IMAGES = fgDrawPath+"images/";
+
+    public void init() {
+        fIconkit = new Iconkit(this);
+/*
+	ImageIcon icon = new ImageIcon( IMAGES.substring(1)+"SEL1.gif");
+	JButton fred = new JButton(icon);
+	add(fred);
+*/
+        fDefaultToolButton = new ToolButton(this, IMAGES+"SEL",
+					    "Selection Tool",
+					    null);
+        add(fDefaultToolButton);
+    }
+
+    /**
+     * Handles a user selection in the palette.
+     * @see PaletteListener
+     */
+    public void paletteUserSelected(PaletteButton button) {
+    }
+
+    /**
+     * Handles when the mouse enters or leaves a palette button.
+     * @see PaletteListener
+     */
+    public void paletteUserOver(PaletteButton button, boolean inside) {
+        if (inside)
+            showStatus(((ToolButton) button).name());
+        else
+	    showStatus("Not over");
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/ChopPolygonConnector.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1996, 1997 Erich Gamma
+ * All Rights Reserved
+ */
+
+package CH.ifa.draw.contrib;
+
+import java.awt.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.Geom;
+
+/**
+ * A ChopPolygonConnector locates a connection point by
+ * chopping the connection at the polygon boundary.
+ */
+public class ChopPolygonConnector extends ChopBoxConnector {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = -156024908227796826L;
+
+    public ChopPolygonConnector() {
+    }
+
+    public ChopPolygonConnector(Figure owner) {
+        super(owner);
+    }
+
+    protected Point chop(Figure target, Point from) {
+        return ((PolygonFigure)target).chop(from);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/DiamondFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,62 @@
+/*
+ * Hacked together by Doug lea
+ * Tue Feb 25 17:39:44 1997  Doug Lea  (dl at gee)
+ *
+ */
+
+package CH.ifa.draw.contrib;
+
+import java.awt.*;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.figures.*;
+
+/**
+ * A diamond with vertices at the midpoints of its enclosing rectangle
+ */
+public  class DiamondFigure extends RectangleFigure {
+
+  public DiamondFigure() {
+    super(new Point(0,0), new Point(0,0));
+  }
+
+  public DiamondFigure(Point origin, Point corner) {
+    super(origin,corner);
+  }
+
+  /** Return the polygon describing the diamond **/
+  protected Polygon polygon() {
+    Rectangle r = displayBox();
+    Polygon p = new Polygon();
+    p.addPoint(r.x, r.y+r.height/2);
+    p.addPoint(r.x+r.width/2, r.y);
+    p.addPoint(r.x+r.width, r.y+r.height/2);
+    p.addPoint(r.x+r.width/2, r.y+r.height);
+    return p;
+  }
+
+  public void draw(Graphics g) {
+    Polygon p = polygon();
+    g.setColor(getFillColor());
+    g.fillPolygon(p);
+    g.setColor(getFrameColor());
+    g.drawPolygon(p);
+  }
+
+  public Insets connectionInsets() {
+    Rectangle r = displayBox();
+    return new Insets(r.height/2, r.width/2, r.height/2, r.width/2);
+  }
+
+  public boolean containsPoint(int x, int y) {
+    return polygon().contains(x, y);
+  }
+
+  /*public Point chop(Point p) {
+    return PolygonFigure.chop(polygon(), p);
+  }*/
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/PolygonFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,513 @@
+/*
+ * Fri Feb 28 07:47:05 1997  Doug Lea  (dl at gee)
+ * Based on PolyLineFigure
+ */
+
+package CH.ifa.draw.contrib;
+
+import java.awt.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.figures.*;
+
+/**
+ * A scalable, rotatable polygon with an arbitrary number of points
+ */
+public  class PolygonFigure extends AttributeFigure {
+    
+    /**
+     * Distance threshold for smoothing away or locating points
+     **/
+    static final int TOO_CLOSE = 2;
+    
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 6254089689239215026L;
+    private int polygonFigureSerializedDataVersion = 1;
+    
+    protected Polygon fPoly = new Polygon();
+    
+    public PolygonFigure() {
+	super();
+    }
+    
+    public PolygonFigure(int x, int y) {
+	fPoly.addPoint(x, y);
+    }
+    
+    public PolygonFigure(Polygon p) {
+	fPoly = new Polygon(p.xpoints, p.ypoints, p.npoints);
+    }
+    
+    public Rectangle displayBox() {
+	return bounds(fPoly);
+    }
+    
+    
+    public boolean isEmpty() {
+	return (fPoly.npoints < 3 ||
+		(size().width < TOO_CLOSE) && (size().height < TOO_CLOSE));
+    }
+    
+    public Vector handles() {
+	Vector handles = new Vector(fPoly.npoints);
+	for (int i = 0; i < fPoly.npoints; i++)
+	    handles.addElement(new PolygonHandle(this, locator(i), i));
+	handles.addElement(new PolygonScaleHandle(this));
+	//handles.addElement(new PolygonPointAddHandle(this));
+	return handles;
+    }
+    
+    
+    public void basicDisplayBox(Point origin, Point corner) {
+	Rectangle r = displayBox();
+	int dx = origin.x - r.x;
+	int dy = origin.y - r.y;
+	fPoly.translate(dx, dy);
+	r = displayBox();
+	Point oldCorner = new Point(r.x + r.width, r.y + r.height);
+	Polygon p = getPolygon();
+	scaleRotate(oldCorner, p, corner);
+    }
+    
+    /**
+     * return a copy of the raw polygon
+     **/
+    public Polygon getPolygon() {
+	return new Polygon(fPoly.xpoints, fPoly.ypoints, fPoly.npoints);
+    }
+    
+    public Point center() {
+	return center(fPoly);
+    }
+    
+    public Enumeration points() {
+	Vector pts = new Vector(fPoly.npoints);
+	for (int i = 0; i < fPoly.npoints; ++i)
+	    pts.addElement(new Point(fPoly.xpoints[i], fPoly.ypoints[i]));
+	return pts.elements();
+    }
+    
+    public int pointCount() {
+	return fPoly.npoints;
+    }
+    
+    public void basicMoveBy(int dx, int dy) {
+	fPoly.translate(dx, dy);
+    }
+    
+    public void drawBackground(Graphics g) {
+	g.fillPolygon(fPoly);
+    }
+    
+    public void drawFrame(Graphics g) {
+	g.drawPolygon(fPoly);
+    }
+    
+    public boolean containsPoint(int x, int y) {
+	return fPoly.contains(x, y);
+    }
+    
+    public Connector connectorAt(int x, int y) {
+	return new ChopPolygonConnector(this);
+    }
+    
+    /**
+     * Adds a node to the list of points.
+     */
+    public  void addPoint(int x, int y) {
+	fPoly.addPoint(x, y);
+	changed();
+    }
+    
+    
+    /**
+     * Changes the position of a node.
+     */
+    public  void setPointAt(Point p, int i) {
+	willChange();
+	fPoly.xpoints[i] = p.x;
+	fPoly.ypoints[i] = p.y;
+	changed();
+    }
+    
+    /**
+     * Insert a node at the given point.
+     */
+    public  void insertPointAt(Point p, int i) {
+	willChange();
+	int n = fPoly.npoints + 1;
+	int[] xs = new int[n];
+	int[] ys = new int[n];
+	for (int j = 0; j < i; ++j) {
+	    xs[j] = fPoly.xpoints[j];
+	    ys[j] = fPoly.ypoints[j];
+	}
+	xs[i] = p.x;
+	ys[i] = p.y;
+	for (int j = i; j < fPoly.npoints; ++j) {
+	    xs[j+1] = fPoly.xpoints[j];
+	    ys[j+1] = fPoly.ypoints[j];
+	}
+	
+	fPoly = new Polygon(xs, ys, n);
+	changed();
+    }
+    
+    public  void removePointAt(int i) {
+	willChange();
+	int n = fPoly.npoints - 1;
+	int[] xs = new int[n];
+	int[] ys = new int[n];
+	for (int j = 0; j < i; ++j) {
+	    xs[j] = fPoly.xpoints[j];
+	    ys[j] = fPoly.ypoints[j];
+	}
+	for (int j = i; j < n; ++j) {
+	    xs[j] = fPoly.xpoints[j+1];
+	    ys[j] = fPoly.ypoints[j+1];
+	}
+	fPoly = new Polygon(xs, ys, n);
+	changed();
+    }
+    
+    /**
+     * Scale and rotate relative to anchor
+     **/
+    public  void scaleRotate(Point anchor, Polygon originalPolygon, Point p) {
+	willChange();
+	
+	// use center to determine relative angles and lengths
+	Point ctr = center(originalPolygon);
+	double anchorLen = Geom.length(ctr.x, ctr.y, anchor.x, anchor.y);
+	
+	if (anchorLen > 0.0) {
+	    double newLen = Geom.length(ctr.x, ctr.y, p.x, p.y);
+	    double ratio = newLen / anchorLen;
+	    
+	    double anchorAngle = Math.atan2(anchor.y - ctr.y, anchor.x - ctr.x);
+	    double newAngle = Math.atan2(p.y - ctr.y, p.x - ctr.x);
+	    double rotation = newAngle - anchorAngle;
+	    
+	    int n = originalPolygon.npoints;
+	    int[] xs = new int[n];
+	    int[] ys = new int[n];
+	    
+	    for (int i = 0; i < n; ++i) {
+		int x = originalPolygon.xpoints[i];
+		int y = originalPolygon.ypoints[i];
+		double l = Geom.length(ctr.x, ctr.y, x, y) * ratio;
+		double a = Math.atan2(y - ctr.y, x - ctr.x) + rotation;
+		xs[i] = (int)(ctr.x + l * Math.cos(a) + 0.5);
+		ys[i] = (int)(ctr.y + l * Math.sin(a) + 0.5);
+	    }
+	    fPoly =  new Polygon(xs, ys, n);
+	}
+	changed();
+    }
+    
+    
+    /**
+     * Remove points that are nearly colinear with others
+     **/
+    public void smoothPoints() {
+	willChange();
+	boolean removed = false;
+	int n = fPoly.npoints;
+	do {
+	    removed = false;
+	    int i = 0;
+	    while (i < n && n >= 3) {
+		int nxt = (i + 1) % n;
+		int prv = (i - 1 + n) % n;
+		
+		if ((distanceFromLine(fPoly.xpoints[prv], fPoly.ypoints[prv],
+				      fPoly.xpoints[nxt], fPoly.ypoints[nxt],
+				      fPoly.xpoints[i], fPoly.ypoints[i]) < TOO_CLOSE)) {
+		    removed = true;
+		    --n;
+		    for (int j = i; j < n; ++j) {
+			fPoly.xpoints[j] = fPoly.xpoints[j+1];
+			fPoly.ypoints[j] = fPoly.ypoints[j+1];
+		    }
+		}
+		else
+		    ++i;
+	    }
+	} while(removed);
+	if (n != fPoly.npoints)
+	    fPoly =  new Polygon(fPoly.xpoints, fPoly.ypoints, n);
+	changed();
+    }
+    
+    /**
+     * Splits the segment at the given point if a segment was hit.
+     * @return the index of the segment or -1 if no segment was hit.
+     */
+    public int splitSegment(int x, int y) {
+	int i = findSegment(x, y);
+	if (i != -1) {
+	    insertPointAt(new Point(x, y), i+1);
+	    return i + 1;
+	}
+	else
+	    return -1;
+    }
+    
+    public Point pointAt(int i) {
+	return new Point(fPoly.xpoints[i], fPoly.ypoints[i]);
+    }
+    
+    /**
+     * Return the point on the polygon that is furthest from the center
+     **/
+    public Point outermostPoint() {
+	Point ctr = center();
+	int outer = 0;
+	long dist = 0;
+	
+	for (int i = 0; i < fPoly.npoints; ++i) {
+	    long d = Geom.length2(ctr.x, ctr.y, fPoly.xpoints[i], fPoly.ypoints[i]);
+	    if (d > dist) {
+		dist = d;
+		outer = i;
+	    }
+	}
+	
+	return new Point(fPoly.xpoints[outer], fPoly.ypoints[outer]);
+    }
+    
+    
+    /**
+     * Gets the segment that is hit by the given point.
+     * @return the index of the segment or -1 if no segment was hit.
+     */
+    public int findSegment(int x, int y) {
+	double dist = TOO_CLOSE;
+	int best = -1;
+	
+	for (int i = 0; i < fPoly.npoints; i++) {
+	    int n = (i + 1) % fPoly.npoints;
+	    double d =  distanceFromLine(fPoly.xpoints[i], fPoly.ypoints[i],
+					 fPoly.xpoints[n], fPoly.ypoints[n],
+					 x, y);
+	    if (d < dist) {
+		dist = d;
+		best = i;
+	    }
+	}
+	return best;
+    }
+    
+    public Point chop(Point p) {
+	return chop(fPoly, p);
+    }
+    
+    public String getMap() {
+	String sense = (String)getAttribute("Sensitive");
+	if (sense == null || sense.length() == 0)
+	    return "";
+	try {
+	    sense = URLDecoder.decode(sense);
+	} catch (Exception e) {}
+	Rectangle box = displayBox();
+	String coords = "";
+	for (int i = 0; i < fPoly.npoints; i++) {
+	    if (i > 0)
+		coords += ",";
+	    coords += fPoly.xpoints[i] + "," + fPoly.ypoints[i];
+	}
+	return "<area shape=\"poly\" coords=\"" +
+	    coords + "\" />\n";
+    }
+    
+    public void write(StorableOutput dw) {
+	super.write(dw);
+	dw.writeInt(fPoly.npoints);
+	for (int i = 0; i < fPoly.npoints; ++i) {
+	    dw.writeInt(fPoly.xpoints[i]);
+	    dw.writeInt(fPoly.ypoints[i]);
+	}
+    }
+    
+    public void read(StorableInput dr) throws IOException {
+	super.read(dr);
+	int size = dr.readInt();
+	int[] xs = new int[size];
+	int[] ys = new int[size];
+	for (int i = 0; i < size; i++) {
+	    xs[i] = dr.readInt();
+	    ys[i] = dr.readInt();
+	}
+	fPoly = new Polygon(xs, ys, size);
+    }
+    
+    /**
+     * Creates a locator for the point with the given index.
+     */
+    public static Locator locator(final int pointIndex) {
+	return new AbstractLocator() {
+		public Point locate(Figure owner) {
+		    PolygonFigure plf = (PolygonFigure)owner;
+		    // guard against changing PolygonFigures -> temporary hack
+		    if (pointIndex < plf.pointCount())
+			return ((PolygonFigure)owner).pointAt(pointIndex);
+		    return new Point(-1, -1);
+		}
+	    };
+    }
+    
+    /**
+     * compute distance of point from line segment, or
+     * Double.MAX_VALUE if perpendicular projection is outside segment; or
+     * If pts on line are same, return distance from point
+     **/
+    public static double distanceFromLine(int xa, int ya,
+					  int xb, int yb,
+					  int xc, int yc) {
+	
+	
+	// source:http://vision.dai.ed.ac.uk/andrewfg/c-g-a-faq.html#q7
+	//Let the point be C (XC,YC) and the line be AB (XA,YA) to (XB,YB).
+	//The length of the
+	//      line segment AB is L:
+	//
+	//                    ___________________
+	//                   |        2         2
+	//              L = \| (XB-XA) + (YB-YA)
+	//and
+	//
+	//                  (YA-YC)(YA-YB)-(XA-XC)(XB-XA)
+	//              r = -----------------------------
+	//                              L**2
+	//
+	//                  (YA-YC)(XB-XA)-(XA-XC)(YB-YA)
+	//              s = -----------------------------
+	//                              L**2
+	//
+	//      Let I be the point of perpendicular projection of C onto AB, the
+	//
+	//              XI=XA+r(XB-XA)
+	//              YI=YA+r(YB-YA)
+	//
+	//      Distance from A to I = r*L
+	//      Distance from C to I = s*L
+	//
+	//      If r < 0 I is on backward extension of AB
+	//      If r>1 I is on ahead extension of AB
+	//      If 0<=r<=1 I is on AB
+	//
+	//      If s < 0 C is left of AB (you can just check the numerator)
+	//      If s>0 C is right of AB
+	//      If s=0 C is on AB
+	
+	int xdiff = xb - xa;
+	int ydiff = yb - ya;
+	long l2 = xdiff*xdiff + ydiff*ydiff;
+	
+	if (l2 == 0) return Geom.length(xa, ya, xc, yc);
+	
+	double rnum =  (ya-yc) * (ya-yb) - (xa-xc) * (xb-xa);
+	double r = rnum / l2;
+	
+	if (r < 0.0 || r > 1.0) return Double.MAX_VALUE;
+	
+	double xi = xa + r * xdiff;
+	double yi = ya + r * ydiff;
+	double xd = xc - xi;
+	double yd = yc - yi;
+	return Math.sqrt(xd * xd + yd * yd);
+	
+	/*
+	  for directional version, instead use
+	  double snum =  (ya-yc) * (xb-xa) - (xa-xc) * (yb-ya);
+	  double s = snum / l2;
+	  
+	  double l = Math.sqrt((double)l2);
+	  return = s * l;
+	*/
+    }
+    
+    /**
+     * replacement for builtin Polygon.getBounds that doesn't always update?
+     */
+    
+    public static Rectangle bounds(Polygon p) {
+	int minx = Integer.MAX_VALUE;
+	int miny = Integer.MAX_VALUE;
+	int maxx = Integer.MIN_VALUE;
+	int maxy = Integer.MIN_VALUE;
+	int n = p.npoints;
+	for (int i = 0; i < n; i++) {
+	    int x = p.xpoints[i];
+	    int y = p.ypoints[i];
+	    if (x > maxx) maxx = x;
+	    if (x < minx) minx = x;
+	    if (y > maxy) maxy = y;
+	    if (y < miny) miny = y;
+	}
+	
+	return new Rectangle(minx, miny, maxx-minx, maxy-miny);
+    }
+    
+    public static Point center(Polygon p) {
+	long sx = 0;
+	long sy = 0;
+	int n = p.npoints;
+	for (int i = 0; i < n; i++) {
+	    sx += p.xpoints[i];
+	    sy += p.ypoints[i];
+	}
+	
+	return new Point((int)(sx/n), (int)(sy/n));
+    }
+    
+    public static Point chop(Polygon poly, Point p) {
+	Point ctr = center(poly);
+	int cx = -1;
+	int cy = -1;
+	long len = Long.MAX_VALUE;
+	
+	// Try for points along edge
+	
+	for (int i = 0; i < poly.npoints; ++i) {
+	    int nxt = (i + 1) % poly.npoints;
+	    Point chop = Geom.intersect(poly.xpoints[i],
+					poly.ypoints[i],
+					poly.xpoints[nxt],
+					poly.ypoints[nxt],
+					p.x,
+					p.y,
+					ctr.x,
+					ctr.y);
+	    if (chop != null) {
+		long cl = Geom.length2(chop.x, chop.y, p.x, p.y);
+		if (cl < len) {
+		    len = cl;
+		    cx = chop.x;
+		    cy = chop.y;
+		}
+	    }
+	}
+	// if none found, pick closest vertex
+	//if (len ==  Long.MAX_VALUE) {
+	{ // try anyway
+	    for (int i = 0; i < poly.npoints; ++i) {
+		long l = Geom.length2(poly.xpoints[i], poly.ypoints[i], p.x, p.y);
+		if (l < len) {
+		    len = l;
+		    cx = poly.xpoints[i];
+		    cy = poly.ypoints[i];
+		}
+	    }
+	}
+	return new Point(cx, cy);
+    }
+    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/PolygonHandle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,53 @@
+/*
+ * Fri Feb 28 07:47:13 1997  Doug Lea  (dl at gee)
+ * Based on PolyLineHandle
+ */
+
+package CH.ifa.draw.contrib;
+
+import java.awt.*;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.standard.*;
+
+
+/**
+ * A handle for a node on the polygon.
+ */
+public class PolygonHandle extends AbstractHandle {
+
+  private int fIndex;
+  private Locator fLocator;
+
+  /**
+   * Constructs a polygon handle.
+   * @param owner the owning polygon figure.
+   * @l the locator
+   * @index the index of the node associated with this handle.
+   */
+  public PolygonHandle(PolygonFigure owner, Locator l, int index) {
+    super(owner);
+    fLocator = l;
+    fIndex = index;
+  }
+
+  public void invokeStep (int x, int y, int anchorX, int anchorY, DrawingView view) {
+    myOwner().setPointAt(new Point(x, y), fIndex);
+  }
+
+  public void invokeEnd  (int x, int y, int anchorX, int anchorY, DrawingView view) {
+    myOwner().smoothPoints();
+  }
+
+  public Point locate() {
+    return fLocator.locate(owner());
+  }
+
+  private PolygonFigure myOwner() {
+    return (PolygonFigure)owner();
+  }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/PolygonScaleHandle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,94 @@
+/*
+ * Sat Mar  1 09:06:09 1997  Doug Lea  (dl at gee)
+ * Based on RadiusHandle
+ */
+
+package CH.ifa.draw.contrib;
+
+import java.awt.*;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.standard.*;
+
+
+/**
+ * A Handle to scale and rotate a PolygonFigure
+ */
+class PolygonScaleHandle extends AbstractHandle {
+
+  private Point fOrigin = null;
+  private Point fCurrent = null;
+  private Polygon fOrigPoly = null;
+
+  public PolygonScaleHandle(PolygonFigure owner) {
+    super(owner);
+ }
+
+  public void invokeStart(int  x, int  y, Drawing drawing) {
+    fOrigPoly = ((PolygonFigure)(owner())).getPolygon();
+    fOrigin = getOrigin();
+    fCurrent = new Point(fOrigin.x, fOrigin.y);
+  }
+
+  public void invokeStep (int dx, int dy, Drawing drawing) {
+    fCurrent = new Point(fOrigin.x + dx, fOrigin.y + dy);
+    ((PolygonFigure)(owner())).scaleRotate(fOrigin, fOrigPoly, fCurrent);
+  }
+
+  public void invokeEnd  (int dx, int dy, Drawing drawing) {
+    fOrigPoly = null;
+    fOrigin = null;
+    fCurrent = null;
+  }
+
+  public Point locate() {
+    if (fCurrent != null)
+      return fCurrent;
+    else
+      return getOrigin();
+  }
+
+  Point getOrigin() { // find a nice place to put handle
+    // Need to pick a place that will not overlap with point handle
+    // and is internal to polygon
+
+    // Try for one HANDLESIZE step away from outermost toward center
+
+    Point outer = ((PolygonFigure)(owner())).outermostPoint();
+    Point ctr = ((PolygonFigure)(owner())).center();
+    double len = Geom.length(outer.x, outer.y, ctr.x, ctr.y);
+    if (len == 0) // best we can do?
+      return new Point(outer.x - HANDLESIZE/2, outer.y + HANDLESIZE/2);
+
+    double u = HANDLESIZE / len;
+    if (u > 1.0) // best we can do?
+      return new Point((outer.x * 3 + ctr.x)/4, (outer.y * 3 + ctr.y)/4);
+    else
+      return new Point((int)(outer.x * (1.0 - u) + ctr.x * u),
+                       (int)(outer.y * (1.0 - u) + ctr.y * u));
+  }
+
+  public void draw(Graphics g) {
+    Rectangle r = displayBox();
+
+    g.setColor(Color.yellow);
+    g.fillOval(r.x, r.y, r.width, r.height);
+
+    g.setColor(Color.black);
+    g.drawOval(r.x, r.y, r.width, r.height);
+
+    /*
+     * for debugging ...
+    Point ctr = ((PolygonFigure)(owner())).center();
+    g.setColor(Color.blue);
+    g.fillOval(ctr.x, ctr.y, r.width, r.height);
+
+    g.setColor(Color.black);
+    g.drawOval(ctr.x, ctr.y, r.width, r.height);
+
+    */
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/PolygonTool.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,108 @@
+/*
+ * Fri Feb 28 07:47:05 1997  Doug Lea  (dl at gee)
+ * Based on ScribbleTool
+ */
+
+package CH.ifa.draw.contrib;
+
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.awt.event.KeyEvent;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.standard.*;
+
+
+/**
+ */
+public class PolygonTool extends AbstractTool {
+
+  private PolygonFigure  fPolygon;
+  private int            fLastX, fLastY;
+
+  public PolygonTool(DrawingView view) {
+    super(view);
+  }
+
+  public void activate() {
+    super.activate();
+    fPolygon = null;
+  }
+
+  public void deactivate() {
+    super.deactivate();
+    if (fPolygon != null) {
+      fPolygon.smoothPoints();
+      if (fPolygon.pointCount() < 3 ||
+          fPolygon.size().width < 4 || fPolygon.size().height < 4)
+        drawing().remove(fPolygon);
+    }
+    fPolygon = null;
+  }
+
+  private void addPoint(int x, int y) {
+      if (fPolygon == null) {
+          fPolygon = new PolygonFigure(x, y);
+          view().add(fPolygon);
+          fPolygon.addPoint(x, y);
+      } else if (fLastX != x || fLastY != y)
+          fPolygon.addPoint(x, y);
+
+      fLastX = x;
+      fLastY = y;
+  }
+
+  public void mouseDown(MouseEvent e, int x, int y) {
+    // replace pts by actual event pts
+    x = e.getX();
+    y = e.getY();
+
+    if (e.getClickCount() >= 2) {
+        if (fPolygon != null) {
+            fPolygon.smoothPoints();
+            editor().toolDone();
+        }
+        fPolygon = null;
+
+    } else {
+        // use original event coordinates to avoid
+        // supress that the scribble is constrained to
+        // the grid
+        addPoint(e.getX(), e.getY());
+    }
+  }
+
+    public void keyDown(KeyEvent e, int key) {
+	System.out.println("Key " + key);
+	if (key == 27) {
+	    if (fPolygon != null) {
+		fPolygon.smoothPoints();
+		editor().toolDone();
+	    }
+	    fPolygon = null;
+	}
+    }
+
+  public void mouseMove(MouseEvent e, int x, int y) {
+    if (fPolygon != null) {
+       if (fPolygon.pointCount() > 1) {
+           fPolygon.setPointAt(new Point(x, y), fPolygon.pointCount()-1);
+           view().checkDamage();
+       }
+    }
+  }
+
+  public void mouseDrag(MouseEvent e, int x, int y) {
+    // replace pts by actual event pts
+    x = e.getX();
+    y = e.getY();
+    addPoint(x, y);
+  }
+
+
+  public void mouseUp(MouseEvent e, int x, int y) {
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/TriangleFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,174 @@
+/*
+ * Hacked together by Doug lea
+ * Tue Feb 25 17:30:58 1997  Doug Lea  (dl at gee)
+ *
+ */
+
+package CH.ifa.draw.contrib;
+
+import java.awt.*;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.figures.*;
+
+/**
+ * A triangle with same dimensions as its enclosing rectangle,
+ * and apex at any of 8 places
+ */
+public  class TriangleFigure extends RectangleFigure {
+
+  static double[] rotations = {
+    -Math.PI/2, -Math.PI/4,
+    0.0, Math.PI/4,
+    Math.PI/2, Math.PI * 3/4,
+    Math.PI,  -Math.PI * 3/4
+  };
+
+  protected int fRotation = 0;
+
+  public TriangleFigure() {
+    super(new Point(0,0), new Point(0,0));
+  }
+
+  public TriangleFigure(Point origin, Point corner) {
+    super(origin, corner);
+  }
+
+  public Vector handles() {
+    Vector h = super.handles();
+    h.addElement(new TriangleRotationHandle(this));
+    return h;
+  }
+
+  public void rotate(double angle) {
+    willChange();
+    //System.out.println("a:"+angle);
+    double dist = Double.MAX_VALUE;
+    int best = 0;
+    for (int i = 0; i < rotations.length; ++i) {
+      double d = Math.abs(angle - rotations[i]);
+      if (d < dist) {
+        dist = d;
+        best = i;
+      }
+    }
+    fRotation = best;
+    changed();
+  }
+
+  /** Return the polygon describing the triangle **/
+  public Polygon polygon() {
+    Rectangle r = displayBox();
+    Polygon p = new Polygon();
+    switch (fRotation) {
+    case 0:
+      p.addPoint(r.x+r.width/2, r.y);
+      p.addPoint(r.x+r.width, r.y+r.height);
+      p.addPoint(r.x, r.y+r.height);
+      break;
+    case 1:
+      p.addPoint(r.x + r.width, r.y);
+      p.addPoint(r.x+r.width, r.y+r.height);
+      p.addPoint(r.x, r.y);
+      break;
+    case 2:
+      p.addPoint(r.x + r.width, r.y+r.height/2);
+      p.addPoint(r.x, r.y+r.height);
+      p.addPoint(r.x, r.y);
+      break;
+    case 3:
+      p.addPoint(r.x+r.width, r.y+r.height);
+      p.addPoint(r.x, r.y+r.height);
+      p.addPoint(r.x + r.width, r.y);
+      break;
+    case 4:
+      p.addPoint(r.x+r.width/2, r.y+r.height);
+      p.addPoint(r.x, r.y);
+      p.addPoint(r.x + r.width, r.y);
+      break;
+    case 5:
+      p.addPoint(r.x, r.y+r.height);
+      p.addPoint(r.x, r.y);
+      p.addPoint(r.x+r.width, r.y+r.height);
+      break;
+    case 6:
+      p.addPoint(r.x, r.y+r.height/2);
+      p.addPoint(r.x + r.width, r.y);
+      p.addPoint(r.x+r.width, r.y+r.height);
+      break;
+    case 7:
+      p.addPoint(r.x, r.y);
+      p.addPoint(r.x + r.width, r.y);
+      p.addPoint(r.x, r.y+r.height);
+      break;
+    }
+    return p;
+  }
+
+
+  public void draw(Graphics g) {
+    Polygon p = polygon();
+    g.setColor(getFillColor());
+    g.fillPolygon(p);
+    g.setColor(getFrameColor());
+    g.drawPolygon(p);
+  }
+
+
+  public Insets connectionInsets() {
+    Rectangle r = displayBox();
+    switch(fRotation) {
+    case 0:
+      return new Insets(r.height, r.width/2, 0, r.width/2);
+    case 1:
+      return new Insets(0, r.width, r.height, 0);
+    case 2:
+      return new Insets(r.height/2, 0, r.height/2, r.width);
+    case 3:
+      return new Insets(r.height, r.width, 0, 0);
+    case 4:
+      return new Insets(0, r.width/2, r.height, r.width/2);
+    case 5:
+      return new Insets(r.height, 0, 0, r.width);
+    case 6:
+      return new Insets(r.height/2, r.width, r.height/2, 0);
+    case 7:
+      return new Insets(0, 0, r.height, r.width);
+    default:
+      return null;
+    }
+  }
+
+  public boolean containsPoint(int x, int y) {
+    return polygon().contains(x, y);
+  }
+
+  public Point center() {
+    return PolygonFigure.center(polygon());
+  }
+
+  public Point chop(Point p) {
+    return PolygonFigure.chop(polygon(), p);
+  }
+
+  public Object clone() {
+    TriangleFigure figure = (TriangleFigure) super.clone();
+    figure.fRotation = fRotation;
+    return figure;
+  }
+
+    //-- store / load ----------------------------------------------
+
+    public void write(StorableOutput dw) {
+      super.write(dw);
+      dw.writeInt(fRotation);
+    }
+
+    public void read(StorableInput dr) throws IOException {
+      super.read(dr);
+      fRotation = dr.readInt();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/contrib/TriangleRotationHandle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,75 @@
+/*
+ * Sun Mar  2 19:15:28 1997  Doug Lea  (dl at gee)
+ * Based on RadiusHandle
+ */
+
+package CH.ifa.draw.contrib;
+
+import java.awt.*;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.standard.*;
+
+
+/**
+ * A Handle to rotate a TriangleFigure
+ */
+class TriangleRotationHandle extends AbstractHandle {
+
+  private Point fOrigin = null;
+  private Point fCenter = null;
+
+  public TriangleRotationHandle(TriangleFigure owner) {
+    super(owner);
+ }
+
+  public void invokeStart(int  x, int  y, Drawing drawing) {
+    fCenter = owner().center();
+    fOrigin = getOrigin();
+  }
+
+  public void invokeStep (int dx, int dy, Drawing drawing) {
+    double angle = Math.atan2(fOrigin.y + dy - fCenter.y,
+                              fOrigin.x + dx - fCenter.x);
+    ((TriangleFigure)(owner())).rotate(angle);
+  }
+
+  public void invokeEnd  (int dx, int dy, Drawing drawing) {
+    fOrigin = null;
+    fCenter = null;
+  }
+
+  public Point locate() {
+    return getOrigin();
+  }
+
+  Point getOrigin() { // find a nice place to put handle
+    // almost same code as PolygonScaleHandle
+    Polygon p = ((TriangleFigure)(owner())).polygon();
+    Point first = new Point(p.xpoints[0], p.ypoints[0]);
+    Point ctr = owner().center();
+    double len = Geom.length(first.x, first.y, ctr.x, ctr.y);
+    if (len == 0) // best we can do?
+      return new Point(first.x - HANDLESIZE/2, first.y + HANDLESIZE/2);
+
+    double u = HANDLESIZE / len;
+    if (u > 1.0) // best we can do?
+      return new Point((first.x * 3 + ctr.x)/4, (first.y * 3 + ctr.y)/4);
+    else
+      return new Point((int)(first.x * (1.0 - u) + ctr.x * u),
+                       (int)(first.y * (1.0 - u) + ctr.y * u));
+  }
+
+  public void draw(Graphics g) {
+    Rectangle r = displayBox();
+
+    g.setColor(Color.yellow);
+    g.fillOval(r.x, r.y, r.width, r.height);
+
+    g.setColor(Color.black);
+    g.drawOval(r.x, r.y, r.width, r.height);
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ArrowTip.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,97 @@
+/*
+ * @(#)ArrowTip.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.io.*;
+import java.awt.*;
+
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * An arrow tip line decoration.
+ * @see PolyLineFigure
+ */
+public  class ArrowTip implements LineDecoration {
+
+    private double  fAngle;         // pointiness of arrow
+    private double  fOuterRadius;
+    private double  fInnerRadius;
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = -3459171428373823638L;
+    private int arrowTipSerializedDataVersion = 1;
+
+	public ArrowTip() {
+	    fAngle = 0.40;//0.35;
+	    fOuterRadius = 8;//15;
+	    fInnerRadius = 8;//12;
+	}
+
+   /**
+    * Constructs an arrow tip with the given angle and radius.
+    */
+	public ArrowTip(double angle, double outerRadius, double innerRadius) {
+	    fAngle = angle;
+	    fOuterRadius = outerRadius;
+	    fInnerRadius = innerRadius;
+	}
+
+   /**
+    * Draws the arrow tip in the direction specified by the given two
+    * points..
+    */
+    public void draw(Graphics g, int x1, int y1, int x2, int y2) {
+        // TBD: reuse the Polygon object
+        Polygon p = outline(x1, y1, x2, y2);
+        g.fillPolygon(p.xpoints, p.ypoints, p.npoints);
+    }
+
+   /**
+    * Calculates the outline of an arrow tip.
+    */
+    public Polygon outline(int x1, int y1, int x2, int y2) {
+        double dir = Math.PI/2 - Math.atan2(x2-x1, y1-y2);
+        return outline(x1, y1, dir);
+    }
+
+    private Polygon outline(int x, int y, double direction) {
+        Polygon shape = new Polygon();
+
+        shape.addPoint(x, y);
+        addPointRelative(shape, x, y, fOuterRadius, direction - fAngle);
+        addPointRelative(shape, x, y, fInnerRadius, direction);
+        addPointRelative(shape, x, y, fOuterRadius, direction + fAngle);
+        shape.addPoint(x,y); // Closing the polygon (TEG 97-04-23)
+        return shape;
+    }
+
+    private void addPointRelative(Polygon shape, int x, int y, double radius, double angle) {
+        shape.addPoint(
+            x + (int) (radius * Math.cos(angle)),
+            y - (int) (radius * Math.sin(angle)));
+    }
+
+    /**
+     * Stores the arrow tip to a StorableOutput.
+     */
+    public void write(StorableOutput dw) {
+    }
+
+    public String getMap() {
+	return "";
+    }
+
+    /**
+     * Reads the arrow tip from a StorableInput.
+     */
+    public void read(StorableInput dr) throws IOException {
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/AttributeFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,142 @@
+/*
+ * @(#)AttributeFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+import java.awt.*;
+import java.util.*;
+import java.io.*;
+
+/**
+ * A figure that can keep track of an open ended set of attributes.
+ * The attributes are stored in a dictionary implemented by
+ * FigureAttributes.
+ *
+ * @see Figure
+ * @see Handle
+ * @see FigureAttributes
+ */
+public abstract class AttributeFigure extends AbstractFigure {
+
+    /**
+     * The default attributes associated with a figure.
+     * If a figure doesn't have an attribute set, a default
+     * value from this shared attribute set is returned.
+     * @see #getAttribute
+     * @see #setAttribute
+     */
+    private static FigureAttributes fgDefaultAttributes = null;
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = -10857585979273442L;
+    private int attributeFigureSerializedDataVersion = 1;
+
+    protected AttributeFigure() { }
+
+    /**
+     * Draws the figure in the given graphics. Draw is a template
+     * method calling drawBackground followed by drawFrame.
+     */
+    public void draw(Graphics g, boolean showGuides) {
+        Color fill = getFillColor();
+        if (!ColorMap.getColorMap().isTransparent(fill)) {
+            g.setColor(fill);
+            drawBackground(g);
+        }
+        Color frame = getFrameColor();
+        if (!ColorMap.getColorMap().isTransparent(frame)) {
+            g.setColor(frame);
+            drawFrame(g);
+        }
+	if (showGuides) {
+	    drawURL(g);
+	}
+    }
+
+    /**
+     * Draws the background of the figure.
+     * @see #draw
+     */
+    protected void drawBackground(Graphics g) {
+    }
+
+    /**
+     * Draws the frame of the figure.
+     * @see #draw
+     */
+    protected void drawFrame(Graphics g) {
+    }
+
+    /**
+     * Draws the map of the figure
+     * @see #draw
+     */
+    private void drawURL(Graphics g) {
+	String sense = (String)getAttribute("Sensitive");
+	if (sense != null && sense.length() > 0) {
+	    Rectangle r = displayBox();
+	    g.setColor(Color.red);
+	    g.setFont(dialogFont);
+	    g.drawString("url=" + sense, r.x, r.y + r.height);
+	}
+    }
+
+    /**
+     * Gets the fill color of a figure. This is a convenience
+     * method.
+     @see getAttribute
+     */
+    public Color getFillColor() {
+        return (Color) getAttribute("FillColor");
+    }
+
+    /**
+     * Gets the frame color of a figure. This is a convenience
+     * method.
+     @see getAttribute
+     */
+    public Color getFrameColor() {
+        return (Color) getAttribute("FrameColor");
+    }
+
+    //---- figure attributes ----------------------------------
+
+    private static void initializeAttributes() {
+        fgDefaultAttributes = new FigureAttributes();
+        fgDefaultAttributes.set("FrameColor", Color.black);
+        fgDefaultAttributes.set("FillColor",  new Color(0x70DB93));
+        fgDefaultAttributes.set("TextColor",  Color.black);
+        fgDefaultAttributes.set("ArrowMode",  new Integer(0));
+        fgDefaultAttributes.set("FontName",  "Helvetica");
+        fgDefaultAttributes.set("FontSize",   new Integer(12));
+        fgDefaultAttributes.set("FontStyle",  new Integer(Font.PLAIN));
+        fgDefaultAttributes.set("TextAlign",  "Left");
+        fgDefaultAttributes.set("Sensitive",  "");
+    }
+
+    /**
+     * Gets a the default value for a named attribute
+     * @see getAttribute
+     */
+    public static Object getDefaultAttribute(String name) {
+        if (fgDefaultAttributes == null)
+            initializeAttributes();
+        return fgDefaultAttributes.get(name);
+    }
+
+    public Object defaultAttribute(String name) {
+	return getDefaultAttribute(name);
+    }
+
+    public String getMap() {
+	return "";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/BorderDecorator.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,75 @@
+/*
+ * @(#)BorderDecorator.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.util.*;
+
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+/**
+ * BorderDecorator decorates an arbitrary Figure with
+ * a border.
+ */
+public  class BorderDecorator extends DecoratorFigure {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 1205601808259084917L;
+    private int borderDecoratorSerializedDataVersion = 1;
+
+    public BorderDecorator() { }
+    public BorderDecorator(Figure figure) {
+        super(figure);
+    }
+
+    private Point border() {
+        return new Point(3,3);
+    }
+
+    /**
+     * Draws a the figure and decorates it with a border.
+     */
+    public void draw(Graphics g, boolean showGuides) {
+        Rectangle r = displayBox();
+        super.draw(g, showGuides);
+        g.setColor(Color.white);
+        g.drawLine(r.x, r.y, r.x, r.y + r.height);
+        g.drawLine(r.x, r.y, r.x + r.width, r.y);
+        g.setColor(Color.gray);
+        g.drawLine(r.x + r.width, r.y, r.x + r.width, r.y + r.height);
+        g.drawLine(r.x , r.y + r.height, r.x + r.width, r.y + r.height);
+    }
+
+    /**
+     * Gets the displaybox including the border.
+     */
+    public Rectangle displayBox() {
+        Rectangle r = fComponent.displayBox();
+        r.grow(border().x, border().y);
+        return r;
+    }
+
+    /**
+     * Invalidates the figure extended by its border.
+     */
+    public void figureInvalidated(FigureChangeEvent e) {
+        Rectangle rect = e.getInvalidatedRectangle();
+        rect.grow(border().x, border().y);
+        super.figureInvalidated(new FigureChangeEvent(e.getFigure(), rect));
+    }
+
+    public Insets connectionInsets() {
+        Insets i = super.connectionInsets();
+        i.top -= 3;
+        i.bottom -= 3;
+        i.left -= 3;
+        i.right -= 3;
+        return i;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/BorderTool.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,28 @@
+/*
+ * @(#)BorderTool.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.framework.*;
+
+/**
+ * BorderTool decorates the clicked figure with a BorderDecorator.
+ *
+ * @see BorderDecorator
+ */
+public  class BorderTool extends ActionTool {
+
+    public BorderTool(DrawingView view) {
+        super(view);
+    }
+
+    /**
+    * Decorates the clicked figure with a border.
+    */
+    public void action(Figure figure) {
+        drawing().replace(figure, new BorderDecorator(figure));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ChopEllipseConnector.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,38 @@
+/*
+ * @(#)ChopEllipseConnector.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.Geom;
+
+/**
+ * A ChopEllipseConnector locates a connection point by
+ * chopping the connection at the ellipse defined by the
+ * figure's display box.
+ */
+public class ChopEllipseConnector extends ChopBoxConnector {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = -3165091511154766610L;
+
+    public ChopEllipseConnector() {
+    }
+
+    public ChopEllipseConnector(Figure owner) {
+        super(owner);
+    }
+
+    protected Point chop(Figure target, Point from) {
+        Rectangle r = target.displayBox();
+        double angle = Geom.pointToAngle(r, from);
+	    return Geom.ovalAngleToPoint(r, angle);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ConnectedTextTool.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,52 @@
+/*
+ * @(#)ConnectedTextTool.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+/**
+ * Tool to create new or edit existing text figures.
+ * A new text figure is connected with the clicked figure.
+ *
+ * @see TextHolder
+ */
+public  class ConnectedTextTool extends TextTool {
+
+    boolean     fConnected = false;
+
+    public ConnectedTextTool(DrawingView view, Figure prototype) {
+        super(view, prototype);
+    }
+
+    /**
+     * If the pressed figure is a TextHolder it can be edited otherwise
+     * a new text figure is created.
+     */
+    public void mouseDown(MouseEvent e, int x, int y) {
+        super.mouseDown(e, x, y);
+
+	    Figure pressedFigure =  drawing().findFigureInside(x, y);
+
+	    TextHolder textHolder = (TextHolder)createdFigure();
+        if (!fConnected && pressedFigure != null &&
+                     textHolder != null && pressedFigure != textHolder) {
+            textHolder.connect(pressedFigure);
+            fConnected = true;
+        }
+    }
+
+    /**
+     * If the pressed figure is a TextHolder it can be edited otherwise
+     * a new text figure is created.
+     */
+    public void activate() {
+        fConnected = false;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ElbowConnection.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,88 @@
+/*
+ * @(#)ElbowConnection.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * A LineConnection that constrains a connection to
+ * orthogonal lines.
+ */
+public  class ElbowConnection extends LineConnection {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 2193968743082078559L;
+    private int elbowConnectionSerializedDataVersion = 1;
+
+    public ElbowConnection() {
+        super();
+    }
+
+    public void updateConnection() {
+        super.updateConnection();
+        updatePoints();
+    }
+
+    public void layoutConnection() {
+    }
+
+    /**
+     * Gets the handles of the figure.
+     */
+    public Vector handles() {
+        Vector handles = new Vector(fPoints.size()*2);
+        handles.addElement(new ChangeConnectionStartHandle(this));
+        for (int i = 1; i < fPoints.size()-1; i++)
+            handles.addElement(new NullHandle(this, locator(i)));
+        handles.addElement(new ChangeConnectionEndHandle(this));
+        for (int i = 0; i < fPoints.size()-1; i++)
+            handles.addElement(new ElbowHandle(this, i));
+        return handles;
+    }
+
+    public Locator connectedTextLocator(Figure f) {
+        return new ElbowTextLocator();
+    }
+
+    protected void updatePoints() {
+        willChange();
+        Point start = startPoint();
+        Point end = endPoint();
+        fPoints.removeAllElements();
+        fPoints.addElement(start);
+
+        if (start.x == end.x || start.y == end.y) {
+            fPoints.addElement(end);
+        }
+        else {
+            Rectangle r1 = start().owner().displayBox();
+            Rectangle r2 = end().owner().displayBox();
+
+            int x1, y1, x2, y2;
+            int dir = Geom.direction(r1.x + r1.width/2, r1.y + r1.height/2,
+                        r2.x + r2.width/2, r2.y + r2.height/2);
+            if (dir == Geom.NORTH || dir == Geom.SOUTH) {
+                fPoints.addElement(new Point(start.x, (start.y + end.y)/2));
+                fPoints.addElement(new Point(end.x, (start.y + end.y)/2));
+            }
+            else {
+                fPoints.addElement(new Point((start.x + end.x)/2, start.y));
+                fPoints.addElement(new Point((start.x + end.x)/2, end.y));
+            }
+            fPoints.addElement(end);
+        }
+        changed();
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ElbowHandle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,126 @@
+/*
+ * @(#)ElbowHandle.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.Geom;
+
+/**
+ * A Handle to move an ElbowConnection left/right or up/down.
+ */
+public class ElbowHandle extends AbstractHandle {
+
+    private int fSegment;
+    private int fLastX, fLastY;      // previous mouse position
+
+    public ElbowHandle(LineConnection owner, int segment) {
+        super(owner);
+        fSegment = segment;
+    }
+
+    public void invokeStart(int  x, int  y, DrawingView view) {
+        fLastX = x;
+        fLastY = y;
+    }
+
+    public void invokeStep (int x, int y, int anchorX, int anchorY, DrawingView view) {
+        LineConnection line = ownerConnection();
+        Point p1 = line.pointAt(fSegment);
+        Point p2 = line.pointAt(fSegment+1);
+        int ddx = x - fLastX;
+        int ddy = y - fLastY;
+
+        Point np1;
+        Point np2;
+        if (isVertical(p1, p2)) {
+            int cx = constrainX(p1.x + ddx);
+            np1 = new Point(cx, p1.y);
+            np2 = new Point(cx, p2.y);
+        } else {
+            int cy = constrainY(p1.y + ddy);
+            np1 = new Point(p1.x, cy);
+            np2 = new Point(p2.x, cy);
+        }
+        line.setPointAt(np1, fSegment);
+        line.setPointAt(np2, fSegment+1);
+        fLastX = x;
+        fLastY = y;
+    }
+
+    private boolean isVertical(Point p1, Point p2) {
+        return p1.x == p2.x;
+    }
+
+    public Point locate() {
+        LineConnection line = ownerConnection();
+        int segment = Math.min(fSegment, line.pointCount()-2);
+        Point p1 = line.pointAt(segment);
+        Point p2 = line.pointAt(segment+1);
+        return new Point((p1.x + p2.x)/2, (p1.y + p2.y)/2);
+    }
+
+    public void draw(Graphics g) {
+        Rectangle r = displayBox();
+
+        g.setColor(Color.yellow);
+        g.fillOval(r.x, r.y, r.width, r.height);
+
+        g.setColor(Color.black);
+        g.drawOval(r.x, r.y, r.width, r.height);
+    }
+
+    private int constrainX(int x) {
+        LineConnection line = ownerConnection();
+        Figure startFigure = line.start().owner();
+        Figure endFigure = line.end().owner();
+        Rectangle start = startFigure.displayBox();
+        Rectangle end = endFigure.displayBox();
+        Insets i1 = startFigure.connectionInsets();
+        Insets i2 = endFigure.connectionInsets();
+
+        int r1x, r1width, r2x, r2width;
+        r1x = start.x + i1.left;
+        r1width = start.width - i1.left - i1.right-1;
+
+        r2x = end.x + i2.left;
+        r2width = end.width - i2.left - i2.right-1;
+
+        if (fSegment == 0)
+            x = Geom.range(r1x, r1x + r1width, x);
+        if (fSegment == line.pointCount()-2)
+            x = Geom.range(r2x, r2x + r2width, x);
+        return x;
+    }
+
+    private int constrainY(int y) {
+        LineConnection line = ownerConnection();
+        Figure startFigure = line.start().owner();
+        Figure endFigure = line.end().owner();
+        Rectangle start = startFigure.displayBox();
+        Rectangle end = endFigure.displayBox();
+        Insets i1 = startFigure.connectionInsets();
+        Insets i2 = endFigure.connectionInsets();
+
+        int r1y, r1height, r2y, r2height;
+        r1y = start.y + i1.top;
+        r1height = start.height - i1.top - i1.bottom-1;
+        r2y = end.y + i2.top;
+        r2height = end.height - i2.top - i2.bottom-1;
+
+        if (fSegment == 0)
+            y = Geom.range(r1y, r1y + r1height, y);
+        if (fSegment == line.pointCount()-2)
+            y = Geom.range(r2y, r2y + r2height, y);
+        return y;
+    }
+
+    private LineConnection ownerConnection() {
+        return (LineConnection)owner();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ElbowTextLocator.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,21 @@
+/*
+ * @(#)ElbowTextLocator.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+public class ElbowTextLocator extends AbstractLocator {
+    public Point locate(Figure owner) {
+        Point p = owner.center();
+        Rectangle r = owner.displayBox();
+        return new Point(p.x, p.y-10); // hack
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/EllipseFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,124 @@
+/*
+ * @(#)EllipseFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.util.Vector;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+/**
+ * An ellipse figure.
+ */
+public class EllipseFigure extends AttributeFigure {
+
+    private Rectangle   fDisplayBox;
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = -6856203289355118951L;
+    private int ellipseFigureSerializedDataVersion = 1;
+
+    public EllipseFigure() {
+        this(new Point(0,0), new Point(0,0));
+    }
+
+    public EllipseFigure(Point origin, Point corner) {
+        basicDisplayBox(origin,corner);
+    }
+
+    public Vector handles() {
+        Vector handles = new Vector();
+        BoxHandleKit.addHandles(this, handles);
+        return handles;
+    }
+
+    public void basicDisplayBox(Point origin, Point corner) {
+        fDisplayBox = new Rectangle(origin);
+        fDisplayBox.add(corner);
+    }
+
+    public Rectangle displayBox() {
+        return new Rectangle(
+            fDisplayBox.x,
+            fDisplayBox.y,
+            fDisplayBox.width,
+            fDisplayBox.height);
+    }
+
+    protected void basicMoveBy(int x, int y) {
+        fDisplayBox.translate(x,y);
+    }
+
+    public void drawBackground(Graphics g) {
+        Rectangle r = displayBox();
+        g.fillOval(r.x, r.y, r.width, r.height);
+    }
+
+    public void drawFrame(Graphics g) {
+        Rectangle r = displayBox();
+        g.drawOval(r.x, r.y, r.width-1, r.height-1);
+    }
+
+    public Insets connectionInsets() {
+        Rectangle r = fDisplayBox;
+        int cx = r.width/2;
+        int cy = r.height/2;
+        return new Insets(cy, cx, cy, cx);
+    }
+
+    public Connector connectorAt(int x, int y) {
+        return new ChopEllipseConnector(this);
+    }
+
+    public void write(StorableOutput dw) {
+        super.write(dw);
+        dw.writeInt(fDisplayBox.x);
+        dw.writeInt(fDisplayBox.y);
+        dw.writeInt(fDisplayBox.width);
+        dw.writeInt(fDisplayBox.height);
+    }
+
+    public String getMap() {
+	String sense = (String)getAttribute("Sensitive");
+	if (sense != null && sense.length() > 0) {
+		try {
+			sense = URLDecoder.decode(sense);
+		} catch (Exception e) {}
+	    Rectangle box = displayBox();
+	    double w = box.width / 2.0;
+	    double h = box.height / 2.0;
+	    double ang = (box.height > box.width) ? Math.PI / 2 : 0;
+	    Point c = center();
+	    String coords = "";
+	    for (int i = 0; i < 6; i++) {
+		if (i > 0)
+		    coords += ",";
+		int x = (int)(c.x + Math.cos(ang) * w);
+		int y = (int)(c.y + Math.sin(ang) * h);
+		coords += x + "," + y;
+		ang += Math.PI / 3;
+	    }
+	    return "<area shape=\"poly\" coords=\"" + coords +
+		"\" href=\"" +
+		sense + "\" />\n";
+	}
+	return "";
+    }
+
+    public void read(StorableInput dr) throws IOException {
+        super.read(dr);
+        fDisplayBox = new Rectangle(
+            dr.readInt(),
+            dr.readInt(),
+            dr.readInt(),
+            dr.readInt());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/FontSizeHandle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,45 @@
+/*
+ * @(#)FontSizeHandle.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+/**
+ * A Handle to change the font size by direct manipulation.
+ */
+public class FontSizeHandle extends LocatorHandle {
+
+    private Font    fFont;
+    private int     fSize;
+
+    public FontSizeHandle(Figure owner, Locator l) {
+        super(owner, l);
+    }
+
+    public void invokeStart(int  x, int  y, DrawingView view) {
+        TextFigure textOwner = (TextFigure) owner();
+        fFont = textOwner.getFont();
+        fSize = fFont.getSize();
+    }
+
+    public void invokeStep (int x, int y, int anchorX, int anchorY, DrawingView view) {
+        TextFigure textOwner = (TextFigure) owner();
+        int newSize = fSize + y-anchorY;
+        textOwner.setFont(new Font(fFont.getName(), fFont.getStyle(), newSize) );
+    }
+
+    public void draw(Graphics g) {
+        Rectangle r = displayBox();
+
+        g.setColor(Color.yellow);
+        g.fillOval(r.x, r.y, r.width, r.height);
+
+        g.setColor(Color.black);
+        g.drawOval(r.x, r.y, r.width, r.height);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/GroupCommand.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,52 @@
+/*
+ * @(#)GroupCommand.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.util.*;
+import CH.ifa.draw.util.Command;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+/**
+ * Command to group the selection into a GroupFigure.
+ *
+ * @see GroupFigure
+ */
+public  class GroupCommand extends Command {
+
+    private DrawingView fView;
+
+   /**
+    * Constructs a group command.
+    * @param name the command name
+    * @param view the target view
+    */
+    public GroupCommand(String name, DrawingView view) {
+        super(name);
+        fView = view;
+    }
+
+    public void execute() {
+        Vector selected = fView.selectionZOrdered();
+        Drawing drawing = fView.drawing();
+        if (selected.size() > 0) {
+            fView.clearSelection();
+            drawing.orphanAll(selected);
+
+            GroupFigure group = new GroupFigure();
+            group.addAll(selected);
+            fView.addToSelection(drawing.add(group));
+        }
+        fView.checkDamage();
+    }
+
+    public boolean isExecutable() {
+        return fView.selectionCount() > 0;
+    }
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/GroupFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,110 @@
+/*
+ * @(#)GroupFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+//import java.awt.geom.*;
+import java.util.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+/**
+ * A Figure that groups a collection of figures.
+ */
+public  class GroupFigure extends CompositeFigure {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 8311226373023297933L;
+    private int groupFigureSerializedDataVersion = 1;
+
+   /**
+    * GroupFigures cannot be connected
+    */
+    public boolean canConnect() {
+        return false;
+    }
+
+   /**
+    * Gets the display box. The display box is defined as the union
+    * of the contained figures.
+    */
+    public Rectangle displayBox() {
+        FigureEnumeration k = figures();
+        Rectangle r = k.nextFigure().displayBox();
+
+        while (k.hasMoreElements())
+            r.add(k.nextFigure().displayBox());
+        return r;
+    }
+
+    /**
+     * Transform all components proportionally.
+     * Note that because components are position at integer
+     * locations, we may get some odd-looking results!<p>
+     * @author Crawford Currie, Motorola
+     */
+    public void basicDisplayBox(Point origin, Point corner) {
+	Rectangle srcr = displayBox();
+	Rectangle dstr = new Rectangle(
+	    origin.x, origin.y,
+	    corner.x - origin.x, corner.y - origin.y);
+
+	if (srcr.equals(dstr) ||
+	    corner.x <= origin.x || corner.y <= origin.y)
+	    return;
+
+	// Scaling transform
+	double xtx = (double)(corner.x - origin.x) / srcr.width;
+	double ytx = (double)(corner.y - origin.y) / srcr.height;
+
+        FigureEnumeration k = figures();
+
+        while (k.hasMoreElements()) {
+	    Figure child = k.nextFigure();
+	    Rectangle sr = child.displayBox();
+	    Point childOrigin = new Point(
+		(int)Math.round(dstr.x + (sr.x - srcr.x) * xtx),
+		(int)Math.round(dstr.y + (sr.y - srcr.y) * ytx));
+	    Point childCorner = new Point(
+		(int)Math.round(childOrigin.x + sr.width * xtx),
+		(int)Math.round(childOrigin.y + sr.height * ytx));
+	    child.displayBox(childOrigin, childCorner);
+	}
+    }
+
+    public FigureEnumeration decompose() {
+        return new FigureEnumerator(fFigures);
+    }
+
+   /**
+    * Gets the handles for the GroupFigure.
+    */
+    public Vector handles() {
+        Vector handles = new Vector();
+	// Handles changed to standard handles to support group scaling
+	// Crawford Currie, Motorola
+	BoxHandleKit.addCornerHandles(this, handles);
+	/* Was:
+	handles.addElement(new GroupHandle(this, RelativeLocator.northWest()));
+        handles.addElement(new GroupHandle(this, RelativeLocator.northEast()));
+        handles.addElement(new GroupHandle(this, RelativeLocator.southWest()));
+        handles.addElement(new GroupHandle(this, RelativeLocator.southEast()));
+	*/
+        return handles;
+    }
+
+   /**
+    * Sets the attribute of all the contained figures.
+    */
+    public void setAttribute(String name, Object value) {
+        super.setAttribute(name, value);
+        FigureEnumeration k = figures();
+        while (k.hasMoreElements())
+            k.nextFigure().setAttribute(name, value);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/GroupHandle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,33 @@
+/*
+ * @(#)GroupHandle.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.NullHandle;
+
+/**
+ * A Handle for a GroupFigure.
+ */
+final class GroupHandle extends NullHandle {
+
+    public GroupHandle(Figure owner, Locator locator) {
+        super(owner, locator);
+    }
+
+    /**
+     * Draws the Group handle.
+     */
+    public void draw(Graphics g) {
+        Rectangle r = displayBox();
+
+        g.setColor(Color.black);
+        g.drawRect(r.x, r.y, r.width, r.height);
+        r.grow(-1,-1);
+        g.setColor(Color.white);
+        g.drawRect(r.x, r.y, r.width, r.height);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ImageFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,132 @@
+/*
+ * @(#)ImageFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.io.*;
+import java.util.Vector;
+import java.awt.image.ImageObserver;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * A Figure that shows an Image.
+ * Images shown by an image figure are shared by using the Iconkit.
+ * @see Iconkit
+ */
+public  class ImageFigure
+        extends AttributeFigure implements ImageObserver {
+
+    private String   fFileName;
+    private transient Image fImage;
+    private Rectangle fDisplayBox;
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 148012030121282439L;
+    private int imageFigureSerializedDataVersion = 1;
+
+    public ImageFigure() {
+        fFileName = null;
+        fImage = null;
+        fDisplayBox = null;
+    }
+
+    public ImageFigure(Image image, String fileName, Point origin) {
+        fFileName = fileName;
+        fImage = image;
+        fDisplayBox = new Rectangle(origin.x, origin.y, 0, 0);
+        fDisplayBox.width = fImage.getWidth(this);
+        fDisplayBox.height = fImage.getHeight(this);
+    }
+
+    public void basicDisplayBox(Point origin, Point corner) {
+        fDisplayBox = new Rectangle(origin);
+        fDisplayBox.add(corner);
+    }
+
+    public Vector handles() {
+        Vector handles = new Vector();
+        BoxHandleKit.addHandles(this, handles);
+        return handles;
+    }
+
+    public Rectangle displayBox() {
+        return new Rectangle(
+            fDisplayBox.x,
+            fDisplayBox.y,
+            fDisplayBox.width,
+            fDisplayBox.height);
+    }
+
+    protected void basicMoveBy(int x, int y) {
+        fDisplayBox.translate(x,y);
+    }
+
+    public void draw(Graphics g) {
+        if (fImage == null)
+            fImage = Iconkit.instance().getImage(fFileName);
+        if (fImage != null)
+            g.drawImage(fImage, fDisplayBox.x, fDisplayBox.y, fDisplayBox.width, fDisplayBox.height, this);
+        else
+            drawGhost(g);
+    }
+
+    private void drawGhost(Graphics g) {
+        g.setColor(Color.gray);
+        g.fillRect(fDisplayBox.x, fDisplayBox.y, fDisplayBox.width, fDisplayBox.height);
+    }
+
+   /**
+    * Handles asynchroneous image updates.
+    */
+    public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
+	    if ((flags & (FRAMEBITS|ALLBITS)) != 0) {
+	        invalidate();
+	        if (listener() != null)
+	            listener().figureRequestUpdate(new FigureChangeEvent(this));
+	    }
+	    return (flags & (ALLBITS|ABORT)) == 0;
+    }
+
+   /**
+    * Writes the ImageFigure to a StorableOutput. Only a reference to the
+    * image, that is its pathname is saved.
+    */
+    public void write(StorableOutput dw) {
+        super.write(dw);
+        dw.writeInt(fDisplayBox.x);
+        dw.writeInt(fDisplayBox.y);
+        dw.writeInt(fDisplayBox.width);
+        dw.writeInt(fDisplayBox.height);
+        dw.writeString(fFileName);
+    }
+
+   /**
+    * Reads the ImageFigure from a StorableInput. It registers the
+    * referenced figure to be loaded from the Iconkit.
+    * @see Iconkit#registerImage
+    */
+    public void read(StorableInput dr) throws IOException {
+        super.read(dr);
+        fDisplayBox = new Rectangle(
+            dr.readInt(),
+            dr.readInt(),
+            dr.readInt(),
+            dr.readInt());
+        fFileName = dr.readString();
+        Iconkit.instance().registerImage(fFileName);
+    }
+
+    private void readObject(ObjectInputStream s)
+        throws ClassNotFoundException, IOException {
+
+        s.defaultReadObject();
+        Iconkit.instance().registerImage(fFileName);
+        fImage = null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/InsertImageCommand.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,46 @@
+/*
+ * @(#)InsertImageCommand.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.util.*;
+import java.awt.*;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+/**
+ * Command to insert a named image.
+ */
+public class InsertImageCommand extends Command {
+
+    private DrawingView  fView;
+    private String       fImage;
+
+   /**
+    * Constructs an insert image command.
+    * @param name the command name
+    * @param image the pathname of the image
+    * @param view the target view
+    */
+    public InsertImageCommand(String name, String image, DrawingView view) {
+        super(name);
+        fImage = image;
+        fView = view;
+    }
+
+    public void execute() {
+        // ugly cast to component, but AWT wants and Component instead of an ImageObserver...
+        Image image = Iconkit.instance().registerAndLoadImage((Component)fView, fImage);
+        ImageFigure figure = new ImageFigure(image, fImage, fView.lastClick());
+        fView.add(figure);
+        fView.clearSelection();
+        fView.addToSelection(figure);
+        fView.checkDamage();
+    }
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/LineConnection.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,302 @@
+/*
+ * @(#)LineConnection.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.util.*;
+import java.io.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * A LineConnection is a standard implementation of the
+ * ConnectionFigure interface. The interface is implemented with PolyLineFigure.
+ * @see ConnectionFigure
+ */
+public  class LineConnection extends PolyLineFigure implements ConnectionFigure {
+
+    protected Connector    fStart = null;
+    protected Connector    fEnd = null;
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 6883731614578414801L;
+    private int lineConnectionSerializedDataVersion = 1;
+
+    /**
+     * Constructs a LineConnection. A connection figure has
+     * an arrow decoration at the start and end.
+     */
+    public LineConnection() {
+        super(4);
+        setStartDecoration(new ArrowTip());
+        setEndDecoration(new ArrowTip());
+    }
+
+    /**
+     * Tests whether a figure can be a connection target.
+     * ConnectionFigures cannot be connected and return false.
+     */
+    public boolean canConnect() {
+        return false;
+    }
+
+    /**
+     * Ensures that a connection is updated if the connection
+     * was moved.
+     */
+    protected void basicMoveBy(int dx, int dy) {
+        // don't move the start and end point since they are connected
+        for (int i = 1; i < fPoints.size()-1; i++)
+            ((Point) fPoints.elementAt(i)).translate(dx, dy);
+
+        updateConnection(); // make sure that we are still connected
+    }
+
+    /**
+     * Sets the start figure of the connection.
+     */
+    public void connectStart(Connector start) {
+        fStart = start;
+        startFigure().addFigureChangeListener(this);
+    }
+
+    /**
+     * Sets the end figure of the connection.
+     */
+    public void connectEnd(Connector end) {
+        fEnd = end;
+        endFigure().addFigureChangeListener(this);
+        handleConnect(startFigure(), endFigure());
+    }
+
+    /**
+     * Disconnects the start figure.
+     */
+    public void disconnectStart() {
+        startFigure().removeFigureChangeListener(this);
+        fStart = null;
+    }
+
+    /**
+     * Disconnects the end figure.
+     */
+    public void disconnectEnd() {
+        handleDisconnect(startFigure(), endFigure());
+        endFigure().removeFigureChangeListener(this);
+        fEnd = null;
+    }
+
+    /**
+     * Tests whether a connection connects the same figures
+     * as another ConnectionFigure.
+     */
+    public boolean connectsSame(ConnectionFigure other) {
+        return other.start() == start() && other.end() == end();
+    }
+
+    /**
+     * Handles the disconnection of a connection.
+     * Override this method to handle this event.
+     */
+    protected void handleDisconnect(Figure start, Figure end) {}
+
+    /**
+     * Handles the connection of a connection.
+     * Override this method to handle this event.
+     */
+    protected void handleConnect(Figure start, Figure end) {}
+
+    /**
+     * Gets the start figure of the connection.
+     */
+    public Figure startFigure() {
+        if (start() != null)
+            return start().owner();
+        return null;
+    }
+
+    /**
+     * Gets the end figure of the connection.
+     */
+    public Figure endFigure() {
+        if (end() != null)
+            return end().owner();
+        return null;
+    }
+
+    /**
+     * Gets the start figure of the connection.
+     */
+    public Connector start() {
+        return fStart;
+    }
+
+    /**
+     * Gets the end figure of the connection.
+     */
+    public Connector end() {
+        return fEnd;
+    }
+
+    /**
+     * Tests whether two figures can be connected.
+     */
+    public boolean canConnect(Figure start, Figure end) {
+        return true;
+    }
+
+    /**
+     * Sets the start point.
+     */
+    public void startPoint(int x, int y) {
+        willChange();
+        if (fPoints.size() == 0)
+            fPoints.addElement(new Point(x, y));
+        else
+            fPoints.setElementAt(new Point(x, y), 0);
+        changed();
+    }
+
+    /**
+     * Sets the end point.
+     */
+    public void endPoint(int x, int y) {
+        willChange();
+        if (fPoints.size() < 2)
+            fPoints.addElement(new Point(x, y));
+        else
+            fPoints.setElementAt(new Point(x, y), fPoints.size()-1);
+        changed();
+    }
+
+    /**
+     * Gets the start point.
+     */
+    public Point startPoint(){
+        Point p = (Point)fPoints.firstElement();
+        return new Point(p.x, p.y);
+    }
+
+    /**
+     * Gets the end point.
+     */
+    public Point endPoint() {
+        Point p = (Point)fPoints.lastElement();
+        return new Point(p.x, p.y);
+    }
+
+    /**
+     * Gets the handles of the figure. It returns the normal
+     * PolyLineHandles but adds ChangeConnectionHandles at the
+     * start and end.
+     */
+    public Vector handles() {
+        Vector handles = new Vector(fPoints.size());
+        handles.addElement(new ChangeConnectionStartHandle(this));
+        for (int i = 1; i < fPoints.size()-1; i++)
+            handles.addElement(new PolyLineHandle(this, locator(i), i));
+        handles.addElement(new ChangeConnectionEndHandle(this));
+        return handles;
+    }
+
+    /**
+     * Sets the point and updates the connection.
+     */
+    public void setPointAt(Point p, int i) {
+        super.setPointAt(p, i);
+        layoutConnection();
+    }
+
+    /**
+     * Inserts the point and updates the connection.
+     */
+    public void insertPointAt(Point p, int i) {
+        super.insertPointAt(p, i);
+        layoutConnection();
+    }
+
+    /**
+     * Removes the point and updates the connection.
+     */
+    public void removePointAt(int i) {
+        super.removePointAt(i);
+        layoutConnection();
+    }
+
+    /**
+     * Updates the connection.
+     */
+    public void updateConnection() {
+        if (fStart != null) {
+            Point start = fStart.findStart(this);
+            startPoint(start.x, start.y);
+        }
+        if (fEnd != null) {
+            Point end = fEnd.findEnd(this);
+            endPoint(end.x, end.y);
+        }
+    }
+
+    /**
+     * Lays out the connection. This is called when the connection
+     * itself changes. By default the connection is recalculated
+     */
+    public void layoutConnection() {
+        updateConnection();
+    }
+
+    public void figureChanged(FigureChangeEvent e) {
+        updateConnection();
+    }
+
+    public void figureRemoved(FigureChangeEvent e) {
+        if (listener() != null)
+            listener().figureRequestRemove(new FigureChangeEvent(this));
+    }
+
+    public void figureRequestRemove(FigureChangeEvent e) {}
+    public void figureInvalidated(FigureChangeEvent e) {}
+    public void figureRequestUpdate(FigureChangeEvent e) {}
+
+    public void release() {
+        super.release();
+        handleDisconnect(startFigure(), endFigure());
+        if (fStart != null) startFigure().removeFigureChangeListener(this);
+        if (fEnd   != null) endFigure().removeFigureChangeListener(this);
+    }
+
+    public void write(StorableOutput dw) {
+        super.write(dw);
+        dw.writeStorable(fStart);
+        dw.writeStorable(fEnd);
+    }
+
+    public void read(StorableInput dr) throws IOException {
+        super.read(dr);
+        Connector start = (Connector)dr.readStorable();
+        if (start != null)
+            connectStart(start);
+        Connector end = (Connector)dr.readStorable();
+        if (end != null)
+            connectEnd(end);
+        if (start != null && end != null)
+            updateConnection();
+    }
+
+    private void readObject(ObjectInputStream s)
+        throws ClassNotFoundException, IOException {
+
+        s.defaultReadObject();
+
+        if (fStart != null)
+            connectStart(fStart);
+        if (fEnd != null)
+            connectEnd(fEnd);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/LineDecoration.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,25 @@
+/*
+ * @(#)LineDecoration.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.io.Serializable;
+
+import CH.ifa.draw.util.Storable;
+
+/**
+ * Decorate the start or end point of a line or poly line figure.
+ * LineDecoration is the base class for the different line decorations.
+ * @see PolyLineFigure
+ */
+public interface LineDecoration
+                extends Storable, Cloneable, Serializable {
+
+    /**
+     * Draws the decoration in the direction specified by the two points.
+     */
+    public abstract void draw(Graphics g, int x1, int y1, int x2, int y2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/LineFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,73 @@
+/*
+ * @(#)LineFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.util.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * A line figure.
+ */
+public  class LineFigure extends PolyLineFigure {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 511503575249212371L;
+    private int lineFigureSerializedDataVersion = 1;
+
+    /**
+     * Constructs a LineFigure with both start and end set to Point(0,0).
+     */
+    public LineFigure() {
+        addPoint(0, 0);
+        addPoint(0, 0);
+    }
+
+    /**
+     * Gets a copy of the start point.
+     */
+    public Point startPoint() {
+        return pointAt(0);
+    }
+
+    /**
+     * Gets a copy of the end point.
+     */
+    public Point endPoint() {
+        return pointAt(1);
+    }
+
+    /**
+     * Sets the start point.
+     */
+    public void  startPoint(int x, int y) {
+        setPointAt(new Point(x,y), 0);
+    }
+
+    /**
+     * Sets the end point.
+     */
+    public void  endPoint(int x, int y) {
+        setPointAt(new Point(x,y), 1);
+    }
+
+    /**
+     * Sets the start and end point.
+     */
+    public void setPoints(Point start, Point end) {
+        setPointAt(start, 0);
+        setPointAt(end, 1);
+    }
+
+    public void basicDisplayBox(Point origin, Point corner) {
+        setPoints(origin, corner);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/NumberTextFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,58 @@
+/*
+ * @(#)NumberTextFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.util.*;
+import java.awt.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+
+/**
+ * A TextFigure specialized to edit numbers.
+ */
+public  class NumberTextFigure extends TextFigure {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = -4056859232918336475L;
+    private int numberTextFigureSerializedDataVersion = 1;
+
+    /**
+     * Gets the number of columns to be used by the text overlay.
+     * @see FloatingTextField
+     */
+    public int overlayColumns() {
+        return Math.max(4, getText().length());
+    }
+
+    public int overlayRows() {
+	return 1;
+    }
+
+    /**
+     * Gets the numerical value of the contained text.
+     * return the value or 0 in the case of an illegal number format.
+     */
+    public int getValue() {
+        int value = 0;
+        try {
+            value = Integer.parseInt(getText());
+        } catch (NumberFormatException e) {
+            value = 0;
+        }
+        return value;
+    }
+
+    /**
+     * Sets the numberical value of the contained text.
+     */
+    public void setValue(int value) {
+        setText(Integer.toString(value));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/PolyLineConnector.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,84 @@
+/*
+ * @(#)PolyLineConnector.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * PolyLineConnector finds connection points on a
+ * PolyLineFigure.
+ *
+ * @see PolyLineFigure
+ */
+public class PolyLineConnector extends ChopBoxConnector {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 6018435940519102865L;
+
+    public PolyLineConnector() {
+        super();
+    }
+
+    /**
+     * Constructs a connector with the given owner figure.
+     */
+    public PolyLineConnector(Figure owner) {
+        super(owner);
+    }
+
+    protected Point chop(Figure target, Point from) {
+        PolyLineFigure p = (PolyLineFigure)owner();
+        // *** based on PolygonFigure's heuristic
+        Point ctr = p.center();
+        int cx = -1;
+        int cy = -1;
+        long len = Long.MAX_VALUE;
+
+        // Try for points along edge
+
+        for (int i = 0; i < p.pointCount()-1; i++) {
+            Point p1 = p.pointAt(i);
+            Point p2 = p.pointAt(i+1);
+            Point chop = Geom.intersect(p1.x,
+                                 p1.y,
+                                 p2.x,
+                                 p2.y,
+                                 from.x,
+                                 from.y,
+                                 ctr.x,
+                                 ctr.y);
+            if (chop != null) {
+                long cl = Geom.length2(chop.x, chop.y, from.x, from.y);
+                if (cl < len) {
+                    len = cl;
+                    cx = chop.x;
+                    cy = chop.y;
+                }
+            }
+        }
+        // if none found, pick closest vertex
+        //if (len ==  Long.MAX_VALUE) {
+        { // try anyway
+            for (int i = 0; i < p.pointCount(); i++) {
+                Point pp = p.pointAt(i);
+                long l = Geom.length2(pp.x, pp.y, from.x, from.y);
+                if (l < len) {
+                    len = l;
+                    cx = pp.x;
+                    cy = pp.y;
+                }
+            }
+        }
+        return new Point(cx, cy);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/PolyLineFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,385 @@
+/*
+ * @(#)PolyLineFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * A poly line figure consists of a list of points.
+ * It has an optional line decoration at the start and end.
+ *
+ * @see LineDecoration
+ */
+public  class PolyLineFigure extends AbstractFigure {
+
+    public final static int ARROW_TIP_NONE  = 0;
+    public final static int ARROW_TIP_START = 1;
+    public final static int ARROW_TIP_END   = 2;
+    public final static int ARROW_TIP_BOTH  = 3;
+    
+    protected Vector              fPoints;
+    protected LineDecoration      fStartDecoration = null;
+    protected LineDecoration      fEndDecoration = null;
+    protected Color               fFrameColor = Color.black;
+    
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = -7951352179906577773L;
+    private int polyLineFigureSerializedDataVersion = 1;
+    
+    public PolyLineFigure() {
+        fPoints = new Vector(4);
+    }
+    
+    public PolyLineFigure(int size) {
+        fPoints = new Vector(size);
+    }
+    
+    public PolyLineFigure(int x, int y) {
+	fPoints = new Vector();
+	fPoints.addElement(new Point(x, y));
+    }
+    
+    public Rectangle displayBox() {
+        Enumeration k = points();
+        Rectangle r = new Rectangle((Point) k.nextElement());
+	
+        while (k.hasMoreElements())
+            r.add((Point) k.nextElement());
+	
+        return r;
+    }
+    
+    public boolean isEmpty() {
+        return (size().width < 3) && (size().height < 3);
+    }
+    
+    public Vector handles() {
+        Vector handles = new Vector(fPoints.size());
+        for (int i = 0; i < fPoints.size(); i++)
+            handles.addElement(new PolyLineHandle(this, locator(i), i));
+        return handles;
+    }
+    
+    public void basicDisplayBox(Point origin, Point corner) {
+    }
+    
+    /**
+     * Adds a node to the list of points.
+     */
+    public void addPoint(int x, int y) {
+        fPoints.addElement(new Point(x, y));
+        changed();
+    }
+    
+    public Enumeration points() {
+        return fPoints.elements();
+    }
+    
+    public int pointCount() {
+        return fPoints.size();
+    }
+
+    protected void basicMoveBy(int dx, int dy) {
+        Enumeration k = fPoints.elements();
+        while (k.hasMoreElements())
+            ((Point) k.nextElement()).translate(dx, dy);
+    }
+
+    /**
+     * Changes the position of a node.
+     */
+    public void setPointAt(Point p, int i) {
+        willChange();
+        fPoints.setElementAt(p, i);
+        changed();
+    }
+
+    /**
+     * Insert a node at the given point.
+     */
+    public void insertPointAt(Point p, int i) {
+        fPoints.insertElementAt(p, i);
+        changed();
+    }
+
+    public void removePointAt(int i) {
+        willChange();
+        fPoints.removeElementAt(i);
+        changed();
+    }
+
+    /**
+     * Splits the segment at the given point if a segment was hit.
+     * @return the index of the segment or -1 if no segment was hit.
+     */
+    public int splitSegment(int x, int y) {
+        int i = findSegment(x, y);
+        if (i != -1)
+            insertPointAt(new Point(x, y), i+1);
+        return i+1;
+    }
+
+    public Point pointAt(int i) {
+        return (Point)fPoints.elementAt(i);
+    }
+
+    /**
+     * Joins to segments into one if the given point hits a node
+     * of the polyline.
+     * @return true if the two segments were joined.
+     */
+    public boolean joinSegments(int x, int y) {
+        for (int i= 1; i < fPoints.size()-1; i++) {
+            Point p = pointAt(i);
+            if (Geom.length(x, y, p.x, p.y) < 3) {
+                removePointAt(i);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Connector connectorAt(int x, int y) {
+        return new PolyLineConnector(this);
+    }
+
+    /**
+     * Sets the start decoration.
+     */
+    public void setStartDecoration(LineDecoration l) {
+        fStartDecoration = l;
+    }
+
+    /**
+     * Sets the end decoration.
+     */
+    public void setEndDecoration(LineDecoration l) {
+        fEndDecoration = l;
+    }
+
+    public void draw(Graphics g, boolean showGuides) {
+        g.setColor(getFrameColor());
+        Point p1, p2;
+        for (int i = 0; i < fPoints.size()-1; i++) {
+            p1 = (Point) fPoints.elementAt(i);
+            p2 = (Point) fPoints.elementAt(i+1);
+            g.drawLine(p1.x, p1.y, p2.x, p2.y);
+        }
+        decorate(g);
+	if (showGuides) {
+	    drawURL(g);
+	}
+    }
+
+    private void drawURL(Graphics g) {
+	String sensitive = (String)getAttribute("Sensitive");
+	if (sensitive == null || sensitive.length() == 0)
+	    return;
+	int i;
+	if (fPoints.size() < 3)
+	    i = 0;
+	else
+	    i =  fPoints.size() / 2 - 1;
+	Point p1, p2;
+
+	if ((fPoints.size() & 1) == 1) {
+	    p2 = p1 = (Point) fPoints.elementAt(i+1);
+	} else {
+	    p1 = (Point) fPoints.elementAt(i);
+	    p2 = (Point) fPoints.elementAt(i+1);
+	}
+	g.setColor(Color.red);
+	g.setFont(dialogFont);
+	g.drawString("url=" + sensitive, (p1.x + p2.x) / 2,
+		     (p1.y + p2.y) / 2);
+    }
+
+    public boolean containsPoint(int x, int y) {
+        Rectangle bounds = displayBox();
+        bounds.grow(4,4);
+        if (!bounds.contains(x, y))
+            return false;
+
+        Point p1, p2;
+        for (int i = 0; i < fPoints.size()-1; i++) {
+            p1 = (Point) fPoints.elementAt(i);
+            p2 = (Point) fPoints.elementAt(i+1);
+            if (Geom.lineContainsPoint(p1.x, p1.y, p2.x, p2.y, x, y))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Gets the segment of the polyline that is hit by
+     * the given point.
+     * @return the index of the segment or -1 if no segment was hit.
+     */
+    public int findSegment(int x, int y) {
+        Point p1, p2;
+        for (int i = 0; i < fPoints.size()-1; i++) {
+            p1 = (Point) fPoints.elementAt(i);
+            p2 = (Point) fPoints.elementAt(i+1);
+            if (Geom.lineContainsPoint(p1.x, p1.y, p2.x, p2.y, x, y))
+                return i;
+        }
+        return -1;
+    }
+
+    private void decorate(Graphics g) {
+        if (fStartDecoration != null) {
+            Point p1 = (Point)fPoints.elementAt(0);
+            Point p2 = (Point)fPoints.elementAt(1);
+            fStartDecoration.draw(g, p1.x, p1.y, p2.x, p2.y);
+        }
+        if (fEndDecoration != null) {
+            Point p3 = (Point)fPoints.elementAt(fPoints.size()-2);
+            Point p4 = (Point)fPoints.elementAt(fPoints.size()-1);
+            fEndDecoration.draw(g, p4.x, p4.y, p3.x, p3.y);
+        }
+    }
+
+    /**
+     * Gets the attribute with the given name.
+     * PolyLineFigure maps "ArrowMode"to a
+     * line decoration.
+     */
+    public Object getAttribute(String name) {
+        if (name.equals("FrameColor")) {
+            return getFrameColor();
+        } else if (name.equals("ArrowMode")) {
+            int value = 0;
+            if (fStartDecoration != null)
+                value |= ARROW_TIP_START;
+            if (fEndDecoration != null)
+                value |= ARROW_TIP_END;
+            return new Integer(value);
+	}
+        return super.getAttribute(name);
+    }
+
+    /**
+     * Sets the attribute with the given name.
+     * PolyLineFigure interprets "ArrowMode"to set
+     * the line decoration.
+     */
+    public void setAttribute(String name, Object value) {
+        if (name.equals("FrameColor")) {
+            setFrameColor((Color)value);
+            changed();
+        } else if (name.equals("ArrowMode")) {
+            Integer intObj = (Integer) value;
+            if (intObj != null) {
+                int decoration = intObj.intValue();
+                if ((decoration & ARROW_TIP_START) != 0)
+                    fStartDecoration = new ArrowTip();
+                else
+                    fStartDecoration = null;
+                if ((decoration & ARROW_TIP_END) != 0)
+                    fEndDecoration = new ArrowTip();
+                else
+                    fEndDecoration = null;
+            }
+            changed();
+	} else {
+            super.setAttribute(name, value);
+	    changed();
+	}
+    }
+
+    public void write(StorableOutput dw) {
+        super.write(dw);
+        dw.writeInt(fPoints.size());
+        Enumeration k = fPoints.elements();
+        while (k.hasMoreElements()) {
+            Point p = (Point) k.nextElement();
+            dw.writeInt(p.x);
+            dw.writeInt(p.y);
+        }
+        dw.writeStorable(fStartDecoration);
+        dw.writeStorable(fEndDecoration);
+        dw.writeColor(fFrameColor);
+    }
+
+    public String getMap() {
+	String sensitive = (String)getAttribute("Sensitive");
+	if (sensitive == null || sensitive.length() == 0)
+	    return "";
+	try {
+		sensitive = URLDecoder.decode(sensitive);
+	} catch (Exception e) {}
+	Enumeration k = fPoints.elements();
+	boolean first  = true;
+	String forwards = "", backwards = "";
+        for (int i = 1; i < fPoints.size(); i++) {
+            Point p1 = (Point)fPoints.elementAt(i-1);
+            Point p2 = (Point)fPoints.elementAt(i);
+	    double l = Math.sqrt((p2.x-p1.x) * (p2.x-p1.x) +
+				 (p2.y-p1.y) * (p2.y-p1.y));
+	    int x = (int)(4 * (p2.y-p1.y) / l);
+	    int y = (int)(4 * (p2.x-p1.x) / l);
+	    if (!first) {
+		forwards += ",";
+		backwards = "," + backwards;
+	    }
+	    first = false;
+	    forwards += (p1.x + x) + "," + (p1.y + y) + "," +
+		(p2.x + x) + "," + (p2.y + y);
+	    backwards = (p2.x - x) + "," + (p2.y - y) + "," +
+		(p1.x - x) + "," + (p1.y - y) + backwards;
+        }
+	return "<area shape=\"poly\" coords=\"" +
+	    forwards + "," + backwards + "\" href=\"" +
+	    sensitive + "\" />\n";
+    }
+
+    public void read(StorableInput dr) throws IOException {
+	// test for the next token being a number to allow us to
+	// skip attribute reading which. Old-format files don't
+	// have attributes on connections.
+	super.read(dr);
+        int size = dr.readInt();
+	if (size == -1) {
+	    // Temp for files created during devt. Remove when we're sure
+	    // they are all gone
+	    String s = dr.readString();
+	    setAttribute("Sensitive", s);
+	    size = dr.readInt();
+	}
+        fPoints = new Vector(size);
+        for (int i=0; i<size; i++) {
+            int x = dr.readInt();
+            int y = dr.readInt();
+            fPoints.addElement(new Point(x,y));
+        }
+        fStartDecoration = (LineDecoration)dr.readStorable();
+        fEndDecoration = (LineDecoration)dr.readStorable();
+        fFrameColor = dr.readColor();
+    }
+
+    /**
+     * Creates a locator for the point with the given index.
+     */
+    public static Locator locator(int pointIndex) {
+        return new PolyLineLocator(pointIndex);
+    }
+
+    protected Color getFrameColor() {
+        return fFrameColor;
+    }
+
+    protected void setFrameColor(Color c) {
+        fFrameColor = c;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/PolyLineHandle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,44 @@
+/*
+ * @(#)PolyLineHandle.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.LocatorHandle;
+
+/**
+ * A handle for a node on the polyline.
+ */
+public class PolyLineHandle extends LocatorHandle {
+
+    private int fIndex;
+    private Point fAnchor;
+
+   /**
+    * Constructs a poly line handle.
+    * @param owner the owning polyline figure.
+    * @l the locator
+    * @index the index of the node associated with this handle.
+    */
+    public PolyLineHandle(PolyLineFigure owner, Locator l, int index) {
+        super(owner, l);
+        fIndex = index;
+    }
+
+    public void invokeStart(int  x, int  y, DrawingView view) {
+        fAnchor = new Point(x, y);
+    }
+
+    public void invokeStep (int x, int y, int anchorX, int anchorY, DrawingView view) {
+        myOwner().setPointAt(new Point(x, y), fIndex);
+    }
+
+    private PolyLineFigure myOwner() {
+        return (PolyLineFigure)owner();
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/PolyLineLocator.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,34 @@
+/*
+ * @(#)PolyLineLocator.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * A poly line figure consists of a list of points.
+ * It has an optional line decoration at the start and end.
+ *
+ * @see LineDecoration
+ */
+class PolyLineLocator extends AbstractLocator {
+    int fIndex;
+
+    public PolyLineLocator(int index) {
+        fIndex = index;
+    }
+
+    public Point locate(Figure owner) {
+        PolyLineFigure plf = (PolyLineFigure)owner;
+        // guard against changing PolyLineFigures -> temporary hack
+        if (fIndex < plf.pointCount())
+            return ((PolyLineFigure)owner).pointAt(fIndex);
+        return new Point(0, 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/RadiusHandle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,58 @@
+/*
+ * @(#)RadiusHandle.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.Geom;
+
+/**
+ * A Handle to manipulate the radius of a round corner rectangle.
+ */
+class RadiusHandle extends AbstractHandle {
+
+    private Point fRadius;
+    private RoundRectangleFigure fOwner;
+    private static final int OFFSET = 4;
+
+    public RadiusHandle(RoundRectangleFigure owner) {
+        super(owner);
+        fOwner = owner;
+    }
+
+    public void invokeStart(int  x, int  y, DrawingView view) {
+        fRadius = fOwner.getArc();
+        fRadius.x = fRadius.x/2;
+        fRadius.y = fRadius.y/2;
+    }
+
+    public void invokeStep (int x, int y, int anchorX, int anchorY, DrawingView view) {
+        int dx = x-anchorX;
+        int dy = y-anchorY;
+        Rectangle r = fOwner.displayBox();
+        int rx = Geom.range(0, r.width, 2*(fRadius.x + dx));
+        int ry = Geom.range(0, r.height, 2*(fRadius.y + dy));
+        fOwner.setArc(rx, ry);
+    }
+
+    public Point locate() {
+        Point radius = fOwner.getArc();
+        Rectangle r = fOwner.displayBox();
+        return new Point(r.x+radius.x/2+OFFSET, r.y+radius.y/2+OFFSET);
+    }
+
+    public void draw(Graphics g) {
+        Rectangle r = displayBox();
+
+        g.setColor(Color.yellow);
+        g.fillOval(r.x, r.y, r.width, r.height);
+
+        g.setColor(Color.black);
+        g.drawOval(r.x, r.y, r.width, r.height);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/RectangleFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,106 @@
+/*
+ * @(#)RectangleFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.util.Vector;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+
+/**
+ * A rectangle figure.
+ */
+public class RectangleFigure extends AttributeFigure {
+
+    private Rectangle   fDisplayBox;
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 184722075881789163L;
+    private int rectangleFigureSerializedDataVersion = 1;
+
+    public RectangleFigure() {
+        this(new Point(0,0), new Point(0,0));
+    }
+
+    public RectangleFigure(Point origin, Point corner) {
+        basicDisplayBox(origin,corner);
+    }
+
+    public void basicDisplayBox(Point origin, Point corner) {
+        fDisplayBox = new Rectangle(origin);
+        fDisplayBox.add(corner);
+    }
+
+    public Vector handles() {
+        Vector handles = new Vector();
+        BoxHandleKit.addHandles(this, handles);
+        return handles;
+    }
+
+    public Rectangle displayBox() {
+        return new Rectangle(
+            fDisplayBox.x,
+            fDisplayBox.y,
+            fDisplayBox.width,
+            fDisplayBox.height);
+    }
+
+    protected void basicMoveBy(int x, int y) {
+        fDisplayBox.translate(x,y);
+    }
+
+    public void drawBackground(Graphics g) {
+        Rectangle r = displayBox();
+	g.fillRect(r.x, r.y, r.width, r.height);
+    }
+
+    public void drawFrame(Graphics g) {
+        Rectangle r = displayBox();
+	g.drawRect(r.x, r.y, r.width-1, r.height-1);
+    }
+
+    //-- store / load ----------------------------------------------
+
+    public void write(StorableOutput dw) {
+        super.write(dw);
+        dw.writeInt(fDisplayBox.x);
+        dw.writeInt(fDisplayBox.y);
+        dw.writeInt(fDisplayBox.width);
+        dw.writeInt(fDisplayBox.height);
+    }
+
+    public String getMap() {
+	String sense = (String)getAttribute("Sensitive");
+	if (sense != null && sense.length() > 0) {
+		try {
+			sense = URLDecoder.decode(sense);
+		} catch (Exception e) {}
+	    Rectangle box = displayBox();
+	    return "<area shape=\"rect\" coords=\"" +
+		box.x + "," + box.y + "," +
+		(box.x + box.width) + "," +
+		(box.y + box.height) +
+		"\" href=\"" + sense + "\" />\n";
+	}
+	return "";
+    }
+
+    public void read(StorableInput dr) throws IOException {
+        super.read(dr);
+        fDisplayBox = new Rectangle(
+            dr.readInt(),
+            dr.readInt(),
+            dr.readInt(),
+            dr.readInt());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/RoundRectangleFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,143 @@
+/*
+ * @(#)RoundRectangleFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.util.Vector;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+
+/**
+ * A round rectangle figure.
+ * @see RadiusHandle
+ */
+public  class RoundRectangleFigure extends AttributeFigure {
+
+    private Rectangle   fDisplayBox;
+    private int         fArcWidth;
+    private int         fArcHeight;
+    private static final int DEFAULT_ARC = 8;
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 7907900248924036885L;
+    private int roundRectangleSerializedDataVersion = 1;
+
+    public RoundRectangleFigure() {
+        this(new Point(0,0), new Point(0,0));
+        fArcWidth = fArcHeight = DEFAULT_ARC;
+    }
+
+    public RoundRectangleFigure(Point origin, Point corner) {
+        basicDisplayBox(origin,corner);
+        fArcWidth = fArcHeight = DEFAULT_ARC;
+    }
+
+    public void basicDisplayBox(Point origin, Point corner) {
+        fDisplayBox = new Rectangle(origin);
+        fDisplayBox.add(corner);
+    }
+
+    /**
+     * Sets the arc's witdh and height.
+     */
+    public void setArc(int width, int height) {
+        willChange();
+        fArcWidth = width;
+        fArcHeight = height;
+        changed();
+    }
+
+    /**
+     * Gets the arc's width and height.
+     */
+    public Point getArc() {
+        return new Point(fArcWidth, fArcHeight);
+    }
+
+    public Vector handles() {
+        Vector handles = new Vector();
+        BoxHandleKit.addHandles(this, handles);
+
+        handles.addElement(new RadiusHandle(this));
+
+        return handles;
+    }
+
+    public Rectangle displayBox() {
+        return new Rectangle(
+            fDisplayBox.x,
+            fDisplayBox.y,
+            fDisplayBox.width,
+            fDisplayBox.height);
+    }
+
+    protected void basicMoveBy(int x, int y) {
+        fDisplayBox.translate(x,y);
+    }
+
+    public void drawBackground(Graphics g) {
+        Rectangle r = displayBox();
+        g.fillRoundRect(r.x, r.y, r.width, r.height, fArcWidth, fArcHeight);
+    }
+
+    public void drawFrame(Graphics g) {
+        Rectangle r = displayBox();
+        g.drawRoundRect(r.x, r.y, r.width-1, r.height-1, fArcWidth, fArcHeight);
+    }
+
+    public Insets connectionInsets() {
+        return new Insets(fArcHeight/2, fArcWidth/2, fArcHeight/2, fArcWidth/2);
+    }
+
+    public Connector connectorAt(int x, int y) {
+        return new ShortestDistanceConnector(this); // just for demo purposes
+    }
+
+    public void write(StorableOutput dw) {
+        super.write(dw);
+        dw.writeInt(fDisplayBox.x);
+        dw.writeInt(fDisplayBox.y);
+        dw.writeInt(fDisplayBox.width);
+        dw.writeInt(fDisplayBox.height);
+        dw.writeInt(fArcWidth);
+        dw.writeInt(fArcHeight);
+    }
+
+    public String getMap() {
+	String sense = (String)getAttribute("Sensitive");
+	if (sense != null && sense.length() > 0) {
+		try {
+			sense = URLDecoder.decode(sense);
+		} catch (Exception e) {}
+	    Rectangle box = displayBox();
+	    return "<area shape=\"rect\" coords=\"" +
+		       box.x + "," + box.y + "," +
+		       (box.x + box.width) + "," +
+		       (box.y + box.height) +
+		       "\" href=\"" +
+		       sense + "\" />\n";
+	}
+	return "";
+    }
+
+    public void read(StorableInput dr) throws IOException {
+        super.read(dr);
+        fDisplayBox = new Rectangle(
+            dr.readInt(),
+            dr.readInt(),
+            dr.readInt(),
+            dr.readInt());
+        fArcWidth = dr.readInt();
+        fArcHeight = dr.readInt();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ScribbleTool.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,68 @@
+/*
+ * @(#)ScribbleTool.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.AbstractTool;
+
+/**
+ * Tool to scribble a PolyLineFigure
+ * @see PolyLineFigure
+ */
+public class ScribbleTool extends AbstractTool {
+
+    private PolyLineFigure  fScribble;
+    private int             fLastX, fLastY;
+
+    public ScribbleTool(DrawingView view) {
+        super(view);
+    }
+
+    public void activate() {
+        super.activate();
+        fScribble = null;
+    }
+
+    public void deactivate() {
+        super.deactivate();
+        if (fScribble != null) {
+            if (fScribble.size().width < 4 || fScribble.size().height < 4)
+                drawing().remove(fScribble);
+        }
+    }
+
+    private void point(int x, int y) {
+        if (fScribble == null) {
+            fScribble = new PolyLineFigure(x, y);
+            view().add(fScribble);
+        } else if (fLastX != x || fLastY != y)
+            fScribble.addPoint(x, y);
+
+        fLastX = x;
+        fLastY = y;
+    }
+
+    public void mouseDown(MouseEvent e, int x, int y) {
+        if (e.getClickCount() >= 2) {
+            fScribble = null;
+            editor().toolDone();
+        }
+        else {
+            // use original event coordinates to avoid
+            // supress that the scribble is constrained to
+            // the grid
+            point(e.getX(), e.getY());
+        }
+    }
+
+    public void mouseDrag(MouseEvent e, int x, int y) {
+        if (fScribble != null)
+            point(e.getX(), e.getY());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/ShortestDistanceConnector.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,145 @@
+/*
+ * @(#)ShortestDistanceConnector.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.Geom;
+
+/**
+ * A ShortestDistance locates connection points by
+ * finding the shortest distance between the start and
+ * end of the connection.
+ * It doesn't connect to the ares defined by Figure.connectionInsets()
+ * @see Figure#connectionInsets
+ * @see Connector
+ */
+public class ShortestDistanceConnector extends AbstractConnector {
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = -2273446020593433887L;
+
+    public ShortestDistanceConnector() { // only used for Storable implementation
+        super();
+    }
+
+    public ShortestDistanceConnector(Figure owner) {
+        super(owner);
+    }
+
+    public Point findStart(ConnectionFigure connection) {
+        return findPoint(connection, true);
+    }
+
+    public Point findEnd(ConnectionFigure connection) {
+        return findPoint(connection, false);
+    }
+
+    protected Point findPoint(ConnectionFigure connection, boolean getStart) {
+        Figure startFigure = connection.start().owner();
+        Figure endFigure = connection.end().owner();
+
+        Rectangle r1 = startFigure.displayBox();
+        Rectangle r2 = endFigure.displayBox();
+
+        Insets i1 = startFigure.connectionInsets();
+        Insets i2 = endFigure.connectionInsets();
+
+        Point p1, p2;
+        Point start = null, end = null, s = null, e = null;
+        long len2 = Long.MAX_VALUE, l2;
+        int x1, x2, y1, y2; // connection points
+        int xmin, xmax, ymin, ymax;
+
+        // X-dimension
+        // constrain width connection insets
+        int r1x, r1width, r2x, r2width, r1y, r1height, r2y, r2height;
+        r1x = r1.x + i1.left;
+        r1width = r1.width - i1.left - i1.right-1;
+        r2x = r2.x + i2.left;
+        r2width = r2.width - i2.left - i2.right-1;
+
+        // find x connection point
+        if (r1x + r1width < r2x) {
+            x1 = r1x + r1width;
+            x2 = r2x;
+        } else if (r1x > r2x + r2width) {
+            x1 = r1x;
+            x2 = r2x + r2width;
+        } else {
+            xmax = Math.max(r1x, r2x);
+            xmin = Math.min(r1x+r1width, r2x+r2width);
+            x1 = x2 = (xmax + xmin) /2;
+        }
+
+        // Y-Dimension
+        // constrain with connection insets
+        r1y = r1.y + i1.top;
+        r1height = r1.height - i1.top - i1.bottom-1;
+        r2y = r2.y + i2.top;
+        r2height = r2.height - i2.top - i2.bottom-1;
+
+        // y connection point
+        if (r1y + r1height < r2y) {
+            y1 = r1y + r1height;
+            y2 = r2y;
+        } else if (r1y > r2y + r2height) {
+            y1 = r1y;
+            y2 = r2y + r2height;
+        } else {
+            ymax = Math.max(r1y, r2y);
+            ymin = Math.min(r1y+r1height, r2y+r2height);
+            y1 = y2 = (ymax + ymin) /2;
+        }
+        // find shortest connection
+        for (int i = 0; i < 4; i++) {
+            switch(i) {
+            case 0:
+                // EAST-WEST
+                p1 = Geom.east(r1);
+                p2 = Geom.west(r2);
+                s = new Point(p1.x, y1);
+                e = new Point(p2.x, y2);
+                break;
+            case 1:
+                // WEST-EAST
+                p1 = Geom.west(r1);
+                p2 = Geom.east(r2);
+                s = new Point(p1.x, y1);
+                e = new Point(p2.x, y2);
+                break;
+            case 2:
+                // NORTH-SOUTH
+                p1 = Geom.north(r1);
+                p2 = Geom.south(r2);
+                s = new Point(x1, p1.y);
+                e = new Point(x2, p2.y);
+                break;
+            case 3:
+                // SOUTH-NORTH
+                p1 = Geom.south(r1);
+                p2 = Geom.north(r2);
+                s = new Point(x1, p1.y);
+                e = new Point(x2, p2.y);
+                break;
+            }
+            l2 = Geom.length2(s.x, s.y, e.x, e.y);
+            if (l2 < len2) {
+                start = s;
+                end = e;
+                len2 = l2;
+            }
+        }
+        if (getStart)
+            return start;
+        return end;
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/TextFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,429 @@
+/*
+ * @(#)TextFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.util.*;
+import java.awt.*;
+import java.io.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * A text figure.
+ *
+ * @see TextTool
+ */
+public  class TextFigure
+        extends AttributeFigure
+        implements FigureChangeListener, TextHolder {
+
+    private int               fOriginX;
+    private int               fOriginY;
+
+    // cache of the TextFigure's size
+    transient private boolean fSizeIsDirty = true;
+    transient private int     fWidth;
+    transient private int     fHeight;
+
+    private String  fText;
+    private Font    fFont;
+    private boolean fIsReadOnly;
+    private int  fAlign;
+
+    private static final int TEXTALIGN_LEFT = 0;
+    private static final int TEXTALIGN_CENTRE = 1;
+    private static final int TEXTALIGN_RIGHT = 2;
+
+    private Figure  fObservedFigure = null;
+    private OffsetLocator fLocator = null;
+
+    private static String fgCurrentFontName  = "Helvetica";
+    private static int    fgCurrentFontSize  = 12;
+    private static int    fgCurrentFontStyle = Font.PLAIN;
+
+    /*
+     * Serialization support.
+     */
+    private static final long serialVersionUID = 4599820785949456124L;
+    private int textFigureSerializedDataVersion = 1;
+
+    public TextFigure() {
+        fOriginX = 0;
+        fOriginY = 0;
+        fFont = createCurrentFont();
+        setAttribute("FillColor", ColorMap.getColorMap().color("None"));
+	fAlign = TEXTALIGN_LEFT;
+        fText = new String("");
+        fSizeIsDirty = true;
+    }
+
+    public void moveBy(int x, int y) {
+        willChange();
+        basicMoveBy(x, y);
+        if (fLocator != null)
+            fLocator.moveBy(x, y);
+        changed();
+    }
+
+    protected void basicMoveBy(int x, int y) {
+        fOriginX += x;
+        fOriginY += y;
+    }
+
+    public void basicDisplayBox(Point newOrigin, Point newCorner) {
+        fOriginX = newOrigin.x;
+        fOriginY = newOrigin.y;
+    }
+
+    public Rectangle displayBox() {
+        Dimension extent = textExtent();
+        return new Rectangle(fOriginX, fOriginY, extent.width, extent.height);
+    }
+
+    public Rectangle textDisplayBox() {
+        return displayBox();
+    }
+
+    /**
+     * Tests whether this figure is read only.
+     */
+    public boolean readOnly() {
+        return fIsReadOnly;
+    }
+
+    /**
+     * Sets the read only status of the text figure.
+     */
+    public void setReadOnly(boolean isReadOnly) {
+        fIsReadOnly = isReadOnly;
+    }
+
+    /**
+     * Gets the font.
+     */
+    public Font getFont() {
+        return fFont;
+    }
+
+    /**
+     * Sets the font.
+     */
+    public void setFont(Font newFont) {
+        willChange();
+        fFont = newFont;
+        markDirty();
+        changed();
+    }
+
+    /**
+     * Updates the location whenever the figure changes itself.
+     */
+    public void changed() {
+        super.changed();
+        updateLocation();
+    }
+
+    /**
+     * A text figure understands the "FontSize", "FontStyle", "FontName"
+     * and "TextAlign" attributes.
+     */
+    public Object getAttribute(String name) {
+        Font font = getFont();
+        if (name.equals("FontSize"))
+            return new Integer(font.getSize());
+        if (name.equals("FontStyle"))
+            return new Integer(font.getStyle());
+        if (name.equals("FontName"))
+            return font.getName();
+	if (name.equals("TextAlign")) {
+	    switch (fAlign) {
+	    default: return "Left";
+	    case TEXTALIGN_CENTRE: return "Centre";
+	    case TEXTALIGN_RIGHT: return "Right";
+	    }
+	}
+        return super.getAttribute(name);
+    }
+
+    /**
+     * A text figure understands the "FontSize", "FontStyle", "FontName"
+     * and "Align" attributes.
+     */
+    public void setAttribute(String name, Object value) {
+        Font font = getFont();
+        if (name.equals("FontSize")) {
+            Integer s = (Integer)value;
+            setFont(new Font(font.getName(), font.getStyle(), s.intValue()) );
+        }
+        else if (name.equals("FontStyle")) {
+            Integer s = (Integer)value;
+            int style = font.getStyle();
+            if (s.intValue() == Font.PLAIN)
+                style = font.PLAIN;
+            else
+                style = style ^ s.intValue();
+            setFont(new Font(font.getName(), style, font.getSize()) );
+        }
+        else if (name.equals("FontName")) {
+            String n = (String)value;
+            setFont(new Font(n, font.getStyle(), font.getSize()) );
+        }
+	else if (name.equals("TextAlign")) {
+            String n = (String)value;
+	    if (n.equals("Centre"))
+		fAlign = TEXTALIGN_CENTRE;
+	    else if (n.equals("Right"))
+		fAlign = TEXTALIGN_RIGHT;
+	    else
+		fAlign = TEXTALIGN_LEFT;
+            changed();
+	}
+        else
+            super.setAttribute(name, value);
+    }
+
+    /**
+     * Gets the text shown by the text figure.
+     */
+    public String getText() {
+        return fText;
+    }
+
+    /**
+     * Sets the text shown by the text figure.
+     */
+    public void setText(String newText) {
+        if (!newText.equals(fText)) {
+            willChange();
+            fText = new String(newText);
+            markDirty();
+            changed();
+        }
+    }
+
+    /**
+     * Tests whether the figure accepts typing.
+     */
+    public boolean acceptsTyping() {
+        return !fIsReadOnly;
+    }
+
+    public void drawBackground(Graphics g) {
+        Rectangle r = displayBox();
+        g.fillRect(r.x, r.y, r.width, r.height);
+    }
+
+    public void drawFrame(Graphics g) {
+        g.setFont(fFont);
+        g.setColor((Color) getAttribute("TextColor"));
+        FontMetrics metrics = g.getFontMetrics(fFont);
+	StringTokenizer st = new StringTokenizer(fText, "\n");
+	Dimension d = textExtent();
+	
+	int orgy = fOriginY;
+	while (st.hasMoreTokens()) {
+	    String t = st.nextToken();
+	    int xpos = fOriginX;
+	    if (fAlign == TEXTALIGN_RIGHT) {
+		xpos += d.width - metrics.stringWidth(t);
+	    } else if (fAlign == TEXTALIGN_CENTRE) {
+		xpos += (d.width - metrics.stringWidth(t)) / 2;
+	    }
+	    g.drawString(t, xpos, orgy + metrics.getAscent());
+	    orgy += metrics.getHeight();
+	}
+    }
+
+    private Dimension textExtent() {
+        if (!fSizeIsDirty)
+            return new Dimension(fWidth, fHeight);
+        FontMetrics metrics =
+	    Toolkit.getDefaultToolkit().getFontMetrics(fFont);
+	fWidth = 0;
+	fHeight = 0;
+	StringTokenizer st = new StringTokenizer(fText, "\n");
+	while (st.hasMoreTokens()) {
+	    String t = st.nextToken();
+	    fHeight += metrics.getHeight();
+	    int w = metrics.stringWidth(t);
+	    if (w > fWidth)
+		fWidth = w;
+	}
+
+        fSizeIsDirty = false;
+        return new Dimension(fWidth, fHeight);
+    }
+
+    private void markDirty() {
+        fSizeIsDirty = true;
+    }
+
+    private Dimension getRowsAndColumns(String text) {
+	int rows = 0;
+	int cols = 0;
+	StringTokenizer st = new StringTokenizer(fText, "\n");
+	while (st.hasMoreTokens()) {
+	    String t = st.nextToken();
+	    if (t.length() > cols)
+		cols = t.length();
+	    rows++;
+	}
+	return new Dimension(cols, rows);
+    }
+
+    /**
+     * Gets the number of columns to be overlaid when the figure is edited.
+     */
+    public int overlayColumns() {
+	Dimension d = getRowsAndColumns(getText());
+        return d.width < 20 ? 20 : d.width;
+    }
+
+    /**
+     * Gets the number of rows to be overlaid when the figure is edited.
+     */
+    public int overlayRows() {
+	Dimension d = getRowsAndColumns(getText());
+        return d.height < 5 ? 5 : d.height;
+    }
+
+    public Vector handles() {
+        Vector handles = new Vector();
+        handles.addElement(new NullHandle(this, RelativeLocator.northWest()));
+        handles.addElement(new NullHandle(this, RelativeLocator.northEast()));
+        handles.addElement(new NullHandle(this, RelativeLocator.southEast()));
+        handles.addElement(new FontSizeHandle(this, RelativeLocator.southWest()));
+        return handles;
+    }
+
+    public void write(StorableOutput dw) {
+        super.write(dw);
+        dw.writeInt(fOriginX);
+        dw.writeInt(fOriginY);
+        dw.writeString(fText);
+        dw.writeString(fFont.getName());
+        dw.writeInt(fFont.getStyle());
+        dw.writeInt(fFont.getSize());
+        dw.writeBoolean(fIsReadOnly);
+        dw.writeStorable(fObservedFigure);
+        dw.writeStorable(fLocator);
+    }
+
+    public void read(StorableInput dr) throws IOException {
+        super.read(dr);
+        markDirty();
+        fOriginX = dr.readInt();
+        fOriginY = dr.readInt();
+        fText = dr.readString();
+        fFont = new Font(dr.readString(), dr.readInt(), dr.readInt());
+        fIsReadOnly = dr.readBoolean();
+
+        fObservedFigure = (Figure)dr.readStorable();
+        if (fObservedFigure != null) {
+            setAttribute("observed.figure", fObservedFigure);
+            fObservedFigure.addFigureChangeListener(this);
+        }
+        fLocator = (OffsetLocator)dr.readStorable();
+    }
+
+    private void readObject(ObjectInputStream s)
+        throws ClassNotFoundException, IOException {
+
+        s.defaultReadObject();
+
+        if (fObservedFigure != null)
+            fObservedFigure.addFigureChangeListener(this);
+        markDirty();
+    }
+
+    public void connect(Figure figure) {
+        if (fObservedFigure != null)
+            fObservedFigure.removeFigureChangeListener(this);
+
+        fObservedFigure = figure;
+        fLocator = new OffsetLocator(figure.connectedTextLocator(this));
+        fObservedFigure.addFigureChangeListener(this);
+        setAttribute("observed.figure", fObservedFigure);
+        updateLocation();
+    }
+
+    public void figureChanged(FigureChangeEvent e) {
+        updateLocation();
+    }
+
+    public void figureRemoved(FigureChangeEvent e) {
+        if (listener() != null)
+            listener().figureRequestRemove(new FigureChangeEvent(this));
+    }
+
+    public void figureRequestRemove(FigureChangeEvent e) {}
+    public void figureInvalidated(FigureChangeEvent e) {}
+    public void figureRequestUpdate(FigureChangeEvent e) {}
+
+    /**
+     * Updates the location relative to the connected figure.
+     * The TextFigure is centered around the located point.
+     */
+    protected void updateLocation() {
+        if (fLocator != null) {
+            Point p = fLocator.locate(fObservedFigure);
+            p.x -= size().width/2 + fOriginX;
+            p.y -= size().height/2 + fOriginY;
+
+            if (p.x != 0 || p.y != 0) {
+                willChange();
+                basicMoveBy(p.x, p.y);
+                changed();
+            }
+        }
+    }
+
+    public void release() {
+        super.release();
+        if (fObservedFigure != null)
+            fObservedFigure.removeFigureChangeListener(this);
+    }
+
+    /**
+     * Disconnects the text figure.
+     */
+    public void disconnect() {
+        fObservedFigure.removeFigureChangeListener(this);
+        fObservedFigure = null;
+        fLocator = null;
+        setAttribute("observed.figure", null);
+    }
+
+
+    /**
+     * Creates the current font to be used for new text figures.
+     */
+    static public Font createCurrentFont() {
+        return new Font(fgCurrentFontName, fgCurrentFontStyle, fgCurrentFontSize);
+    }
+
+    /**
+     * Sets the current font name
+     */
+    static public void setCurrentFontName(String name) {
+        fgCurrentFontName = name;
+    }
+
+    /**
+     * Sets the current font size.
+     */
+    static public void setCurrentFontSize(int size) {
+        fgCurrentFontSize = size;
+    }
+
+    /**
+     * Sets the current font style.
+     */
+    static public void setCurrentFontStyle(int style) {
+        fgCurrentFontStyle = style;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/TextTool.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,118 @@
+/*
+ * @(#)TextTool.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.awt.event.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.FloatingTextField;
+
+/**
+ * Tool to create new or edit existing text figures.
+ * The editing behavior is implemented by overlaying the
+ * Figure providing the text with a FloatingTextField.<p>
+ * A tool interaction is done once a Figure that is not
+ * a TextHolder is clicked.
+ *
+ * @see TextHolder
+ * @see FloatingTextField
+ */
+public class TextTool extends CreationTool {
+
+    private FloatingTextField   fTextField;
+    private TextHolder  fTypingTarget;
+
+    public TextTool(DrawingView view, Figure prototype) {
+        super(view, prototype);
+    }
+
+    /**
+     * If the pressed figure is a TextHolder it can be edited otherwise
+     * a new text figure is created.
+     */
+    public void mouseDown(MouseEvent e, int x, int y)
+    {
+	    Figure pressedFigure;
+	    TextHolder textHolder = null;
+
+	    pressedFigure = drawing().findFigureInside(x, y);
+	    if (pressedFigure instanceof TextHolder) {
+	        textHolder = (TextHolder) pressedFigure;
+	        if (!textHolder.acceptsTyping())
+	            textHolder = null;
+        }
+	    if (textHolder != null) {
+	        beginEdit(textHolder);
+	        return;
+	    }
+	    if (fTypingTarget != null) {
+	        editor().toolDone();
+	        endEdit();
+	    } else {
+    	    super.mouseDown(e, x, y);
+    	    textHolder = (TextHolder)createdFigure();
+    	    beginEdit(textHolder);
+        }
+    }
+
+    public void mouseDrag(MouseEvent e, int x, int y) {
+    }
+
+    public void mouseUp(MouseEvent e, int x, int y) {
+    }
+
+    /**
+     * Terminates the editing of a text figure.
+     */
+    public void deactivate() {
+        super.deactivate();
+        endEdit();
+    }
+
+    /**
+     * Sets the text cursor.
+     */
+    public void activate() {
+        super.activate();
+        view().clearSelection();
+        // JDK1.1 TEXT_CURSOR has an incorrect hot spot
+        //view.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+    }
+
+    protected void beginEdit(TextHolder figure) {
+        if (fTextField == null)
+            fTextField = new FloatingTextField();
+
+	if (figure != fTypingTarget && fTypingTarget != null)
+	    endEdit();
+
+        fTextField.createOverlay((Container)view(), figure.getFont());
+	fTextField.setBounds(fieldBounds(figure), figure.getText());
+	fTypingTarget = figure;
+    }
+
+    protected void endEdit() {
+	if (fTypingTarget != null) {
+	    if (fTextField.getText().length() > 0)
+		fTypingTarget.setText(fTextField.getText());
+	    else
+		drawing().remove((Figure)fTypingTarget);
+	    fTypingTarget = null;
+	    fTextField.endOverlay();
+	    view().checkDamage();
+	}
+    }
+
+    private Rectangle fieldBounds(TextHolder figure) {
+    	Rectangle box = figure.textDisplayBox();
+    	int nRows = figure.overlayRows();
+    	int nCols = figure.overlayColumns();
+        Dimension d = fTextField.getPreferredSize(nRows, nCols);
+        return new Rectangle(box.x, box.y, d.width, d.height);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/URLTool.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,105 @@
+/*
+ * @(#)URLTool.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.net.URLEncoder;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.standard.*;
+import CH.ifa.draw.util.FloatingTextField;
+
+/**
+ * Tool to create new or edit existing text figures.
+ * The editing behavior is implemented by overlaying the
+ * Figure providing the text with a FloatingTextField.<p>
+ * A tool interaction is done once a Figure that is not
+ * a TextHolder is clicked.
+ *
+ * @see TextHolder
+ * @see FloatingTextField
+ */
+public class URLTool extends CreationTool {
+
+    private FloatingTextField   fTextField;
+    private Figure  fTypingTarget;
+    private static Font dialogFont = Font.decode("dialog-PLAIN-12");
+
+    public URLTool(DrawingView view, Figure prototype) {
+        super(view, prototype);
+    }
+
+    /**
+     * If the pressed figure is a TextHolder it can be edited otherwise
+     * a new text figure is created.
+     */
+    public void mouseDown(MouseEvent e, int x, int y)
+    {
+	Figure pressedFigure;
+
+	pressedFigure = drawing().findFigureInside(x, y);
+	if (pressedFigure != null) {
+	    beginEdit(pressedFigure);
+	} else if (fTypingTarget != null) {
+	    editor().toolDone();
+	    endEdit();
+	}
+    }
+
+    public void mouseDrag(MouseEvent e, int x, int y) {
+    }
+
+    public void mouseUp(MouseEvent e, int x, int y) {
+    }
+
+    /**
+     * Terminates the editing of a text figure.
+     */
+    public void deactivate() {
+        super.deactivate();
+        endEdit();
+    }
+
+    /**
+     * Sets the text cursor.
+     */
+    public void activate() {
+        super.activate();
+        view().clearSelection();
+        // JDK1.1 TEXT_CURSOR has an incorrect hot spot
+        //view.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+    }
+
+    protected void beginEdit(Figure figure) {
+        if (fTextField == null)
+            fTextField = new FloatingTextField();
+
+	if (figure != fTypingTarget && fTypingTarget != null)
+	    endEdit();
+
+        fTextField.createOverlay((Container)view(), dialogFont);
+	fTextField.setBounds(fieldBounds(figure),
+			     (String)figure.getAttribute("Sensitive"));
+	fTypingTarget = figure;
+    }
+
+    protected void endEdit() {
+	if (fTypingTarget != null) {
+	    String s = URLEncoder.encode(fTextField.getText());
+	    fTypingTarget.setAttribute("Sensitive", s);
+	    fTypingTarget = null;
+	    fTextField.endOverlay();
+	    view().checkDamage();
+	}
+    }
+
+    private Rectangle fieldBounds(Figure figure) {
+    	Rectangle box = figure.displayBox();
+        Dimension d = fTextField.getPreferredSize(1, 20);
+        return new Rectangle(box.x, box.y, d.width, d.height);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/figures/UngroupCommand.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,50 @@
+/*
+ * @(#)UngroupCommand.java 5.1
+ *
+ */
+
+package CH.ifa.draw.figures;
+
+import java.awt.*;
+import java.util.*;
+import CH.ifa.draw.framework.*;
+import CH.ifa.draw.util.Command;
+
+/**
+ * Command to ungroup the selected figures.
+ * @see GroupCommand
+ */
+public  class UngroupCommand extends Command {
+
+    private DrawingView fView;
+
+   /**
+    * Constructs a group command.
+    * @param name the command name
+    * @param view the target view
+    */
+    public UngroupCommand(String name, DrawingView view) {
+        super(name);
+        fView = view;
+    }
+
+    public void execute() {
+        FigureEnumeration selection = fView.selectionElements();
+        fView.clearSelection();
+
+        Vector parts = new Vector();
+        while (selection.hasMoreElements()) {
+            Figure selected = selection.nextFigure();
+            Figure group = fView.drawing().orphan(selected);
+            FigureEnumeration k = group.decompose();
+            while (k.hasMoreElements())
+                fView.addToSelection(fView.add(k.nextFigure()));
+        }
+        fView.checkDamage();
+    }
+
+    public boolean isExecutable() {
+        return fView.selectionCount() > 0;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/ConnectionFigure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,135 @@
+/*
+ * @(#)ConnectionFigure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.Point;
+import java.io.Serializable;
+import CH.ifa.draw.util.*;
+import CH.ifa.draw.framework.*;
+
+/**
+ * Figures to connect Connectors provided by Figures.
+ * A ConnectionFigure knows its start and end Connector.
+ * It uses the Connectors to locate its connection points.<p>
+ * A ConnectionFigure can have multiple segments. It provides
+ * operations to split and join segments.
+ *
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld034.htm>Strategy</a></b><br>
+ * Strategy is used encapsulate the algorithm to locate the connection point.
+ * ConnectionFigure is the Strategy context and Connector is the Strategy.<br>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld026.htm>Observer</a></b><br>
+ * Observer is used to track changes of the connected figures. A connection
+ * figure registers itself as listeners or observers of the source and
+ * target connector.
+ * <hr>
+ *
+ * @see Connector
+ */
+
+public interface ConnectionFigure extends Figure, FigureChangeListener {
+
+    /**
+     * Sets the start Connector of the connection.
+     * @param figure the start figure of the connection
+     */
+    public void connectStart(Connector start);
+
+    /**
+     * Sets the end Connector of the connection.
+     * @param figure the end figure of the connection
+     */
+    public void connectEnd(Connector end);
+
+    /**
+     * Updates the connection
+     */
+    public void updateConnection();
+
+    /**
+     * Disconnects the start figure from the dependent figure
+     */
+    public void disconnectStart();
+
+    /**
+     * Disconnects the end figure from the dependent figure
+     */
+    public void disconnectEnd();
+
+    /**
+     * Gets the start Connector
+     */
+    public Connector start();
+
+    /**
+     * Gets the end Connector.
+     */
+    public Connector end();
+
+    /**
+     * Checks if two figures can be connected. Implement this method
+     * to constrain the allowed connections between figures.
+     */
+    public boolean canConnect(Figure start, Figure end);
+
+    /**
+     * Checks if the ConnectionFigure connects the same figures.
+     */
+    public boolean connectsSame(ConnectionFigure other);
+
+    /**
+     * Sets the start point.
+     */
+    public void startPoint(int x, int y);
+
+    /**
+     * Sets the end point.
+     */
+    public void endPoint(int x, int y);
+
+    /**
+     * Gets the start point.
+     */
+    public Point startPoint();
+
+    /**
+     * Gets the end point.
+     */
+    public Point endPoint();
+
+    /**
+     * Sets the position of the point at the given position
+     */
+    public void setPointAt(Point p, int index);
+
+    /**
+     * Gets the Point at the given position
+     */
+    public Point pointAt(int index);
+
+    /**
+     * Gets the number of points or nodes of the connection
+     */
+    public int pointCount();
+
+    /**
+     * Splits the hit segment.
+     * @param x, y the position where the figure should be split
+     * @return the index of the splitting point
+     */
+    public int splitSegment(int x, int y);
+
+
+    /**
+     * Joins the hit segments.
+     * @param x, y the position where the figure should be joined.
+     * @return whether the segment was joined
+     */
+    public boolean joinSegments(int x, int y);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Connector.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,67 @@
+/*
+ * @(#)Connector.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.*;
+import java.io.Serializable;
+
+import CH.ifa.draw.util.*;
+
+/**
+ * Connectors know how to locate a connection point on a figure.
+ * A Connector knows its owning figure and can determine either
+ * the start or the endpoint of a given connection figure. A connector
+ * has a display box that describes the area of a figure it is
+ * responsible for. A connector can be visible but it doesn't have
+ * to be.<br>
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld004.htm>Strategy</a></b><br>
+ * Connector implements the strategy to determine the connections points.<br>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld016.htm>Factory Method</a></b><br>
+ * Connectors are created by the Figure's factory method connectorAt.
+ * <hr>
+ *
+ * @see Figure#connectorAt
+ * @see ConnectionFigure
+ */
+public interface Connector extends Serializable, Storable {
+
+    /**
+     * Finds the start point for the connection.
+     */
+    public abstract Point findStart(ConnectionFigure connection);
+
+    /**
+     * Finds the end point for the connection.
+     */
+    public abstract Point findEnd(ConnectionFigure connection);
+
+    /**
+     * Gets the connector's owner.
+     */
+    public abstract Figure owner();
+
+    /**
+     * Gets the display box of the connector.
+     */
+    public abstract Rectangle displayBox();
+
+    /**
+     * Tests if a point is contained in the connector.
+     */
+    public abstract boolean containsPoint(int x, int y);
+
+    /**
+     * Draws this connector. Connectors don't have to be visible
+     * and it is OK leave this method empty.
+     */
+    public abstract void draw(Graphics g);
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Drawing.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,201 @@
+/*
+ * @(#)Drawing.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import CH.ifa.draw.util.*;
+import java.awt.*;
+import java.util.*;
+import java.io.*;
+
+/**
+ * Drawing is a container for figures.
+ * <p>
+ * Drawing sends out DrawingChanged events to DrawingChangeListeners
+ * whenever a part of its area was invalidated.
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld026.htm>Observer</a></b><br>
+ * The Observer pattern is used to decouple the Drawing from its views and
+ * to enable multiple views.<hr>
+ *
+ * @see Figure
+ * @see DrawingView
+ * @see FigureChangeListener
+ */
+
+public interface Drawing
+        extends Storable, FigureChangeListener, Serializable {
+
+    /**
+     * Releases the drawing and its contained figures.
+     */
+    public void release();
+
+    /**
+     * Returns an enumeration to iterate in
+     * Z-order back to front over the figures.
+     */
+    public FigureEnumeration figures();
+
+    /**
+     * Returns an enumeration to iterate in
+     * Z-order front to back over the figures.
+     */
+    public FigureEnumeration figuresReverse();
+
+    /**
+     * Finds a top level Figure. Use this call for hit detection that
+     * should not descend into the figure's children.
+     */
+    public Figure findFigure(int x, int y);
+
+    /**
+     * Finds a top level Figure that intersects the given rectangle.
+     */
+    public Figure findFigure(Rectangle r);
+
+    /**
+     * Finds a top level Figure, but supresses the passed
+     * in figure. Use this method to ignore a figure
+     * that is temporarily inserted into the drawing.
+     * @param x the x coordinate
+     * @param y the y coordinate
+     * @param without the figure to be ignored during
+     * the find.
+     */
+    public Figure findFigureWithout(int x, int y, Figure without);
+
+    /**
+     * Finds a top level Figure that intersects the given rectangle.
+     * It supresses the passed
+     * in figure. Use this method to ignore a figure
+     * that is temporarily inserted into the drawing.
+     */
+    public Figure findFigure(Rectangle r, Figure without);
+
+    /**
+     * Finds a figure but descends into a figure's
+     * children. Use this method to implement <i>click-through</i>
+     * hit detection, that is, you want to detect the inner most
+     * figure containing the given point.
+     */
+    public Figure findFigureInside(int x, int y);
+
+    /**
+     * Finds a figure but descends into a figure's
+     * children. It supresses the passed
+     * in figure. Use this method to ignore a figure
+     * that is temporarily inserted into the drawing.
+     * @param x the x coordinate
+     * @param y the y coordinate
+     * @param without the figure to be ignored during
+     * the find.
+     */
+    public Figure findFigureInsideWithout(int x, int y, Figure without);
+
+    /**
+     * Adds a listener for this drawing.
+     */
+    public void addDrawingChangeListener(DrawingChangeListener listener);
+
+    /**
+     * Removes a listener from this drawing.
+     */
+    public void removeDrawingChangeListener(DrawingChangeListener listener);
+
+    /**
+     * Gets the listeners of a drawing.
+     */
+    public Enumeration drawingChangeListeners();
+
+    /**
+     * Adds a figure and sets its container to refer
+     * to this drawing.
+     * @return the figure that was inserted.
+     */
+    public Figure add(Figure figure);
+
+    /**
+     * Adds a vector of figures.
+     */
+    public void addAll(Vector newFigures);
+
+    /**
+     * Removes the figure from the drawing and releases it.
+     */
+    public Figure remove(Figure figure);
+
+    /**
+     * Removes a figure from the figure list, but
+     * doesn't release it. Use this method to temporarily
+     * manipulate a figure outside of the drawing.
+     */
+    public Figure orphan(Figure figure);
+
+    /**
+     * Removes a vector of figures from the figure's list
+     * without releasing the figures.
+     * @see orphan
+     */
+    public void orphanAll(Vector newFigures);
+
+    /**
+     * Removes a vector of figures .
+     * @see remove
+     */
+    public void removeAll(Vector figures);
+
+    /**
+     * Replaces a figure in the drawing without
+     * removing it from the drawing.
+     */
+    public void replace(Figure figure, Figure replacement);
+
+    /**
+     * Sends a figure to the back of the drawing.
+     */
+    public void sendToBack(Figure figure);
+
+    /**
+     * Brings a figure to the front.
+     */
+    public void bringToFront(Figure figure);
+
+    /**
+     * Draws all the figures back to front.
+     */
+    public void draw(Graphics g, boolean showGuides);
+
+    /**
+     * Invalidates a rectangle and merges it with the
+     * existing damaged area.
+     */
+    public void figureInvalidated(FigureChangeEvent e);
+
+    /**
+     * Forces an update of the drawing change listeners.
+     */
+    public void figureRequestUpdate(FigureChangeEvent e);
+
+    /**
+     * Handles a removeFrfigureRequestRemove request that
+     * is passed up the figure container hierarchy.
+     * @see FigureChangeListener
+     */
+    public void figureRequestRemove(FigureChangeEvent e);
+
+    /**
+     * Acquires the drawing lock.
+     */
+    public void lock();
+
+    /**
+     * Releases the drawing lock.
+     */
+    public void unlock();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/DrawingChangeEvent.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,40 @@
+/*
+ * @(#)DrawingChangeEvent.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.Rectangle;
+import java.util.EventObject;
+
+/**
+ * The event passed to DrawingChangeListeners.
+ *
+ */
+public class DrawingChangeEvent extends EventObject {
+
+    private Rectangle fRectangle;
+
+    /**
+     *  Constructs a drawing change event.
+     */
+    public DrawingChangeEvent(Drawing source, Rectangle r) {
+        super(source);
+        fRectangle = r;
+    }
+
+    /**
+     *  Gets the changed drawing
+     */
+    public Drawing getDrawing() {
+        return (Drawing)getSource();
+    }
+
+    /**
+     *  Gets the changed rectangle
+     */
+    public Rectangle getInvalidatedRectangle() {
+        return fRectangle;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/DrawingChangeListener.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,25 @@
+/*
+ * @(#)DrawingChangeListener.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.Rectangle;
+import java.util.EventListener;
+
+/**
+ * Listener interested in Drawing changes.
+ */
+public interface DrawingChangeListener extends EventListener {
+
+    /**
+     *  Sent when an area is invalid
+     */
+    public void drawingInvalidated(DrawingChangeEvent e);
+
+    /**
+     *  Sent when the drawing wants to be refreshed
+     */
+    public void drawingRequestUpdate(DrawingChangeEvent e);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/DrawingEditor.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,59 @@
+/*
+ * @(#)DrawingEditor.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.*;
+
+/**
+ * DrawingEditor defines the interface for coordinating
+ * the different objects that participate in a drawing editor.
+ *
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld022.htm>Mediator</a></b><br>
+ * DrawingEditor is the mediator. It decouples the participants
+ * of a drawing editor.
+ *
+ * @see Tool
+ * @see DrawingView
+ * @see Drawing
+ */
+public interface DrawingEditor {
+
+    /**
+     * Gets the editor's drawing view.
+     */
+    DrawingView view();
+
+    /**
+     * Gets the editor's drawing.
+     */
+    Drawing     drawing();
+
+    /**
+     * Gets the editor's current tool.
+     */
+    Tool        tool();
+
+    /**
+     * Informs the editor that a tool has done its interaction.
+     * This method can be used to switch back to the default tool.
+     */
+    void        toolDone();
+
+    /**
+     * Informs that the current selection has changed.
+     * Override this method to handle selection changes.
+     */
+    void        selectionChanged(DrawingView view);
+
+    /**
+     * Shows a status message in the editor's user interface
+     */
+    void        showStatus(String string);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/DrawingView.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,267 @@
+/*
+ * @(#)DrawingView.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.image.ImageObserver;
+import java.awt.*;
+import java.util.*;
+import java.io.*;
+import CH.ifa.draw.util.*;
+
+/**
+ * DrawingView renders a Drawing and listens to its changes.
+ * It receives user input and delegates it to the current tool.
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld026.htm>Observer</a></b><br>
+ * DrawingView observes drawing for changes via the DrawingListener interface.<br>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld032.htm>State</a></b><br>
+ * DrawingView plays the role of the StateContext in
+ * the State pattern. Tool is the State.<br>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld034.htm>Strategy</a></b><br>
+ * DrawingView is the StrategyContext in the Strategy pattern
+ * with regard to the UpdateStrategy. <br>
+ * DrawingView is the StrategyContext for the PointConstrainer.
+ *
+ * @see Drawing
+ * @see Painter
+ * @see Tool
+ */
+
+
+public interface DrawingView extends ImageObserver, DrawingChangeListener {
+
+    /**
+     * Sets the view's editor.
+     */
+    public void setEditor(DrawingEditor editor);
+
+    /**
+     * Gets the current tool.
+     */
+    public Tool tool();
+
+    /**
+     * Gets the drawing.
+     */
+    public Drawing drawing();
+
+    public void enableGuides(boolean enable);
+
+    public boolean guidesEnabled();
+
+    /**
+     * Sets and installs another drawing in the view.
+     */
+    public void setDrawing(Drawing d);
+
+    /**
+     * Gets the editor.
+     */
+    public DrawingEditor editor();
+
+    /**
+     * Adds a figure to the drawing.
+     * @return the added figure.
+     */
+    public Figure add(Figure figure);
+
+    /**
+     * Removes a figure from the drawing.
+     * @return the removed figure
+     */
+    public Figure remove(Figure figure);
+
+    /**
+     * Adds a vector of figures to the drawing.
+     */
+    public void addAll(Vector figures);
+
+    /**
+     * Gets the size of the drawing.
+     */
+    public Dimension getSize();
+
+    /**
+     * Gets the minimum dimension of the drawing.
+     */
+    public Dimension getMinimumSize();
+
+    /**
+     * Gets the preferred dimension of the drawing..
+     */
+    public Dimension getPreferredSize();
+
+    /**
+     * Sets the current display update strategy.
+     * @see UpdateStrategy
+     */
+    public void setDisplayUpdate(Painter updateStrategy);
+
+    /**
+     * Gets the currently selected figures.
+     * @return a vector with the selected figures. The vector
+     * is a copy of the current selection.
+     */
+    public Vector selection();
+
+    /**
+     * Gets an enumeration over the currently selected figures.
+     */
+    public FigureEnumeration selectionElements();
+
+    /**
+     * Gets the currently selected figures in Z order.
+     * @see #selection
+     * @return a vector with the selected figures. The vector
+     * is a copy of the current selection.
+     */
+    public Vector selectionZOrdered();
+
+    /**
+     * Gets the number of selected figures.
+     */
+    public int selectionCount();
+
+    /**
+     * Adds a figure to the current selection.
+     */
+    public void addToSelection(Figure figure);
+
+    /**
+     * Adds a vector of figures to the current selection.
+     */
+    public void addToSelectionAll(Vector figures);
+
+    /**
+     * Removes a figure from the selection.
+     */
+    public void removeFromSelection(Figure figure);
+
+    /**
+     * If a figure isn't selected it is added to the selection.
+     * Otherwise it is removed from the selection.
+     */
+    public void toggleSelection(Figure figure);
+
+    /**
+     * Clears the current selection.
+     */
+    public void clearSelection();
+
+    /**
+     * Gets the current selection as a FigureSelection. A FigureSelection
+     * can be cut, copied, pasted.
+     */
+    public FigureSelection getFigureSelection();
+
+    /**
+     * Move current selection by dx and dy
+     */
+    public void moveSelection(int dx, int dy);
+
+    /**
+     * Finds a handle at the given coordinates.
+     * @return the hit handle, null if no handle is found.
+     */
+    public Handle findHandle(int x, int y);
+
+    /**
+     * Gets the position of the last click inside the view.
+     */
+    public Point lastClick();
+
+    /**
+     * Sets the current point constrainer.
+     */
+    public void setConstrainer(PointConstrainer p);
+
+    /**
+     * Gets the current grid setting.
+     */
+    public PointConstrainer getConstrainer();
+
+    /**
+     * Checks whether the drawing has some accumulated damage
+     */
+    public void checkDamage();
+
+    /**
+     * Repair the damaged area
+     */
+    public void repairDamage();
+
+    /**
+     * Paints the drawing view. The actual drawing is delegated to
+     * the current update strategy.
+     * @see Painter
+     */
+    public void paint(Graphics g);
+
+    /**
+     * Creates an image with the given dimensions
+     */
+    public Image createImage(int width, int height);
+
+    /**
+     * Gets a graphic to draw into
+     */
+    public Graphics getGraphics();
+
+    /**
+     * Gets the background color of the DrawingView
+     */
+    public Color getBackground();
+
+    /**
+     * Gets the background color of the DrawingView
+     */
+    public void setBackground(Color c);
+
+    /**
+     * Draws the contents of the drawing view.
+     * The view has three layers: background, drawing, handles.
+     * The layers are drawn in back to front order.
+     */
+    public void drawAll(Graphics g);
+
+    /**
+     * Draws the currently active handles.
+     */
+    public void drawHandles(Graphics g);
+
+    /**
+     * Draws the drawing.
+     */
+    public void drawDrawing(Graphics g, boolean showGuides);
+
+    /**
+     * Draws the background. If a background pattern is set it
+     * is used to fill the background. Otherwise the background
+     * is filled in the background color.
+     */
+    public void drawBackground(Graphics g);
+
+    /**
+     * Sets the cursor of the DrawingView
+     */
+    public void setCursor(Cursor c);
+
+    /**
+     * Freezes the view by acquiring the drawing lock.
+     * @see Drawing#lock
+     */
+    public void freezeView();
+
+    /**
+     * Unfreezes the view by releasing the drawing lock.
+     * @see Drawing#unlock
+     */
+    public void unfreezeView();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Figure.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,254 @@
+/*
+ * @(#)Figure.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import CH.ifa.draw.util.*;
+import java.awt.*;
+import java.util.*;
+import java.io.Serializable;
+
+/**
+ * The interface of a graphical figure. A figure knows
+ * its display box and can draw itself. A figure can be
+ * composed of several figures. To interact and manipulate
+ * with a figure it can provide Handles and Connectors.<p>
+ * A figure has a set of handles to manipulate its shape or attributes.
+ * A figure has one or more connectors that define how
+ * to locate a connection point.<p>
+ * Figures can have an open ended set of attributes.
+ * An attribute is identified by a string.<p>
+ * Default implementations for the Figure interface are provided
+ * by AbstractFigure.
+ *
+ * @see Handle
+ * @see Connector
+ * @see AbstractFigure
+ */
+
+public interface Figure
+                extends Storable, Cloneable, Serializable {
+
+    /**
+     * Moves the Figure to a new location.
+     * @param x the x delta
+     * @param y the y delta
+     */
+    public void moveBy(int dx, int dy);
+
+    /**
+     * Changes the display box of a figure. This method is
+     * always implemented in figure subclasses.
+     * It only changes
+     * the displaybox and does not announce any changes. It
+     * is usually not called by the client. Clients typically call
+     * displayBox to change the display box.
+     * @param origin the new origin
+     * @param corner the new corner
+     * @see #displayBox
+     */
+    public void basicDisplayBox(Point origin, Point corner);
+
+    /**
+     * Changes the display box of a figure. Clients usually
+     * invoke this method. It changes the display box
+     * and announces the corresponding changes.
+     * @param origin the new origin
+     * @param corner the new corner
+     * @see #displayBox
+     */
+    public void displayBox(Point origin, Point corner);
+
+    /**
+     * Gets the display box of a figure
+     * @see #basicDisplayBox
+     */
+    public Rectangle displayBox();
+
+    /**
+     * Draws the figure.
+     * @param g the Graphics to draw into
+     */
+    public void draw(Graphics g, boolean showGuides);
+
+    /**
+     * Returns the handles used to manipulate
+     * the figure. Handles is a Factory Method for
+     * creating handle objects.
+     *
+     * @return a Vector of handles
+     * @see Handle
+     */
+    public Vector handles();
+
+    /**
+     * Gets the size of the figure
+     */
+    public Dimension size();
+
+    /**
+     * Gets the figure's center
+     */
+    public Point center();
+
+    /**
+     * Checks if the Figure should be considered as empty.
+     */
+    public boolean isEmpty();
+
+    /**
+     * Returns an Enumeration of the figures contained in this figure
+     */
+    public FigureEnumeration figures();
+
+    /**
+     * Returns the figure that contains the given point.
+     */
+    public Figure findFigureInside(int x, int y);
+
+    /**
+     * Checks if a point is inside the figure.
+     */
+    public boolean containsPoint(int x, int y);
+
+    /**
+     * Returns a Clone of this figure
+     */
+    public Object clone();
+
+    /**
+     * Changes the display box of a figure. This is a
+     * convenience method. Implementors should only
+     * have to override basicDisplayBox
+     * @see #displayBox
+     */
+    public void displayBox(Rectangle r);
+
+    /**
+     * Checks whether the given figure is contained in this figure.
+     */
+    public boolean includes(Figure figure);
+
+    /**
+     * Decomposes a figure into its parts. A figure is considered
+     * as a part of itself.
+     */
+    public FigureEnumeration decompose();
+
+    /**
+     * Sets the Figure's container and registers the container
+     * as a figure change listener. A figure's container can be
+     * any kind of FigureChangeListener. A figure is not restricted
+     * to have a single container.
+     */
+    public void addToContainer(FigureChangeListener c);
+
+    /**
+     * Removes a figure from the given container and unregisters
+     * it as a change listener.
+     */
+    public void removeFromContainer(FigureChangeListener c);
+
+    /**
+     * Gets the Figure's listeners.
+     */
+    public FigureChangeListener listener();
+
+    /**
+     * Adds a listener for this figure.
+     */
+    public void addFigureChangeListener(FigureChangeListener l);
+
+    /**
+     * Removes a listener for this figure.
+     */
+    public void removeFigureChangeListener(FigureChangeListener l);
+
+    /**
+     * Releases a figure's resources. Release is called when
+     * a figure is removed from a drawing. Informs the listeners that
+     * the figure is removed by calling figureRemoved.
+     */
+    public void release();
+
+    /**
+     * Invalidates the figure. This method informs its listeners
+     * that its current display box is invalid and should be
+     * refreshed.
+     */
+    public void invalidate();
+
+    /**
+     * Informes that a figure is about to change such that its
+     * display box is affected.
+     * Here is an example of how it is used together with changed()
+     * <pre>
+     * public void move(int x, int y) {
+     *      willChange();
+     *      // change the figure's location
+     *      changed();
+     *  }
+     * </pre>
+     * @see #invalidate
+     * @see #changed
+     */
+    public void willChange();
+
+    /**
+     * Informes that a figure has changed its display box.
+     * This method also triggers an update call for its
+     * registered observers.
+     * @see #invalidate
+     * @see #willChange
+     *
+     */
+    public void changed();
+
+    /**
+     * Checks if this figure can be connected
+     */
+    public boolean canConnect();
+
+    /**
+     * Gets a connector for this figure at the given location.
+     * A figure can have different connectors at different locations.
+     */
+    public Connector connectorAt(int x, int y);
+
+    /**
+     * Sets whether the connectors should be visible.
+     * Connectors can be optionally visible. Implement
+     * this method and react on isVisible to turn the
+     * connectors on or off.
+     */
+    public void connectorVisibility(boolean isVisible);
+
+    /**
+     * Returns the connection inset. This is only a hint that
+     * connectors can use to determine the connection location.
+     * The inset defines the area where the display box of a
+     * figure should not be connected.
+     *
+     */
+    public Insets connectionInsets();
+
+    /**
+     * Returns the locator used to located connected text.
+     */
+    public Locator connectedTextLocator(Figure text);
+
+    /**
+     * Returns the named attribute or null if a
+     * a figure doesn't have an attribute.
+     * All figures support the attribute names
+     * FillColor and FrameColor
+     */
+    public Object getAttribute(String name);
+
+    /**
+     * Sets the named attribute to the new value
+     */
+    public void setAttribute(String name, Object value);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/FigureChangeEvent.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,47 @@
+/*
+ * @(#)FigureChangeEvent.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.Rectangle;
+import java.util.EventObject;
+
+/**
+ * FigureChange event passed to FigureChangeListeners.
+ *
+ */
+public class FigureChangeEvent extends EventObject {
+
+    private Rectangle fRectangle;
+    private static final Rectangle  fgEmptyRectangle = new Rectangle(0, 0, 0, 0);
+
+   /**
+    * Constructs an event for the given source Figure. The rectangle is the
+    * area to be invalvidated.
+    */
+    public FigureChangeEvent(Figure source, Rectangle r) {
+        super(source);
+        fRectangle = r;
+    }
+
+    public FigureChangeEvent(Figure source) {
+        super(source);
+        fRectangle = fgEmptyRectangle;
+    }
+
+    /**
+     *  Gets the changed figure
+     */
+    public Figure getFigure() {
+        return (Figure)getSource();
+    }
+
+    /**
+     *  Gets the changed rectangle
+     */
+    public Rectangle getInvalidatedRectangle() {
+        return fRectangle;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/FigureChangeListener.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,43 @@
+/*
+ * @(#)FigureChangeListener.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.Rectangle;
+import java.util.EventListener;
+
+/**
+ * Listener interested in Figure changes.
+ *
+ */
+public interface FigureChangeListener extends EventListener {
+
+    /**
+     *  Sent when an area is invalid
+     */
+    public void figureInvalidated(FigureChangeEvent e);
+
+    /**
+     * Sent when a figure changed
+     */
+    public void figureChanged(FigureChangeEvent e);
+
+    /**
+     * Sent when a figure was removed
+     */
+    public void figureRemoved(FigureChangeEvent e);
+
+    /**
+     * Sent when requesting to remove a figure.
+     */
+    public void figureRequestRemove(FigureChangeEvent e);
+
+    /**
+     * Sent when an update should happen.
+     *
+     */
+    public void figureRequestUpdate(FigureChangeEvent e);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/FigureEnumeration.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,28 @@
+/*
+ * @(#)FigureEnumeration.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.util.*;
+
+/**
+ * Interface for Enumerations that access Figures.
+ * It provides a method nextFigure, that hides the down casting
+ * from client code.
+ */
+public interface FigureEnumeration extends Enumeration {
+    /**
+     * Returns the next element of the enumeration. Calls to this
+     * method will enumerate successive elements.
+     * @exception NoSuchElementException If no more elements exist.
+     */
+    public Figure nextFigure();
+
+    /**
+     * Returns true if thje enumeration contains the specified figure
+     * @param   figure      the figure to check
+     */
+    public boolean contains(Figure figure);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/FigureSelection.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,80 @@
+/*
+ * @(#)FigureSelection.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import CH.ifa.draw.util.*;
+import java.util.*;
+import java.io.*;
+
+/**
+ * FigureSelection enables to transfer the selected figures
+ * to a clipboard.<p>
+ * Will soon be converted to the JDK 1.1 Transferable interface.
+ *
+ * @see Clipboard
+ */
+
+public class FigureSelection extends Object {
+
+    private byte[] fData; // flattend figures, ready to be resurrected
+    /**
+     * The type identifier of the selection.
+     */
+    public final static String TYPE = "CH.ifa.draw.Figures";
+
+    /**
+     * Constructes the Figure selection for the vecotor of figures.
+     */
+    public FigureSelection(Vector figures) {
+        // a FigureSelection is represented as a flattened ByteStream
+        // of figures.
+        ByteArrayOutputStream output = new ByteArrayOutputStream(200);
+        StorableOutput writer = new StorableOutput(output);
+        writer.writeInt(figures.size());
+        Enumeration selected = figures.elements();
+        while (selected.hasMoreElements()) {
+            Figure figure = (Figure) selected.nextElement();
+            writer.writeStorable(figure);
+        }
+        writer.close();
+        fData = output.toByteArray();
+    }
+
+    /**
+     * Gets the type of the selection.
+     */
+    public String getType() {
+        return TYPE;
+    }
+
+    /**
+     * Gets the data of the selection. The result is returned
+     * as a Vector of Figures.
+     *
+     * @return a copy of the figure selection.
+     */
+    public Object getData(String type) {
+        if (type.equals(TYPE)) {
+            InputStream input = new ByteArrayInputStream(fData);
+            Vector result = new Vector(10);
+            StorableInput reader = new StorableInput(input);
+            int numRead = 0;
+            try {
+                int count = reader.readInt();
+                while (numRead < count) {
+                    Figure newFigure = (Figure) reader.readStorable();
+                    result.addElement(newFigure);
+                    numRead++;
+                }
+            } catch (IOException e) {
+                System.out.println(e.toString());
+            }
+            return result;
+        }
+        return null;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/HJDError.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,17 @@
+/*
+ * @(#)HJDError.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+/**
+ * A HJD Error.
+ *
+ */
+public class HJDError extends Error {
+
+    public HJDError(String msg) {
+	    super(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Handle.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,110 @@
+/*
+ * @(#)Handle.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.*;
+
+/**
+ * Handles are used to change a figure by direct manipulation.
+ * Handles know their owning figure and they provide methods to
+ * locate the handle on the figure and to track changes.
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld004.htm>Adapter</a></b><br>
+ * Handles adapt the operations to manipulate a figure to a common interface.
+ *
+ * @see Figure
+ */
+public interface Handle {
+
+    public static final int HANDLESIZE = 8;
+
+    /**
+     * Locates the handle on the figure. The handle is drawn
+     * centered around the returned point.
+     */
+    public abstract Point locate();
+
+    /**
+     * @deprecated As of version 4.1,
+     * use invokeStart(x, y, drawingView)
+     * Tracks the start of the interaction. The default implementation
+     * does nothing.
+     * @param x the x position where the interaction started
+     * @param y the y position where the interaction started
+     */
+    public void invokeStart(int  x, int  y, Drawing drawing);
+
+    /**
+     * @deprecated As of version 4.1,
+     * use invokeStart(x, y, drawingView)
+     * Tracks the start of the interaction. The default implementation
+     * does nothing.
+     * @param x the x position where the interaction started
+     * @param y the y position where the interaction started
+     * @param view the handles container
+     */
+    public void invokeStart(int  x, int  y, DrawingView view);
+
+    /**
+     * @deprecated As of version 4.1,
+     * use invokeStep(x, y, anchorX, anchorY, drawingView)
+     *
+     * Tracks a step of the interaction.
+     * @param dx x delta of this step
+     * @param dy y delta of this step
+     */
+    public void invokeStep (int dx, int dy, Drawing drawing);
+
+    /**
+     * Tracks a step of the interaction.
+     * @param x the current x position
+     * @param y the current y position
+     * @param anchorX the x position where the interaction started
+     * @param anchorY the y position where the interaction started
+     */
+    public void invokeStep (int x, int y, int anchorX, int anchorY, DrawingView view);
+
+    /**
+     * Tracks the end of the interaction.
+     * @param x the current x position
+     * @param y the current y position
+     * @param anchorX the x position where the interaction started
+     * @param anchorY the y position where the interaction started
+     */
+    public void invokeEnd(int x, int y, int anchorX, int anchorY, DrawingView view);
+
+    /**
+     * @deprecated As of version 4.1,
+     * use invokeEnd(x, y, anchorX, anchorY, drawingView).
+     *
+     * Tracks the end of the interaction.
+     */
+    public void invokeEnd  (int dx, int dy, Drawing drawing);
+
+    /**
+     * Gets the handle's owner.
+     */
+    public Figure owner();
+
+    /**
+     * Gets the display box of the handle.
+     */
+    public Rectangle displayBox();
+
+    /**
+     * Tests if a point is contained in the handle.
+     */
+    public boolean containsPoint(int x, int y);
+
+    /**
+     * Draws this handle.
+     */
+    public void draw(Graphics g);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Locator.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,30 @@
+/*
+ * @(#)Locator.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import CH.ifa.draw.util.Storable;
+import java.awt.*;
+import java.io.Serializable;
+
+/**
+ * Locators can be used to locate a position on a figure.<p>
+ *
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld034.htm>Strategy</a></b><br>
+ * Locator encapsulates the strategy to locate a handle on a figure.
+ */
+
+public interface Locator extends Storable, Serializable, Cloneable {
+
+    /**
+     * Locates a position on the passed figure.
+     * @return a point on the figure.
+     */
+    public Point locate(Figure owner);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Painter.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,32 @@
+/*
+ * @(#)Painter.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.*;
+import java.io.Serializable;
+
+/**
+ * Painter defines the interface for drawing a layer
+ * into a DrawingView.<p>
+ *
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld034.htm>Strategy</a></b><br>
+ * Painter encapsulates an algorithm to render something in
+ * the DrawingView. The DrawingView plays the role of the StrategyContext.
+ * <hr>
+ * @see DrawingView
+ */
+
+public interface Painter extends Serializable {
+
+    /**
+     * Draws into the given DrawingView.
+     */
+    public void draw(Graphics g, DrawingView view);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/PointConstrainer.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,40 @@
+/*
+ * @(#)PointConstrainer.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.*;
+
+/**
+ * Interface to constrain a Point. This can be used to implement
+ * different kinds of grids.
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld034.htm>Strategy</a></b><br>
+ * DrawingView is the StrategyContext.<br>
+ *
+ * @see DrawingView
+ */
+
+
+public interface PointConstrainer {
+    /**
+     * Constrains the given point.
+     * @return constrained point.
+     */
+    public Point constrainPoint(Point p);
+
+    /**
+     * Gets the x offset to move an object.
+     */
+    public int getStepX();
+
+    /**
+     * Gets the y offset to move an object.
+     */
+    public int getStepY();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/framework/Tool.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,69 @@
+/*
+ * @(#)Tool.java 5.1
+ *
+ */
+
+package CH.ifa.draw.framework;
+
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.awt.event.KeyEvent;
+
+/**
+ * A tool defines a mode of the drawing view. All input events
+ * targeted to the drawing view are forwarded to its current tool.<p>
+ * Tools inform their editor when they are done with an interaction
+ * by calling the editor's toolDone() method.
+ * The Tools are created once and reused. They
+ * are initialized/deinitialized with activate()/deactivate().
+ * <hr>
+ * <b>Design Patterns</b><P>
+ * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
+ * <b><a href=../pattlets/sld032.htm>State</a></b><br>
+ * Tool plays the role of the State. In encapsulates all state
+ * specific behavior. DrawingView plays the role of the StateContext.
+ * @see DrawingView
+ */
+
+public interface Tool {
+
+    /**
+     * Activates the tool for the given view. This method is called
+     * whenever the user switches to this tool. Use this method to
+     * reinitialize a tool.
+     */
+    public void activate();
+
+    /**
+     * Deactivates the tool. This method is called whenever the user
+     * switches to another tool. Use this method to do some clean-up
+     * when the tool is switched. Subclassers should always call
+     * super.deactivate.
+     */
+    public void deactivate();
+
+    /**
+     * Handles mouse down events in the drawing view.
+     */
+    public void mouseDown(MouseEvent e, int x, int y);
+
+    /**
+     * Handles mouse drag events in the drawing view.
+     */
+    public void mouseDrag(MouseEvent e, int x, int y);
+
+    /**
+     * Handles mouse up in the drawing view.
+     */
+    public void mouseUp(MouseEvent e, int x, int y);
+
+    /**
+     * Handles mouse moves (if the mouse button is up).
+     */
+    public void mouseMove(MouseEvent evt, int x, int y);
+
+    /**
+     * Handles key down events in the drawing view.
+     */
+    public void keyDown(KeyEvent evt, int key);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/TWikiDrawPlugin/src/CH/ifa/draw/images/StaticImages.java	Tue Dec 27 23:09:56 2005 +0000
@@ -0,0 +1,1048 @@
+package CH.ifa.draw.images;
+
+import java.util.Hashtable;
+import java.io.*;
+
+public class StaticImages {
+    static final byte[] SEL1 = new byte[] {
+	71,73,70,56,57,97,24,0,
+	24,0,-77,0,0,0,0,0,
+	-65,0,0,0,-65,0,-65,-65,
+	0,0,0,-65,-65,0,-65,0,
+	-65,-65,-64,-64,-64,-128,-128,-128,
+	-1,0,0,0,-1,0,-1,-1,
+	0,0,0,-1,-1,0,-1,0,
+	-1,-1,-1,-1,-1,44,0,0,
+	0,0,24,0,24,0,64,4,
+	111,-16,-128,73,-85,-67,64,-66,
+	-61,59,120,-32,-109,117,29,-126,
+	76,27,-87,106,99,121,126,-21,
+	-76,-86,38,26,-125,95,75,-42,
+	-16,-20,-69,-74,-33,-113,-105,34,
+	125,68,-61,87,-47,19,10,-23,
+	14,-60,-40,40,-73,-118,10,89,
+	-69,-105,105,-53,-19,122,-119,-51,
+	-80,56,-84,-116,93,-95,101,21,
+	53,25,52,58,125,86,119,-13,
+	-119,110,51,-99,-108,-84,-99,-109,
+	59,-46,-29,30,119,79,-128,51,
+	126,122,61,66,50,-121,15,95,
+	-115,94,24,-112,-111,0,17,0,
+	0,59};
+    static final byte[] SEL2 = new byte[] {
+	71,73,70,56,57,97,24,0,
+	24,0,-77,0,0,0,0,0,
+	-65,0,0,0,-65,0,-65,-65,
+	0,0,0,-65,-65,0,-65,0,
+	-65,-65,-64,-64,-64,-128,-128,-128,
+	-1,0,0,0,-1,0,-1,-1,
+	0,0,0,-1,-1,0,-1,0,
+	-1,-1,-1,-1,-1,44,0,0,
+	0,0,24,0,24,0,64,4,
+	107,-16,-128,73,-85,-67,64,34,
+	116,-70,-105,-49,-109,125,-33,-76,
+	-111,-35,-108,-118,-88,-55,-95,112,
+	11,-100,109,104,-77,-27,-4,-58,
+	30,-128,-89,-70,-115,112,72,36,
+	74,116,48,21,-17,72,35,-7,
+	108,35,39,-78,38,-6,-27,-102,
+	60,95,20,-72,91,-58,92,-59,
+	112,-40,-27,-51,78,-99,101,114,
+	109,43,-61,-10,-96,-33,115,-23,
+	102,-27,118,-33,80,-54,-11,-66,
+	-102,-124,-40,76,124,18,120,108,
+	106,75,79,91,-122,-121,-123,24,
+	-115,-114,17,0,0,59};
+    static final byte[] SEL3 = new byte[] {
+	71,73,70,56,57,97,24,0,
+	24,0,-77,0,0,0,0,0,
+	-65,0,0,0,-65,0,-65,-65,
+	0,0,0,-65,-65,0,-65,0,
+	-65,-65,-64,-64,-64,-128,-128,-128,
+	-1,0,0,0,-1,0,-1,-1,
+	0,0,0,-1,-1,0,-1,0,
+	-1,-1,-1,-1,-1,44,0,0,
+	0,0,24,0,24,0,64,4,
+	117,-16,-128,73,-85,-67,64,-94,
+	-13,-72,7,79,8,32,93,-7,
+	-115,-98,57,118,-32,3,-90,18,
+	106,-78,36,60,-57,-37,-35,-118,
+	84,-51,-54,54,-40,68,7,-68,
+	25,-123,-59,-113,-53,23,-100,-28,
+	-124,-95,-24,10,-7,84,-71,90,
+	25,98,53,-24,18,81,-71,96,
+	-43,8,65,46,-101,-49,104,-25,
+	113,-99,82,-37,-36,107,120,91,
+	-60,-100,-55,75,-69,-91,-15,-50,
+	-55,-45,-19,83,118,34,122,111,
+	-127,109,43,32,47,-128,117,77,
+	-124,120,-122,113,93,-121,-116,108,
+	-128,24,-105,-105,17,0,0,59,
+    };
+    static final byte[] TEXT1 = new byte[] {
+	71,73,70,56,57,97,24,0,
+	24,0,-77,0,0,0,0,0,
+	-65,0,0,0,-65,0,-65,-65,
+	0,0,0,-65,-65,0,-65,0,
+	-65,-65,-64,-64,-64,-128,-128,-128,
+	-1,0,0,0,-1,0,-1,-1,
+	0,0,0,-1,-1,0,-1,0,
+	-1,-1,-1,-1,-1,44,0,0,
+	0,0,24,0,24,0,64,4,
+	118,-16,-128,73,-85,-67,64,-66,
+	-61,-69,4,-56,-124,120,28,18,
+	2,27,55,-111,-35,57,30,-26,
+	-108,-78,-46,-53,-58,40,-71,-74,
+	-94,-121,-49,-76,32,-20,4,20,
+	-34,-120,65,-118,-51,-121,-44,101,
+	96,61,-90,-52,-77,-13,69,-121,
+	83,99,16,103,-22,122,-65,-32,
+	-33,99,76,46,-101,-51,77,-19,
+	49,123,-85,-110,126,-76,-109,-69,
+	-107,-26,-115,40,-21,-100,-43,-122,
+	-105,-22,75,24,125,37,105,33,
+	75,29,-126,112,31,115,42,21,
+	88,127,106,126,15,97,-109,96,
+	25,-127,-105,22,7,17,0,0,
+	59};
+    static final byte[] TEXT2 = new byte[] {
+	71,73,70,56,57,97,24,0,
+	24,0,-77,0,0,0,0,0,
+	-65,0,0,0,-65,0,-65,-65,
+	0,0,0,-65,-65,0,-65,0,
+	-65,-65,-64,-64,-64,-128,-128,-128,
+	-1,0,0,0,-1,0,-1,-1,
+	0,0,0,-1,-1,0,-1,0,
+	-1,-1,-1,-1,-1,44,0,0,
+	0,0,24,0,24,0,64,4,
+	106,-16,-128,73,-85,-67,64,34,
+	116,-70,-9,20,-9,117,-45,54,
+	78,-29,-127,-108,32,96,-90,112,
+	90,-118,103,-90,-78,-33,28,-17,
+	-19,-26,-1,-64,32,71,-73,115,
+	-59,-120,50,23,46,-25,-94,-75,
+	62,43,99,-49,-119,-126,69,69,
+	75,-34,-79,41,-20,2,-111,-38,
+	-28,43,44,118,-114,-94,-37,-15,
+	-71,2,3,123,86,-61,42,83,
+	-35,-127,-73,108,-45,53,70,46,
+	105,66,-91,53,120,72,108,73,
+	20,125,116,100,36,125,123,-116,
+	-122,17,0,0,59};
+    static final byte[] TEXT3 = new byte[] {
+	71,73,70,56,57,97,24,0,
+	24,0,-77,0,0,0,0,0,
+	-65,0,0,0,-65,0,-65,-65,
+	0,0,0,-65,-65,0,-65,0,
+	-65,-65,-64,-64,-64,-128,-128,-128,
+	-1,0,0,0,-1,0,-1,-1,
+	0,0,0,-1,-1,0,-1,0,
+	-1,-1,-1,-1,-1,44,0,0,
+	0,0,24,0,24,0,64,4,
+	111,-16,-128,73,-85,-67,64,-94,
+	-13,-72,127,72,-43,-115,-34,-76,
+	-115,33,66,-114,20,105,126,-16,
+	26,75,-64,-7,-91,-20,36,-65,
+	114,63,-13,51,-97,-85,22,60,
+	-124,50,62,32,41,69,67,-62,
+	-108,70,12,-27,73,-20,-120,98,
+	22,107,-75,40,-92,33,-66,-32,
+	-80,120,124,84,113,-71,-90,110,
+	77,-104,14,82,108,-85,-10,-82,
+	38,-1,-44,115,-100,50,118,77,
+	93,-34,-1,78,40,74,117,19,
+	7,51,101,57,102,93,108,124,
+	-117,110,77,82,-111,-123,17,0,
+	0,59};
+    static final byte[] ATEXT1 = new byte[] {
+	71,73,70,56,57,97,24,0,
+	24,0,-77,0,0,0,0,0,
+	-65,0,0,0,-65,0,-65,-65,
+	0,0,0,-65,-65,0,-65,0,
+	-65,-65,-64,-64,-64,-128,-128,-128,
+	-1,0,0,0,-1,0,-1,-1,
+	0,0,0,-1,-1,0,-1,0,
+	-1,-1,-1,-1,-1,44,0,0,
+	0,0,24,0,24,0,64,4,
+	120,-16,-128,73,-85,-67,64,-66,
+	-61,-69,4,-56,-124,120,28,18,
+	2,27,55,-111,-35,57,30,-26,
+	-12,84,36,-56,-106,103,-38,-83,
+	-83,-24,-59,-88,-101,-16,-105,-77,
+	12,73,-64,-103,-118,71,121,33,
+	115,44,-98,-21,41,-13,-16,126,
+	62,92,-11,40,4,-102,-66,-32,
+	-80,56,-7,40,-101,-49,104,116,
+	-15,-54,-43,-94,50,-108,94,-26,
+	-106,-76,-106,104,-84,58,-46,22,
+	-89,6,-79,78,125,45,80,114,
+	24,115,110,58,33,78,86,87,