summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util
diff options
context:
space:
mode:
Diffstat (limited to 'projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util')
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java126
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java160
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java520
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java226
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java89
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java457
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java368
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java203
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java521
-rw-r--r--projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/package.html6
10 files changed, 2676 insertions, 0 deletions
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java
new file mode 100644
index 0000000..4412dbc
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java
@@ -0,0 +1,126 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+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.
+
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Hashtable;
+
+/**
+ * ClassGrabber is a class loader used by WindowGrabber.
+ * It simply loads classes by filename and nothing more.
+ * It exists mainly because the java 1.1 class loading
+ * framework doesn't easily allow the creation of class
+ * loaders nor the loading of arbitrary classes.
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ * $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+ */
+public class ClassGrabber extends ClassLoader
+{
+ Hashtable classMap = new Hashtable();
+
+ public ClassGrabber()
+ {
+ super();
+ }
+
+ protected Class loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ Class c = (Class) classMap.get( name );
+
+ if ( c != null ) return c;
+
+ try
+ {
+ c = findSystemClass( name );
+ }
+ catch ( Exception exc1 )
+ {
+ // System.err.print( "findSystemClass: " + name + ": " );
+ // System.err.println( exc1 );
+ }
+
+ if ( c != null ) return c;
+
+ try
+ {
+ c = findLoadedClass( name );
+ }
+ catch ( Exception exc1 )
+ {
+ // System.err.print( "findLoadedClass: " + name + ": " );
+ // System.err.println( exc1 );
+ }
+
+ if ( c != null ) return c;
+
+ try
+ {
+ InputStream input = new BufferedInputStream( new FileInputStream( name ) );
+ ByteArrayOutputStream output = new ByteArrayOutputStream( 200 );
+ int ch;
+ while ( ( ch = input.read() ) != -1 )
+ {
+ output.write( ch );
+ }
+ byte[] data = output.toByteArray();
+ c = defineClass( null, data, 0, data.length );
+ }
+ catch ( Exception exc )
+ {
+ System.err.print( "getResource: " + name + ": " );
+ System.err.println( exc );
+ c = null;
+ }
+
+ if ( c != null )
+ {
+ classMap.put( name, c );
+ if ( resolve ) resolveClass( c );
+ }
+
+ return c;
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/18 23:19:05 cgruber
+ * Update imports and maven dependencies.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:53 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1.1.1 2000/12/21 15:51:18 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:45 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java
new file mode 100644
index 0000000..c63157d
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java
@@ -0,0 +1,160 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+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.
+
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.URL;
+
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRootPane;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+
+/**
+* Visually highlights a component with the specified image for a brief period.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 904 $
+* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+*/
+public class ComponentHighlighter implements ActionListener
+{
+ // lots of state to track
+ JRootPane rootPane;
+ JComponent component;
+ Component oldGlassPane;
+ JLabel imageLabel;
+ Timer timer;
+ JPanel glassPane;
+
+/**
+* Alternate "Fire-and-forget" constructor loads an image from a URL.
+* @param aComponent A Component that will be highlighted.
+* @param aURL A URL pointing to an image.
+*/
+ public ComponentHighlighter( JComponent aComponent, URL aURL )
+ {
+ if ( aURL == null ) return;
+ init( aComponent, Toolkit.getDefaultToolkit().getImage( aURL ) );
+ }
+
+/**
+* "Fire-and-forget" constructor.
+* @param aComponent A Component that will be highlighted.
+* @param anImage An image, preferably an animated GIF with transparency,
+* that will slide along the length of the component.
+*/
+ public ComponentHighlighter( JComponent aComponent, Image anImage )
+ {
+ init( aComponent, anImage );
+ }
+
+ protected void init( JComponent aComponent, Image anImage )
+ {
+ if ( ( aComponent == null ) || ( anImage == null ) ) return;
+
+ component = aComponent;
+ rootPane = SwingUtilities.getRootPane( component );
+ oldGlassPane = rootPane.getGlassPane();
+
+ glassPane = new JPanel();
+ rootPane.setGlassPane( glassPane );
+ glassPane.setVisible( true );
+ glassPane.setOpaque( false );
+ glassPane.setLayout( null );
+
+ ImageIcon icon = new ImageIcon( anImage );
+
+ imageLabel = new JLabel();
+ imageLabel.setIconTextGap( 0 );
+ imageLabel.setIcon( icon );
+ imageLabel.setSize( icon.getIconWidth(), icon.getIconHeight() );
+ glassPane.add( imageLabel );
+
+ Rectangle bounds = component.getBounds();
+ if ( component.getParent() instanceof Component )
+ {
+ bounds = SwingUtilities.convertRectangle( (Container) component.getParent(),
+ bounds, rootPane.getContentPane() );
+ }
+ imageLabel.setLocation(
+ bounds.x, bounds.y + bounds.height - imageLabel.getBounds().height );
+
+ glassPane.revalidate();
+ glassPane.repaint();
+
+ component.transferFocus(); // halts a caret, if necessary
+
+ timer = new Timer( 80, this );
+ timer.setRepeats( true );
+ timer.start();
+ }
+
+ public void actionPerformed( ActionEvent evt )
+ {
+ Rectangle bounds = imageLabel.getBounds();
+ Rectangle target = component.getBounds();
+ if ( component.getParent() instanceof Component )
+ {
+ target = SwingUtilities.convertRectangle( (Container) component.getParent(),
+ target, rootPane.getContentPane() );
+ }
+
+ if ( bounds.x + bounds.width > target.x + target.width )
+ { // clean up and end
+ timer.stop();
+ rootPane.setGlassPane( oldGlassPane );
+ component.requestFocus();
+ return;
+ }
+
+ // else, slide to the right and continue
+ imageLabel.setLocation(
+ bounds.x + Math.max( bounds.width / 12, 1 ), bounds.y );
+ imageLabel.repaint();
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/18 23:19:05 cgruber
+ * Update imports and maven dependencies.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:51:18 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:45 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java
new file mode 100644
index 0000000..82fd897
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java
@@ -0,0 +1,520 @@
+/*
+ The source code in this file, GIFEncoder.java,
+ belongs to the public domain.
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.awt.AWTException;
+import java.awt.Image;
+import java.awt.image.PixelGrabber;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * GIFEncoder is a class which takes an image and saves it to a stream
+ * using the GIF file format. A GIFEncoder is constructed with either
+ * an AWT Image (which must be fully loaded) or a set of RGB arrays.
+ * The image can be written out with a call to <CODE>write</CODE>.<br><br>
+ *
+ * Three caveats:
+ * <UL>
+ * <LI>GIFEncoder will convert the image to indexed color upon
+ * construction, and this is not fast.
+ *
+ * <LI>The image cannot have more than 256 colors, since GIF is an 8
+ * bit format.
+ *
+ * <LI>Since the image must be completely loaded into memory,
+ * there may be problems with large images.
+ * </UL>
+ *
+ * This implementation is heavily based on code made available by
+ * Adam Doppelt, which was based upon gifsave.c, which was written
+ * and released by Sverre H. Huseby.
+ *
+ * @author amd@brown.edu
+ * @author sverrehu@ifi.uio.no
+ * @author michael@mpowers.net
+ */
+public class GIFEncoder
+{
+ short width_, height_;
+ int numColors_;
+ byte pixels_[], colors_[];
+
+ ScreenDescriptor sd_;
+ ImageDescriptor id_;
+
+/**
+ * Construct a GIFEncoder. The constructor will convert the image to
+ * an indexed color array. This may take some time. If more than 256
+ * colors are encountered, all subsequent colors are mapped to the first
+ * color encountered.
+ * @param image The image to encode. The image must be completely loaded.
+ * @exception AWTException Will be thrown if the pixel grab fails. This
+ * can happen if Java runs out of memory.
+ * */
+ public GIFEncoder(Image image) throws AWTException
+ {
+ width_ = (short)image.getWidth(null);
+ height_ = (short)image.getHeight(null);
+
+ int values[] = new int[width_ * height_];
+ PixelGrabber grabber = new PixelGrabber(
+ image, 0, 0, width_, height_, values, 0, width_);
+
+ try
+ {
+ if(grabber.grabPixels() != true)
+ throw new AWTException("Grabber returned false: " +
+ grabber.status());
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ byte r[][] = new byte[width_][height_];
+ byte g[][] = new byte[width_][height_];
+ byte b[][] = new byte[width_][height_];
+ int index = 0;
+ for (int y = 0; y < height_; ++y)
+ {
+ for (int x = 0; x < width_; ++x)
+ {
+ r[x][y] = (byte)((values[index] >> 16) & 0xFF);
+ g[x][y] = (byte)((values[index] >> 8) & 0xFF);
+ b[x][y] = (byte)((values[index]) & 0xFF);
+ ++index;
+ }
+ }
+ toIndexedColor(r, g, b);
+ }
+
+/**
+ * Construct a GIFEncoder. The constructor will convert the image to
+ * an indexed color array. This may take some time. <br><br>
+ * Each array stores intensity values for the image. In other words,
+ * r[x][y] refers to the red intensity of the pixel at column x, row y.
+ * @param r An array containing the red intensity values.
+ * @param g An array containing the green intensity values.
+ * @param b An array containing the blue intensity values.
+ *
+ * @exception AWTException Will be thrown if the image contains more than
+ * 256 colors.
+ * */
+ public GIFEncoder(byte r[][], byte g[][], byte b[][]) throws AWTException
+ {
+ width_ = (short)(r.length);
+ height_ = (short)(r[0].length);
+
+ toIndexedColor(r, g, b);
+ }
+
+/**
+ * Writes the image out to a stream in the GIF file format. This will
+ * be a single GIF87a image, non-interlaced, with no background color.
+ * <B>This may take some time.</B><P>
+ *
+ * @param output The stream to output to. This should probably be a
+ * buffered stream.
+ *
+ * @exception IOException Will be thrown if a write operation fails.
+ * */
+ public void write(OutputStream output) throws IOException
+ {
+ BitUtils.writeString(output, "GIF87a");
+ ScreenDescriptor sd = new ScreenDescriptor(width_, height_,
+ numColors_);
+ sd.write(output);
+
+ output.write(colors_, 0, colors_.length);
+
+ ImageDescriptor id = new ImageDescriptor(width_, height_, ',');
+ id.write(output);
+
+ byte codesize = BitUtils.bitsNeeded(numColors_);
+ if (codesize == 1)
+ ++codesize;
+ output.write(codesize);
+
+ LZWCompressor.LZWCompress(output, codesize, pixels_);
+ output.write(0);
+ id = new ImageDescriptor((byte)0, (byte)0, ';');
+ id.write(output);
+ output.flush();
+ }
+
+ void toIndexedColor(byte r[][], byte g[][],
+ byte b[][]) throws AWTException
+ {
+ pixels_ = new byte[width_ * height_];
+ colors_ = new byte[256 * 3];
+ int colornum = 0;
+ for (int x = 0; x < width_; ++x)
+ {
+ for (int y = 0; y < height_; ++y)
+ {
+ int search;
+ for (search = 0; search < colornum; ++search)
+ if (colors_[search * 3] == r[x][y] &&
+ colors_[search * 3 + 1] == g[x][y] &&
+ colors_[search * 3 + 2] == b[x][y])
+ break;
+
+ if (search > 255)
+ search = 0;
+ //throw new AWTException("Too many colors.");
+
+ pixels_[y * width_ + x] = (byte)search;
+
+ if (search == colornum) {
+ colors_[search * 3] = r[x][y];
+ colors_[search * 3 + 1] = g[x][y];
+ colors_[search * 3 + 2] = b[x][y];
+ ++colornum;
+ }
+ }
+ }
+
+ numColors_ = 1 << BitUtils.bitsNeeded(colornum);
+ byte copy[] = new byte[numColors_ * 3];
+ System.arraycopy(colors_, 0, copy, 0, numColors_ * 3);
+ colors_ = copy;
+ }
+
+}
+
+class BitFile
+{
+ OutputStream output_;
+ byte buffer_[];
+ int index_, bitsLeft_;
+
+ public BitFile(OutputStream output)
+ {
+ output_ = output;
+ buffer_ = new byte[256];
+ index_ = 0;
+ bitsLeft_ = 8;
+ }
+
+ public void flush() throws IOException
+ {
+ int numBytes = index_ + (bitsLeft_ == 8 ? 0 : 1);
+ if (numBytes > 0)
+ {
+ output_.write(numBytes);
+ output_.write(buffer_, 0, numBytes);
+ buffer_[0] = 0;
+ index_ = 0;
+ bitsLeft_ = 8;
+ }
+ }
+
+ public void writeBits(int bits, int numbits) throws IOException {
+ int bitsWritten = 0;
+ int numBytes = 255;
+ do
+ {
+ if ((index_ == 254 && bitsLeft_ == 0) || index_ > 254)
+ {
+ output_.write(numBytes);
+ output_.write(buffer_, 0, numBytes);
+
+ buffer_[0] = 0;
+ index_ = 0;
+ bitsLeft_ = 8;
+ }
+
+ if (numbits <= bitsLeft_)
+ {
+ buffer_[index_] |= (bits & ((1 << numbits) - 1)) <<
+ (8 - bitsLeft_);
+ bitsWritten += numbits;
+ bitsLeft_ -= numbits;
+ numbits = 0;
+ }
+ else
+ {
+ buffer_[index_] |= (bits & ((1 << bitsLeft_) - 1)) <<
+ (8 - bitsLeft_);
+ bitsWritten += bitsLeft_;
+ bits >>= bitsLeft_;
+ numbits -= bitsLeft_;
+ buffer_[++index_] = 0;
+ bitsLeft_ = 8;
+ }
+
+ }
+ while (numbits != 0);
+ }
+}
+
+class LZWStringTable
+{
+ private final static int RES_CODES = 2;
+ private final static short HASH_FREE = (short)0xFFFF;
+ private final static short NEXT_FIRST = (short)0xFFFF;
+ private final static int MAXBITS = 12;
+ private final static int MAXSTR = (1 << MAXBITS);
+ private final static short HASHSIZE = 9973;
+ private final static short HASHSTEP = 2039;
+
+ byte strChr_[];
+ short strNxt_[];
+ short strHsh_[];
+ short numStrings_;
+
+ public LZWStringTable()
+ {
+ strChr_ = new byte[MAXSTR];
+ strNxt_ = new short[MAXSTR];
+ strHsh_ = new short[HASHSIZE];
+ }
+
+ public int addCharString(short index, byte b)
+ {
+ int hshidx;
+
+ if (numStrings_ >= MAXSTR)
+ return 0xFFFF;
+
+ hshidx = Hash(index, b);
+ while (strHsh_[hshidx] != HASH_FREE)
+ hshidx = (hshidx + HASHSTEP) % HASHSIZE;
+
+ strHsh_[hshidx] = numStrings_;
+ strChr_[numStrings_] = b;
+ strNxt_[numStrings_] = (index != HASH_FREE) ? index : NEXT_FIRST;
+
+ return numStrings_++;
+ }
+
+ public short findCharString(short index, byte b)
+ {
+ int hshidx, nxtidx;
+
+ if (index == HASH_FREE)
+ return b;
+
+ hshidx = Hash(index, b);
+ while ((nxtidx = strHsh_[hshidx]) != HASH_FREE)
+ {
+ if (strNxt_[nxtidx] == index && strChr_[nxtidx] == b)
+ return (short)nxtidx;
+ hshidx = (hshidx + HASHSTEP) % HASHSIZE;
+ }
+
+ return (short)0xFFFF;
+ }
+
+ public void clearTable(int codesize)
+ {
+ numStrings_ = 0;
+
+ for (int q = 0; q < HASHSIZE; q++)
+ {
+ strHsh_[q] = HASH_FREE;
+ }
+
+ int w = (1 << codesize) + RES_CODES;
+ for (int q = 0; q < w; q++)
+ {
+ addCharString((short)0xFFFF, (byte)q);
+ }
+ }
+
+ static public int Hash(short index, byte lastbyte)
+ {
+ return ((int)((short)(lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
+ }
+}
+
+class LZWCompressor {
+
+ public static void LZWCompress(OutputStream output, int codesize,
+ byte toCompress[]) throws IOException
+ {
+ byte c;
+ short index;
+ int clearcode, endofinfo, numbits, limit, errcode;
+ short prefix = (short)0xFFFF;
+
+ BitFile bitFile = new BitFile(output);
+ LZWStringTable strings = new LZWStringTable();
+
+ clearcode = 1 << codesize;
+ endofinfo = clearcode + 1;
+
+ numbits = codesize + 1;
+ limit = (1 << numbits) - 1;
+ strings.clearTable(codesize);
+ bitFile.writeBits(clearcode, numbits);
+
+ for (int loop = 0; loop < toCompress.length; ++loop)
+ {
+ c = toCompress[loop];
+ if ((index = strings.findCharString(prefix, c)) != -1)
+ {
+ prefix = index;
+ }
+ else
+ {
+ bitFile.writeBits(prefix, numbits);
+ if (strings.addCharString(prefix, c) > limit) {
+ if (++numbits > 12) {
+ bitFile.writeBits(clearcode, numbits - 1);
+ strings.clearTable(codesize);
+ numbits = codesize + 1;
+ }
+ limit = (1 << numbits) - 1;
+ }
+
+ prefix = (short)((short)c & 0xFF);
+ }
+ }
+
+ if (prefix != -1)
+ bitFile.writeBits(prefix, numbits);
+
+ bitFile.writeBits(endofinfo, numbits);
+ bitFile.flush();
+ }
+}
+
+class ScreenDescriptor
+{
+ public short localScreenWidth_, localScreenHeight_;
+ private byte byte_;
+ public byte backgroundColorIndex_, pixelAspectRatio_;
+
+ public ScreenDescriptor(short width, short height, int numColors)
+ {
+ localScreenWidth_ = width;
+ localScreenHeight_ = height;
+ setGlobalColorTableSize((byte)(BitUtils.bitsNeeded(numColors) - 1));
+ setGlobalColorTableFlag((byte)1);
+ setSortFlag((byte)0);
+ setColorResolution((byte)7);
+ backgroundColorIndex_ = 0;
+ pixelAspectRatio_ = 0;
+ }
+
+ public void write(OutputStream output) throws IOException
+ {
+ BitUtils.writeWord(output, localScreenWidth_);
+ BitUtils.writeWord(output, localScreenHeight_);
+ output.write(byte_);
+ output.write(backgroundColorIndex_);
+ output.write(pixelAspectRatio_);
+ }
+
+ public void setGlobalColorTableSize(byte num)
+ {
+ byte_ |= (num & 7);
+ }
+
+ public void setSortFlag(byte num)
+ {
+ byte_ |= (num & 1) << 3;
+ }
+
+ public void setColorResolution(byte num)
+ {
+ byte_ |= (num & 7) << 4;
+ }
+
+ public void setGlobalColorTableFlag(byte num)
+ {
+ byte_ |= (num & 1) << 7;
+ }
+}
+
+class ImageDescriptor
+{
+ public byte separator_;
+ public short leftPosition_, topPosition_, width_, height_;
+ private byte byte_;
+
+ public ImageDescriptor(short width, short height, char separator)
+ {
+ separator_ = (byte)separator;
+ leftPosition_ = 0;
+ topPosition_ = 0;
+ width_ = width;
+ height_ = height;
+ setLocalColorTableSize((byte)0);
+ setReserved((byte)0);
+ setSortFlag((byte)0);
+ setInterlaceFlag((byte)0);
+ setLocalColorTableFlag((byte)0);
+ }
+
+ public void write(OutputStream output) throws IOException
+ {
+ output.write(separator_);
+ BitUtils.writeWord(output, leftPosition_);
+ BitUtils.writeWord(output, topPosition_);
+ BitUtils.writeWord(output, width_);
+ BitUtils.writeWord(output, height_);
+ output.write(byte_);
+ }
+
+ public void setLocalColorTableSize(byte num)
+ {
+ byte_ |= (num & 7);
+ }
+
+ public void setReserved(byte num)
+ {
+ byte_ |= (num & 3) << 3;
+ }
+
+ public void setSortFlag(byte num)
+ {
+ byte_ |= (num & 1) << 5;
+ }
+
+ public void setInterlaceFlag(byte num)
+ {
+ byte_ |= (num & 1) << 6;
+ }
+
+ public void setLocalColorTableFlag(byte num)
+ {
+ byte_ |= (num & 1) << 7;
+ }
+}
+
+class BitUtils
+{
+ public static byte bitsNeeded(int n)
+ {
+ byte ret = 1;
+
+ if (n-- == 0)
+ return 0;
+
+ while ((n >>= 1) != 0)
+ ++ret;
+
+ return ret;
+ }
+
+ public static void writeWord(OutputStream output,
+ short w) throws IOException
+ {
+ output.write(w & 0xFF);
+ output.write((w >> 8) & 0xFF);
+ }
+
+ static void writeString(OutputStream output,
+ String string) throws IOException
+ {
+ for (int loop = 0; loop < string.length(); ++loop)
+ output.write((byte)(string.charAt(loop)));
+ }
+}
+
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java
new file mode 100644
index 0000000..6c8d7ee
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java
@@ -0,0 +1,226 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+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.
+
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.lang.reflect.Method;
+
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.KeyStroke;
+import javax.swing.border.EmptyBorder;
+import javax.swing.table.TableModel;
+
+import net.wotonomy.ui.swing.components.PropertyEditorTable;
+import net.wotonomy.ui.swing.components.PropertyEditorTableModel;
+
+/**
+* The ObjectInspector displays a JFrame containing
+* a PropertyEditorTable that displays an object. <br><br>
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 904 $
+*/
+
+public class ObjectInspector implements ActionListener, MouseListener
+{
+ protected JTable table = null;
+
+ // key command to copy contents to clipboard
+ static public final String COPY = "COPY";
+
+ /**
+ * Displays the specified object in a frame.
+ */
+ public ObjectInspector( Object anObject )
+ {
+ initLayout( anObject );
+ }
+
+ protected void initLayout( Object aTargetObject )
+ {
+ PropertyEditorTableModel model =
+ new PropertyEditorTableModel();
+ model.setObject( aTargetObject );
+ table = new PropertyEditorTable()
+ {
+ public void methodInvoked( Object anObject, Method aMethod, Object aResult )
+ {
+ if
+ ( ( aResult == null )
+ || ( aResult instanceof Number )
+ || ( aResult instanceof Boolean )
+ || ( aResult instanceof String ) )
+ {
+ System.out.println( aMethod.getName() + ": " + aResult );
+ }
+ else
+ {
+ new ObjectInspector( aResult );
+ }
+ }
+ };
+ table.setModel( model );
+ table.addMouseListener( this ); // listen for double-clicks
+
+ // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
+ table.registerKeyboardAction( this, COPY,
+ KeyStroke.getKeyStroke( KeyEvent.VK_C,
+ Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
+ table.registerKeyboardAction( this, COPY,
+ KeyStroke.getKeyStroke( KeyEvent.VK_X,
+ Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
+
+ JPanel panel = new JPanel();
+ panel.setBorder( new EmptyBorder( new Insets( 10, 10, 10, 10 ) ) );
+ panel.setLayout( new BorderLayout( 10, 10 ) );
+
+ JScrollPane scrollPane = new JScrollPane( table );
+ scrollPane.setPreferredSize( new Dimension( 325, 350 ) );
+ panel.add( scrollPane, BorderLayout.CENTER );
+
+ JFrame window = new JFrame();
+ window.setTitle( aTargetObject.getClass().getName() );
+ window.getContentPane().add( panel );
+
+ window.pack();
+ WindowUtilities.cascade( window );
+ window.show();
+ }
+
+ // interface MouseListener
+
+ /**
+ * Double click to call invokeFileFromString.
+ */
+
+ public void mouseClicked(MouseEvent e)
+ {
+ if ( e.getSource() == table )
+ {
+ if ( e.getClickCount() > 1 )
+ {
+ int row = table.rowAtPoint( e.getPoint() );
+ int col = table.columnAtPoint( e.getPoint() );
+
+ if ( ( row == -1 ) || ( col != 0 ) ) return;
+
+ /* do something here */
+ }
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {}
+ public void mousePressed(MouseEvent e) {}
+ public void mouseEntered(MouseEvent e) {}
+ public void mouseExited(MouseEvent e) {}
+
+
+ // interface ActionEventListener - for listening to key commands
+
+ public void actionPerformed(ActionEvent evt)
+ {
+ if ( COPY.equals( evt.getActionCommand() ) )
+ {
+ copyToClipboard();
+ return;
+ }
+ }
+
+/**
+* Copies the contents of the table to the clipboard as a tab-delimited string.
+*/
+ public void copyToClipboard()
+ {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Clipboard clipboard = toolkit.getSystemClipboard();
+ StringSelection selection =
+ new StringSelection( getTabDelimitedString() );
+ clipboard.setContents( selection, selection );
+ }
+
+ /**
+ * Converts the contents of the table to a tab-delimited string.
+ * @return A String containing the text contents of the table.
+ */
+ public String getTabDelimitedString()
+ {
+ StringBuffer result = new StringBuffer(64);
+
+ TableModel model = table.getModel();
+ int cols = model.getColumnCount();
+ int rows = model.getRowCount();
+
+ Object o = null;
+ for ( int y = 0; y < rows; y++ )
+ {
+ for ( int x = 0; x < cols; x++ )
+ {
+ o = model.getValueAt( y, x );
+ if ( o == null ) o = "";
+ result.append( o );
+ result.append( '\t' );
+ }
+ result.append( '\n' );
+ }
+
+ return result.toString();
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/18 23:19:05 cgruber
+ * Update imports and maven dependencies.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.3 2003/08/06 23:07:53 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.2 2002/11/16 16:33:31 mpowers
+ * Now using platform-specific accelerator key for shortcuts.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:51:27 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.3 2000/12/20 16:25:45 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java
new file mode 100644
index 0000000..f5fe3e4
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java
@@ -0,0 +1,89 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+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.
+
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Point;
+import java.io.Serializable;
+import java.util.Comparator;
+
+import javax.swing.SwingUtilities;
+
+/**
+* A Comparator that will sort components in a common container
+* based first on their y-coordinate and then on their x-coordinate,
+* producing a list sorted from top to bottom and left to right.
+* If all components are not in the same container, the resulting
+* sort is undefined.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 904 $
+*/
+public class PositionComparator implements Comparator, Serializable
+{
+ private Container rootContainer;
+ private transient Component c1, c2;
+ private transient Point p1, p2;
+
+/**
+* Standard constructor to configure the comparator.
+* @param aContainer The common container for all the objects to be compared.
+*/
+ public PositionComparator( Container aContainer )
+ {
+ rootContainer = aContainer;
+ }
+
+ // interface Comparable
+
+ public int compare(Object o1, Object o2)
+ {
+ c1 = (Component) o1;
+ c2 = (Component) o2;
+
+ p1 = SwingUtilities.convertPoint( c1.getParent(), c1.getLocation(), rootContainer );
+ p2 = SwingUtilities.convertPoint( c2.getParent(), c2.getLocation(), rootContainer );
+
+ if ( p1.y != p2.y )
+ {
+ return p1.y - p2.y;
+ }
+ return p1.x - p2.x;
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/18 23:19:05 cgruber
+ * Update imports and maven dependencies.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:51:27 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:45 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java
new file mode 100644
index 0000000..7e61411
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java
@@ -0,0 +1,457 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+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.
+
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.KeyStroke;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.TableModel;
+
+import net.wotonomy.ui.swing.components.MultiLineLabel;
+
+/**
+* The StackTraceInspector displays a JFrame containing
+* stack trace information for a Throwable. <br><br>
+*
+* There are also a few static methods for obtaining
+* information about the current stack, which is useful
+* for determining who's calling you at runtime.
+*
+* @author michael@mpowers.net
+* @version $Revision: 904 $
+*/
+
+public class StackTraceInspector
+ implements TableModel, MouseListener, ActionListener
+{
+ protected JTable table = null;
+ protected List tableModelListeners = null;
+ protected List methodNames = new ArrayList();
+
+ // key command to copy contents to clipboard
+ static public final String COPY = "COPY";
+
+/**
+* Displays the current stack trace at the time
+* of instantiation in a table on a frame.
+*/
+ public StackTraceInspector()
+ {
+ initLayout( parseStackTrace( new RuntimeException() ), null );
+ }
+
+/**
+* Displays the current stack trace at the time
+* of instantiation in a table on a frame
+* annotated with the specified message.
+*/
+ public StackTraceInspector( String aMessage )
+ {
+ initLayout( parseStackTrace( new RuntimeException() ), aMessage );
+ }
+
+/**
+* Displays the stack trace for the given throwable
+* in a table on a frame.
+* @param aThrowable A Throwable whose stack will be examined.
+*/
+ public StackTraceInspector( Throwable aThrowable )
+ {
+ initLayout( parseStackTrace( aThrowable ),
+ aThrowable.getClass() + ": " + aThrowable.getMessage() );
+ }
+
+/**
+* Simply displays the list items in a dialog.
+* Presumably (but not necessarily) called from
+* the other constructors. (I guess if you just
+* want a frame with strings in table, you can
+* call this.)
+* @param aStringList A List containing Strings.
+*/
+ public StackTraceInspector( List aStringList )
+ {
+ initLayout( aStringList, null );
+ }
+
+ protected void initLayout( List items, String message )
+ {
+ methodNames = new ArrayList( items );
+ table = new JTable( this ); // this class is the table model
+ table.addMouseListener( this ); // listen for double-clicks
+
+ // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
+ table.registerKeyboardAction( this, COPY,
+ KeyStroke.getKeyStroke( KeyEvent.VK_C,
+ Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
+ table.registerKeyboardAction( this, COPY,
+ KeyStroke.getKeyStroke( KeyEvent.VK_X,
+ Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
+
+ JPanel panel = new JPanel();
+ panel.setBorder( new EmptyBorder( new Insets( 10, 10, 10, 10 ) ) );
+ panel.setLayout( new BorderLayout( 10, 10 ) );
+
+ if ( message != null )
+ {
+ panel.add( new MultiLineLabel( message ), BorderLayout.NORTH );
+ }
+
+ JScrollPane scrollPane = new JScrollPane( table );
+ scrollPane.setPreferredSize( new Dimension( 325, 350 ) );
+ panel.add( scrollPane, BorderLayout.CENTER );
+
+ JFrame window = new JFrame();
+ window.setTitle( "Stack Trace Inspector" );
+ window.getContentPane().add( panel );
+
+ window.pack();
+ WindowUtilities.cascade( window );
+ window.show();
+ }
+
+ // interface TableModel
+
+ public int getRowCount()
+ {
+ return methodNames.size();
+ }
+
+ public int getColumnCount()
+ {
+ return 1;
+ }
+
+ public String getColumnName(int columnIndex)
+ {
+ switch ( columnIndex )
+ {
+ case 0:
+ return "Methods";
+ case 1:
+ return "Property";
+ }
+ System.out.println( "StackTraceInspector.getColumnName: unknown column: " + columnIndex );
+ return "";
+ }
+
+ public Class getColumnClass(int columnIndex)
+ {
+ switch ( columnIndex )
+ {
+ case 0:
+ return String.class;
+ case 1:
+ return String.class;
+ }
+ System.out.println( "StackTraceInspector.getColumnClass: unknown column: " + columnIndex );
+ return Object.class;
+ }
+
+ public boolean isCellEditable(int rowIndex,
+ int columnIndex)
+ {
+ return false;
+ }
+
+ public Object getValueAt(int rowIndex,
+ int columnIndex)
+ {
+ return methodNames.get( rowIndex );
+ }
+
+ public void setValueAt(Object aValue,
+ int rowIndex,
+ int columnIndex)
+ {
+ }
+
+ public void addTableModelListener(TableModelListener l)
+ {
+ if ( tableModelListeners == null )
+ {
+ tableModelListeners = new ArrayList();
+ }
+ tableModelListeners.add( l );
+ }
+
+ public void removeTableModelListener(TableModelListener l)
+ {
+ if ( tableModelListeners != null )
+ {
+ tableModelListeners.remove( l );
+ }
+ }
+
+ // interface MouseListener
+
+ /**
+ * Double click to call invokeFileFromString.
+ */
+
+ public void mouseClicked(MouseEvent e)
+ {
+ if ( e.getSource() == table )
+ {
+ if ( e.getClickCount() > 1 )
+ {
+ int row = table.rowAtPoint( e.getPoint() );
+ int col = table.columnAtPoint( e.getPoint() );
+
+ if ( ( row == -1 ) || ( col != 0 ) ) return;
+
+ invokeFileFromString( methodNames.get( row ).toString() );
+ }
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {}
+ public void mousePressed(MouseEvent e) {}
+ public void mouseEntered(MouseEvent e) {}
+ public void mouseExited(MouseEvent e) {}
+
+
+ // interface ActionEventListener - for listening to key commands
+
+ public void actionPerformed(ActionEvent evt)
+ {
+ if ( COPY.equals( evt.getActionCommand() ) )
+ {
+ copyToClipboard();
+ return;
+ }
+ }
+
+/**
+* Copies the contents of the table to the clipboard as a tab-delimited string.
+*/
+ public void copyToClipboard()
+ {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ Clipboard clipboard = toolkit.getSystemClipboard();
+ StringSelection selection =
+ new StringSelection( getSelectedStackString() );
+ clipboard.setContents( selection, selection );
+ }
+
+/**
+* Converts the selected contents of the table to a string.
+* @return A String containing the text contents of the table.
+*/
+ public String getSelectedStackString()
+ {
+ StringBuffer result = new StringBuffer(64);
+
+ TableModel model = table.getModel();
+
+ Object o;
+ int[] selectedRows = table.getSelectedRows();
+ for ( int i = 0; i < selectedRows.length; i++ )
+ {
+ o = model.getValueAt( selectedRows[i], 0 );
+ if ( o == null ) o = "";
+ result.append( o );
+ result.append( '\n' );
+ }
+
+ return result.toString();
+ }
+
+
+ // static methods
+
+/**
+* Obtains a list of strings representing the stack trace
+* associated with this throwable starting with the most recent call.
+* @param aThrowable A Throwable whose stack trace is parsed.
+* @return a List containing the method names as Strings.
+*/
+ static public List parseStackTrace( Throwable aThrowable )
+ {
+ String trace = null;
+
+ // create new stream
+ ByteArrayOutputStream os = new ByteArrayOutputStream( 256 );
+ PrintStream newErr = new PrintStream( os );
+ aThrowable.printStackTrace( newErr ); // prints to System.err
+
+ // convert to string
+ trace = os.toString();
+
+ List result = new ArrayList();
+
+ // populate list with parsed trace, starting from top
+ String token;
+ StringTokenizer tokens = new StringTokenizer( trace, "\n" );
+ tokens.nextToken(); // strip off description of throwable
+ while ( tokens.hasMoreTokens() )
+ {
+ token = tokens.nextToken();
+ if ( token.indexOf( StackTraceInspector.class.getName() ) == -1 )
+ { // add only those methods not from this class
+
+ // strip whitespace, "at " from front, and \r from end
+ token.trim();
+ token = token.substring( 4, token.length() - 1 );
+
+ result.add( token );
+ }
+ }
+
+ return result;
+ }
+
+/**
+* Convenience method that obtains a String representing
+* the caller's caller.
+* @return a String representing a method in stack trace format.
+*/
+ static public String getMyCaller()
+ {
+ List trace = parseStackTrace( new RuntimeException() );
+ if ( trace.size() > 1 )
+ {
+ return trace.get( 1 ).toString();
+ }
+
+ return null;
+ }
+
+/**
+* Prints a stack trace up to the first method whose fully
+* qualified class name begins with "java" to System.out.
+*/
+ static public void printShortStackTrace()
+ {
+ String s;
+ Iterator i = parseStackTrace( new RuntimeException() ).iterator();
+ while ( i.hasNext() )
+ {
+ System.out.println( " " + ( s = i.next().toString() ) );
+ if ( s.startsWith( "java" ) ) break;
+ }
+ }
+
+ protected void invokeFileFromString( String aString )
+ {
+ // strip off parentheses, if any
+ int openParam = aString.indexOf( "(" );
+ if ( openParam != -1 )
+ {
+ aString = aString.substring( 0, openParam );
+ }
+
+ // separate class name from method name
+ int lastDot = aString.lastIndexOf( "." );
+ if ( lastDot == -1 ) return;
+ String className = aString.substring( 0, lastDot );
+ String methodName = aString.substring( lastDot + 1 );
+
+ // convert "."s to file separator characters
+ StringBuffer buf = new StringBuffer();
+ StringTokenizer tokens = new StringTokenizer( className, "." );
+ while ( true )
+ {
+ buf.append( tokens.nextToken() );
+ if ( ! tokens.hasMoreTokens() ) break;
+ buf.append( File.separator );
+ }
+ String path = buf.toString();
+ java.net.URL url = ClassLoader.getSystemResource( path + ".java" );
+ if ( url == null ) return; // do nothing
+
+ String name = url.getFile();
+
+ // try to launch the document
+ try
+ {
+ // NOTE: This is Windows-dependent!
+ String args[] = new String[] {
+ "cmd", "/c", "\"start \"\" \"" + name.substring(1) + "\"\"" };
+ // this translates to: cmd /c "start "" "path""
+ // apparently an array is more reliable for calling exec().
+ // all the extra quotes are to handle paths with spaces.
+ // trims off the first "/" before the drive letter.
+ // needed a dummy title for the console or it wouldn't work.
+ Runtime.getRuntime().exec( args );
+ }
+ catch ( Exception exc )
+ {
+ System.out.println( "DocumentLinkPanel.invokeDocument: " + exc );
+ }
+ return;
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/18 23:19:05 cgruber
+ * Update imports and maven dependencies.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.5 2003/08/06 23:07:53 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.4 2002/11/16 16:33:31 mpowers
+ * Now using platform-specific accelerator key for shortcuts.
+ *
+ * Revision 1.3 2001/07/18 21:53:33 mpowers
+ * Added a string argument for display as a message.
+ *
+ * Revision 1.2 2001/07/17 14:01:43 mpowers
+ * Added short stack trace method.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:51:34 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.5 2000/12/20 16:25:45 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java
new file mode 100644
index 0000000..36dbacf
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java
@@ -0,0 +1,368 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Blacksmith, Inc.
+
+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.
+
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.awt.Component;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+import javax.swing.text.JTextComponent;
+
+/**
+* This class will actively check the inputs of 2 numbers in seperate text
+* components. The number in the text components represent an upper and lower
+* bound to some range. This class checks to make sure the user inputs values
+* in the lower bound text field that are less than the value of the upper
+* bound and vice versa for the upper bound text field. This class will also
+* check to make sure the bounds fall within a given range if specified.
+*
+* The checks are automatically performed when the focus is lost on either
+* component. If the inputs are correct then no event occurs. If the inputs
+* are not correct, then a dialog message is displayed stating the reason why
+* the bounds are invalid, and the original correct value is restored into the
+* text components.
+*
+* @author rglista
+* @author $Author: cgruber $
+* @version $Revision: 904 $
+*/
+public class TextInputRangeChecker implements FocusListener
+{
+ protected static final int NONE = 0;
+ protected static final int LOWER = 1;
+ protected static final int UPPER = 2;
+
+ private JTextComponent lowerComponent;
+ private JTextComponent upperComponent;
+ private double maxRange;
+ private double lowerNumber;
+ private double upperNumber;
+ private Collection focusListeners;
+
+ private String invalidLowerMessage;
+ private String invalidUpperMessage;
+ private String invalidEitherMessage;
+ private String invalidRangeMessage;
+
+
+/**
+* Constructor with some of the settable parameters. No range checking is used.
+* @param aLowerTextComponent A text component for the lower bound.
+* @param anUpperTextComponent A text component for the upper bound.
+*/
+ public TextInputRangeChecker( JTextComponent aLowerTextComponent,
+ JTextComponent anUpperTextComponent )
+ {
+ this( aLowerTextComponent, anUpperTextComponent, null, null, 0.0 );
+ }
+
+/**
+* Constructor with some of the settable parameters. No range checking is
+* used.
+* @param aLowerTextComponent A text component for the lower bound.
+* @param anUpperTextComponent A text component for the upper bound.
+* @param lowerTextName The name of the lower bound, eg - start year.
+* @param upperTextName The name of the upper bound, eg - end year.
+* is used.
+*/
+ public TextInputRangeChecker( JTextComponent aLowerTextComponent,
+ JTextComponent anUpperTextComponent,
+ String lowerTextName, String upperTextName )
+ {
+ this( aLowerTextComponent, anUpperTextComponent, lowerTextName, upperTextName, 0.0 );
+ }
+
+/**
+* Constructor with some of the settable parameters.
+* @param aLowerTextComponent A text component for the lower bound.
+* @param anUpperTextComponent A text component for the upper bound.
+* @param aMaxRange The range the bounds muist fall between, if 0 then no range
+* is used.
+*/
+ public TextInputRangeChecker( JTextComponent aLowerTextComponent,
+ JTextComponent anUpperTextComponent,
+ double aMaxRange )
+ {
+ this( aLowerTextComponent, anUpperTextComponent, null, null, aMaxRange );
+ }
+
+/**
+* Constructor with all the settable parameters.
+* @param aLowerTextComponent A text component for the lower bound.
+* @param anUpperTextComponent A text component for the upper bound.
+* @param lowerTextName The name of the lower bound, eg - start year.
+* @param upperTextName The name of the upper bound, eg - end year.
+* @param aMaxRange The range the bounds muist fall between, if 0 then no range
+* is used.
+*/
+ public TextInputRangeChecker( JTextComponent aLowerTextComponent,
+ JTextComponent anUpperTextComponent,
+ String lowerTextName, String upperTextName,
+ double aMaxRange )
+ {
+ lowerComponent = aLowerTextComponent;
+ upperComponent = anUpperTextComponent;
+ maxRange = aMaxRange;
+
+ focusListeners = new ArrayList( 1 ); // For most cases, there will be only 1 listener.
+
+ lowerComponent.addFocusListener( this );
+ upperComponent.addFocusListener( this );
+
+ lowerNumber = getNumber( lowerComponent );
+ upperNumber = getNumber( upperComponent );
+
+ if ( ( lowerTextName != null ) && ( upperTextName != null ) )
+ {
+ invalidLowerMessage = "The " + lowerTextName + " must be less than or equal to the " + upperTextName + ".";
+ invalidUpperMessage = "The " + upperTextName + " must be greater than or equal to the " + lowerTextName + ".";
+ invalidEitherMessage = "The " + lowerTextName + " and/or the " + upperTextName + " are not correct.";
+ invalidRangeMessage = "The maximum range for the " + lowerTextName + " and " + upperTextName + " is " + maxRange + ".";
+ }
+ else
+ {
+ invalidLowerMessage = "The lower bound must be less than or equal to the upper bound.";
+ invalidUpperMessage = "The upper bound must be greater than or equal to the lower bound.";
+ invalidEitherMessage = "The upper and/or lower bounds are not correct.";
+ invalidRangeMessage = "The maximum range is " + maxRange + ".";
+ }
+ }
+
+/**
+* Allows the caller to perform the validation of the bounds programatically.
+* The lower bound is compared to the upper bound and range checking is performed.
+* If the lower bound is greater than the upper bound, or the range between the
+* bounds is greater than the max range, then validation fails.
+* @return TRUE is validation is successfull, FALSE if it fails.
+*/
+ public boolean performCheck()
+ {
+ return validate( null );
+ }
+
+/**
+* Adds the listener to the lists of focus listener maintened by this object.
+* When one of the 2 text components receives a focus event, this object will
+* fire that focus event to any of its listeners. This is useful when the
+* calling object wants to be notified of the components focus events, but wants
+* to ensure that the validation has occured first.
+* <br><br>
+* NOTE: The focus is only fired if the validation was successful. This might
+* have to be changed.
+* @param aListener A Focus Listener to receive Focus Events.
+*/
+ public void addFocusListener( FocusListener aListener )
+ {
+ focusListeners.add( aListener );
+ }
+
+/**
+* Returns the last valid value of the lower bound. If this is called while
+* the user is updating the text component but before the focus is lost, the
+* value returned will be the original value before the user started updating
+* the bound.
+* @return The last valid value of the lower bound.
+*/
+ public double getLastValidatedLowerNumber()
+ {
+ return lowerNumber;
+ }
+
+/**
+* Returns the last valid value of the upper bound. If this is called while
+* the user is updating the text component but before the focus is lost, the
+* value returned will be the original value before the user started updating
+* the bound.
+* @return The last valid value of the upper bound.
+*/
+ public double getLastValidatedUpperNumber()
+ {
+ return upperNumber;
+ }
+
+/**
+* Method used to be notified when one of the text components has gained its
+* focus.
+*/
+ public void focusGained( FocusEvent e )
+ {
+ lowerNumber = getNumber( lowerComponent );
+ upperNumber = getNumber( upperComponent );
+ }
+
+/**
+* Method used to be notified when one of the text components has lost its
+* focus. Automatic validation occurs here.
+*/
+ public void focusLost( FocusEvent e )
+ {
+ if ( e.isTemporary() )
+ {
+ return;
+ }
+
+ if ( validate( e.getSource() ) )
+ {
+ fireFocusEvent( e );
+ }
+ }
+
+/**
+* Fires a focus lost event if the validation was successfull.
+*/
+ protected void fireFocusEvent( FocusEvent e )
+ {
+ for ( Iterator it = focusListeners.iterator(); it.hasNext(); )
+ {
+ ( ( FocusListener )it.next() ).focusLost( e );
+ }
+ }
+
+/**
+* Validates the bounds inputed by the user.
+* @param aComponent The component to use to display a dialog window, if neccessray.
+* If null, then the parent window of the text componets will be used.
+* @return TRUE if validation was successful, FALSE otherwise.
+*/
+ protected boolean validate( Object aComponent )
+ {
+ int componentUsed = NONE;
+ if ( aComponent == lowerComponent )
+ {
+ componentUsed = LOWER;
+ }
+ else if ( aComponent == upperComponent )
+ {
+ componentUsed = UPPER;
+ }
+
+ double lower = getNumber( lowerComponent );
+ double upper = getNumber( upperComponent );
+
+ if ( lower > upper )
+ {
+ if ( componentUsed == LOWER )
+ {
+ lowerComponent.setText( Double.toString( lowerNumber ) );
+ displayMessage( invalidLowerMessage, lowerComponent );
+ }
+ else if ( componentUsed == UPPER )
+ {
+ upperComponent.setText( Double.toString( upperNumber ) );
+ displayMessage( invalidUpperMessage, upperComponent );
+ }
+ else
+ {
+ upperComponent.setText( Double.toString( upperNumber ) );
+ lowerComponent.setText( Double.toString( lowerNumber ) );
+ displayMessage( invalidEitherMessage, lowerComponent.getTopLevelAncestor() );
+ }
+
+ return false;
+ }
+
+ if ( maxRange != 0.0 )
+ {
+ if ( ( upper - lower ) > maxRange )
+ {
+ if ( componentUsed == LOWER )
+ {
+ lowerComponent.setText( Double.toString( lowerNumber ) );
+ displayMessage( invalidRangeMessage, lowerComponent );
+ }
+ else if ( componentUsed == UPPER )
+ {
+ upperComponent.setText( Double.toString( upperNumber ) );
+ displayMessage( invalidRangeMessage, upperComponent );
+ }
+ else
+ {
+ upperComponent.setText( Double.toString( upperNumber ) );
+ lowerComponent.setText( Double.toString( lowerNumber ) );
+ displayMessage( invalidRangeMessage, lowerComponent.getTopLevelAncestor() );
+ }
+
+ return false;
+ }
+ }
+
+ lowerNumber = lower;
+ upperNumber = upper;
+ return true;
+ }
+
+/**
+* Creates a JOptionPane to display the reason why the bounds failed validation.
+*/
+ protected void displayMessage( final String message, final Component parent )
+ {
+ SwingUtilities.invokeLater( new Runnable()
+ {
+ public void run()
+ {
+ JOptionPane.showMessageDialog( parent, message, "Data Entry Error",
+ JOptionPane.ERROR_MESSAGE );
+ }
+ } );
+ }
+
+/**
+* Gets the number represented in the text component. If the text does not
+* represent a number, then zero is returned.
+*/
+ protected double getNumber( JTextComponent aComponent )
+ {
+ try
+ {
+ return Double.valueOf( aComponent.getText() ).doubleValue();
+//1.2 return Double.parseDouble( aComponent.getText() );
+ }
+ catch ( NumberFormatException e )
+ {
+ System.out.println("[GUI] TextInputRangeChecker.getNumber(): The text is NOT a number: " + aComponent.getText() );
+ return 0.0;
+ }
+ }
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/18 23:19:05 cgruber
+ * Update imports and maven dependencies.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2003/08/06 23:07:53 chochos
+ * general code cleanup (mostly, removing unused imports)
+ *
+ * Revision 1.1.1.1 2000/12/21 15:51:49 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:46 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java
new file mode 100644
index 0000000..c360105
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java
@@ -0,0 +1,203 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+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.
+
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.awt.Component;
+import java.awt.Image;
+import java.awt.Window;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.OutputStream;
+
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JWindow;
+
+/**
+ * WindowGrabber is a collection of static utility methods
+ * for taking screen shots of lightweight containers. All
+ * components that are not native peers will be drawn to a
+ * bitmap that will be run-length compressed (LZW encoding, GIF format)
+ * and written to the specified file or output stream. <br><br>
+ *
+ * Any Swing/JFC or IFC window is a good candidate for use with these
+ * methods. The component is not expected to contain more than 256 colors
+ * (the maximum that GIF allows). While there is a color depth limitation,
+ * the compression is lossless, with no blurs or bleeding color.
+ *
+ * @author michael@mpowers.net
+ * @version $Revision: 904 $
+ * $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+ */
+public class WindowGrabber
+{
+/**
+ * Captures the screen contents of the specified component,
+ * and write it to a file with the specified name.
+ *
+ * @param aComponent a lightweight component or container.
+ * @param aFileName the name of the file to write, optionally preceded by path.
+ * @return True if the file was successfully written, false if there was an error.
+ * Errors are written to the standard error stream.
+ */
+ static public boolean grab( Component aComponent, String aFileName )
+ {
+ OutputStream output = null;
+ try
+ {
+ output = new FileOutputStream( aFileName );
+ }
+ catch ( Exception exc )
+ {
+ System.err.println( exc );
+ return false;
+ }
+
+ return grab( aComponent, output );
+ }
+
+/**
+ * Captures the screen contents of the specified component,
+ * and write it to a file with the specified name.
+ *
+ * @param aComponent a lightweight component or container.
+ * @param anOutputStream an output stream to which the image will be written.
+ * @return True if the image was successfully written, false if there was an error.
+ * Errors are written to the standard error stream.
+ */
+ static public boolean grab( Component aComponent, OutputStream anOutputStream )
+ {
+ Image img = aComponent.createImage(
+ aComponent.getSize().width, aComponent.getSize().height );
+ aComponent.paintAll( img.getGraphics() );
+
+ try {
+ GIFEncoder encoder = new GIFEncoder( img );
+ encoder.write( anOutputStream );
+ anOutputStream.flush();
+ } catch ( Exception exc ) {
+ System.err.println( exc );
+ return false;
+ }
+
+ return true;
+ }
+
+ protected static void processFilenames( String path, String[] filenames )
+ {
+ ClassLoader loader = new ClassGrabber();
+
+ File f;
+ for ( int i = 0; i < filenames.length; i++ )
+ {
+ try
+ {
+ f = new File( path + filenames[i] );
+ if ( f.isDirectory() )
+ {
+ processFilenames (
+ path + filenames[i] + "/",
+ f.list( new FilenameFilter()
+ {
+ public boolean accept(File dir, String name)
+ {
+ return name.endsWith( ".class" );
+ }
+ })
+ );
+ }
+ else
+ {
+ System.out.println( "Loading " + filenames[i] + ": " );
+ Class c = loader.loadClass( path + filenames[i] );
+
+ System.out.println( c );
+
+ if ( JWindow.class.isAssignableFrom( c ) ||
+ JDialog.class.isAssignableFrom( c ) ||
+ JFrame.class.isAssignableFrom( c ) )
+ {
+ try
+ {
+ Window w = (Window) c.newInstance();
+ if ( w.getBounds().width * w.getBounds().height == 0 )
+ { // if size not specified or set, pack it
+ w.pack();
+ }
+ String gifName = // replace .class with .gif
+ filenames[i].substring(
+ 0, filenames[i].length() - 5 ) + "gif";
+ w.addNotify();
+ w.repaint();
+ grab( w, gifName );
+ System.out.println( "wrote: " + gifName );
+ }
+ catch ( Exception exc )
+ {
+ System.err.println( "WindowGrab failed for " + filenames[i] + ": " );
+ exc.printStackTrace();
+ }
+
+ }
+ else
+ {
+ System.out.println( "not a JWindow, JDialog, or JFrame; ignored." );
+ }
+ }
+ }
+ catch ( Exception exc )
+ {
+ System.err.println( filenames[i] + ": " + exc );
+ }
+ }
+ }
+
+/**
+ * Captures images of any Swing window component classes specified
+ * as parameters. If created, image file will have the same name
+ * as the corresponding class file, but with a ".gif" extension.
+ *
+ * @param argv a list of filenames or directory names.
+ */
+ public static void main( String[] argv )
+ {
+ processFilenames( "", argv );
+ System.exit( 0 );
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/18 23:19:05 cgruber
+ * Update imports and maven dependencies.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:51:49 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:46 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java
new file mode 100644
index 0000000..a36ba12
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java
@@ -0,0 +1,521 @@
+/*
+Wotonomy: OpenStep design patterns for pure Java applications.
+Copyright (C) 2000 Michael Powers
+
+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.
+
+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.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, see http://www.gnu.org
+*/
+
+package net.wotonomy.ui.swing.util;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dialog;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+* A collection of window-related utilities.
+*
+* @author michael@mpowers.net
+* @author $Author: cgruber $
+* @version $Revision: 904 $
+* $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006) $
+*
+*/
+public class WindowUtilities
+{
+
+/**
+* Place frame at center (vertically and horizontally) of screen.
+*/
+ public static final int CENTER = 0;
+
+/**
+* Center dialog on frame area of parent, if any.
+*/
+ public static final int CENTER_PARENT = 1;
+
+/**
+* Place lower and to the right of the last window
+* placed in this manner. Will wrap to top and left
+* of screen as necessary.
+*/
+ public static final int CASCADE = 10;
+
+ // cascade state
+ private static int lastX = 0;
+ private static int lastY = 0;
+ private static int incrementX = 20;
+ private static int incrementY = 20;
+
+/**
+* Place the window in the center of the screen.
+* Note: don't forget to first set the size of your window.
+* This is a convenience method and simply calls place() with
+* the CENTER parameter.
+* @param aWindow The window to be centered.
+* @see #place
+*/
+ public static void center( Window aWindow )
+ {
+ place( aWindow, CENTER );
+ }
+
+/**
+* Place lower and to the right of the last window
+* placed in this manner. Will wrap to top and left
+* of screen as necessary.
+* This is a convenience method and simply calls place() with
+* the CASCADE parameter.
+* @param aWindow The window to be cascaded.
+* @see #place
+*/
+ public static void cascade( Window aWindow )
+ {
+ place( aWindow, CASCADE );
+ }
+
+/**
+* Place the window in the specified location.
+* Note: don't forget to first set the size of your window.
+* @param aWindow The window to be placed.
+* @param location Where on screen to place the frame.
+*/
+ public static void place( Window aWindow, int location)
+ {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ Dimension mySize = aWindow.getSize();
+ int x = (aWindow.getLocation()).x;
+ int y = (aWindow.getLocation()).y;
+ float aspectRatio = (float)screenSize.height/(float)screenSize.width;
+
+ // hack to make windows appear on left monitor if dual monitor
+ // if aspect ratio is less than 0.6, assume dual monitor
+ if (aspectRatio < 0.6)
+ {
+ screenSize.width = screenSize.width/2;
+ }
+
+ switch (location)
+ {
+ case CENTER_PARENT:
+ if ( ( ! ( aWindow instanceof Dialog ) )
+ || ( ((Dialog)aWindow).getParent() == null ) )
+//1.2 || ( ((Dialog)aWindow).getOwner() == null ) )
+ {
+ place( aWindow, CENTER );
+ return;
+ }
+ Point parentLocation = (((Dialog)aWindow).getParent()).getLocation();
+//1.2 (((Dialog)aWindow).getOwner()).getLocation();
+ Dimension parentSize = (((Dialog)aWindow).getParent()).getSize();
+
+//1.2 Dimension parentSize = (((Dialog)aWindow).getOwner()).getSize();
+
+ if (parentSize.width > mySize.width)
+ {
+ x = ((parentSize.width - mySize.width)/2) + parentLocation.x;
+ }
+ if (parentSize.height > mySize.height)
+ {
+ y = ((parentSize.height - mySize.height)/2) + parentLocation.y;
+ }
+ break;
+ case CENTER:
+ if (screenSize.width > mySize.width)
+ {
+ x = (screenSize.width - mySize.width)/2;
+ }
+ if (screenSize.height > mySize.height)
+ {
+ y = (screenSize.height - mySize.height)/2;
+ }
+ break;
+ case CASCADE:
+ x = lastX + incrementX;
+ if ( x + mySize.width > screenSize.width )
+ {
+ x = incrementX;
+ }
+ y = lastY + incrementY;
+ if ( y + mySize.height > screenSize.height )
+ {
+ y = incrementY;
+ }
+ lastX = x;
+ lastY = y;
+ break;
+ default:
+ // don't move the frame
+ Point p = aWindow.getLocation();
+ x = p.x;
+ y = p.y;
+ break;
+ }
+ aWindow.setLocation(x,y);
+ }
+
+/**
+ * Returns the first parent Window of the specified component.
+ *
+ * @param c the Component whose parent will be found.
+ * @return the Window that contains the component,
+ * or null if the component does not have a valid Frame parent.
+ */
+ public static Window getWindowForComponent(Component c)
+ {
+ for(Component p = c; p != null; p = p.getParent()) {
+ if (p instanceof Window) {
+ return (Window) p;
+ }
+ }
+ return null;
+ }
+
+
+
+/**
+* Prints out a list of all components to System.out.
+* @param aContainer the Container whose components will be listed.
+*/
+ public static void dumpComponents( Container aContainer )
+ {
+ dumpComponents( aContainer, "" );
+ }
+
+ protected static void dumpComponents( Container aContainer, String padding )
+ {
+ Component c = null;
+ int count = aContainer.getComponentCount();
+ for ( int i = 0; i < count; i++ )
+ {
+ c = aContainer.getComponent( i );
+ if ( c instanceof javax.swing.JComponent )
+ {
+ System.out.println( padding + c.getClass() + ": "
+ + ((javax.swing.JComponent)c).getAccessibleContext().getAccessibleName() );
+ }
+ else
+ {
+ System.out.println( padding + c.getClass() + ": " + c.getName() );
+ }
+ if ( c instanceof Container )
+ {
+ dumpComponents( (Container) c, padding + " " );
+ }
+ }
+ }
+
+/**
+* Gets a list of all children of a specified container, sorted by position.
+* Components are sorted from top to bottom and then left to right.
+* @param aContainer The container whose children are to be returned.
+* @result A List containing the sorted components.
+*/
+ public static List getSortedChildComponents( Container aContainer )
+ {
+ List result = new ArrayList( getAllChildComponents( aContainer ) );
+ Collections.sort( result, new PositionComparator( aContainer ) );
+ return result;
+ }
+
+ public static void dumpSortedChildComponents( Container aContainer )
+ {
+ Component c = null;
+ Iterator it = getSortedChildComponents( aContainer ).iterator();
+ while ( it.hasNext() )
+ {
+ c = (Component) it.next();
+ System.out.println( c.getLocation() + " : " + c.getClass() );
+ }
+ }
+
+ public static void dumpNamedChildComponents( Container aContainer )
+ {
+ Iterator it = getUniqueNameMap( getSortedChildComponents( aContainer ) ).values().iterator();
+ while ( it.hasNext() )
+ {
+ System.out.println( it.next() );
+ }
+ }
+
+/**
+* Generates a unique name for each object in a list and returns
+* a map that maps the objects to the names. The name is based
+* on the class of the object in the object list.
+* @param anObjectList A List of objects.
+* @return A Map that maps the objects in the list to the generated names.
+*/
+ public static Map getUniqueNameMap( List anObjectList )
+ {
+ Map namesToObjects = new HashMap(anObjectList.size(), 1F);
+ Map objectsToNames = new HashMap(anObjectList.size(), 1F);
+
+ Object o = null;
+ String name = null;
+ int lastIndex = 0;
+ Iterator it = anObjectList.iterator();
+ while ( it.hasNext() )
+ {
+ o = it.next();
+ name = o.getClass().getName();
+ lastIndex = name.lastIndexOf( "." );
+ if ( lastIndex != -1 )
+ {
+ name = name.substring( lastIndex+1 );
+ }
+ name = incrementString( name );
+ while ( namesToObjects.get( name ) != null )
+ {
+ name = incrementString( name );
+ }
+ namesToObjects.put( name, o );
+ objectsToNames.put( o, name );
+ }
+
+ return objectsToNames;
+ }
+
+/**
+* Numerically increments a string. For example, "hello" becomes "hello1"
+* and "hello1" becomes "hello2" while "hello999" becomes "hello1000".
+* @param aString a String to be incremented.
+* @return The incremented String.
+*/
+ public static String incrementString( String aString )
+ {
+ int i = aString.length()-1;
+ while ( ( i >= 0 ) && ( Character.isDigit( aString.charAt( i ) ) ) )
+ {
+ i--;
+ }
+
+ if ( i == aString.length()-1 )
+ { // no numerics at end of string, increment manually
+ return aString + "1";
+ }
+
+ String alpha = aString.substring( 0, i+1 );
+ String numeric = aString.substring( i+1 );
+ numeric = Integer.toString( Integer.parseInt( numeric ) + 1 );
+
+ return alpha + numeric;
+
+ }
+
+/**
+* Gets all children of the specified container.
+* @param aContainer the Container to be searched.
+* @return A Collection containing all of the child components of the container.
+*/
+ public static Collection getAllChildComponents( Container aContainer )
+ {
+ Collection result = new ArrayList();
+ addAllChildComponents( aContainer, result );
+ return result;
+ }
+
+/**
+* Adds all children of the specified container to the specified collection.
+* @param aContainer the Container to be searched.
+* @param aCollection the Collection to which the child components will be added.
+*/
+ protected static void addAllChildComponents( Container aContainer, Collection aCollection )
+ {
+ Component c = null;
+ int count = aContainer.getComponentCount();
+ for ( int i = 0; i < count; i++ )
+ {
+ c = aContainer.getComponent( i );
+ aCollection.add( c );
+ if ( c instanceof Container )
+ {
+ addAllChildComponents( (Container) c, aCollection );
+ }
+ }
+ }
+
+/**
+* Sets each child component's tooltip to show the name generated from
+* getComponentNameMap().
+* (We're doing this so the tooltip authors can know how to reference
+* the components.)
+* @param aContainer the Container whose components will be labeled.
+*/
+ public static void labelComponents( Container aContainer )
+ {
+ Map nameToComponent = getNameToComponentMap( aContainer );
+ Map nameToName = new HashMap(nameToComponent.size(), 1F);
+ Iterator it = nameToComponent.keySet().iterator();
+ String key;
+ while ( it.hasNext() )
+ {
+ key = it.next().toString();
+ nameToName.put( key, key );
+ }
+ labelComponents( aContainer, nameToName );
+ }
+
+/**
+* Sets each child component's tooltip to show a given string retrieved
+* from a map using the component's generated name as a key.
+* @param aContainer the Container whose components will be labeled.
+* @param aNameMap a Map of generated names to string values.
+*/
+ public static void labelComponents( Container aContainer, Map aNameMap )
+ {
+ if ( aNameMap == null ) return;
+
+ String key;
+ Object o ;
+ Iterator it = aNameMap.keySet().iterator();
+ Map nameToComponent = getNameToComponentMap( aContainer );
+ while ( it.hasNext() )
+ {
+ key = it.next().toString();
+ o = nameToComponent.get( key );
+ if ( o instanceof javax.swing.JComponent )
+ {
+ ((javax.swing.JComponent)o).setToolTipText( aNameMap.get( key ).toString() );
+ }
+ }
+ }
+
+/**
+* Generates a deterministically unique name for each component in a
+* container. The name is based on the name of the class of the component
+* followed by a number. Each class of component is numbered based on it's
+* position in the container, sorted from top to bottom and left to right.
+* @param aContainer the Container whose components will named.
+* @return a Map that maps each component to its name.
+*/
+ public static Map getComponentToNameMap( Container aContainer )
+ {
+ return getUniqueNameMap( getSortedChildComponents( aContainer ) );
+ }
+
+/**
+* Maps a deterministically unique name to a component in a
+* container. The name is based on the name of the class of the component
+* followed by a number. Each class of component is numbered based on it's
+* position in the container, sorted from top to bottom and left to right.
+* @param aContainer the Container whose components will named.
+* @return a Map that maps each component to its name.
+*/
+ public static Map getNameToComponentMap( Container aContainer )
+ {
+ Map componentToName = getComponentToNameMap( aContainer );
+ Map result = new HashMap(componentToName.size(), 1F);
+ Iterator it = componentToName.keySet().iterator();
+ Object key;
+ while ( it.hasNext() )
+ {
+ key = it.next();
+ result.put( componentToName.get( key ), key );
+ }
+ return result;
+ }
+
+/**
+* Sets the tooltips of all components in a container to the
+* respective names of those components. (We're using this
+* so the tooltip authors can know how to reference the components.)
+* @param aContainer the Container whose components will be labeled.
+*/
+ public static void nameComponents( Container aContainer )
+ {
+ nameComponents( aContainer, "" );
+ }
+
+ protected static void nameComponents( Container aContainer, String path )
+ {
+ Component c = null;
+ String className = null;
+ int index = 0;
+ int count = aContainer.getComponentCount();
+ for ( int i = 0; i < count; i++ )
+ {
+ c = aContainer.getComponent( i );
+ className = c.getClass().getName();
+ className = className.substring( className.lastIndexOf( '.' ) + 1 );
+ System.out.println( path + className );
+ if ( c instanceof javax.swing.JComponent )
+ {
+ // ((javax.swing.JComponent)c).setToolTipText( path + className + " (" + c.getName() + ")" );
+ ((javax.swing.JComponent)c).setToolTipText( c.getName() );
+ }
+ if ( c instanceof Container )
+ {
+ nameComponents( (Container) c, path + className + "." );
+ }
+ }
+ }
+
+/**
+* Sets the enabled state of a container and all of its components.
+* @param aContainer the Container whose components will be enabled.
+* @param isEnabled True if enabled, false id disabled.
+*/
+ public static void enableComponents( Container aContainer, boolean isEnabled )
+ {
+ Component c = null;
+ String className = null;
+ int index = 0;
+ int count = aContainer.getComponentCount();
+ for ( int i = 0; i < count; i++ )
+ {
+ c = aContainer.getComponent( i );
+ if ( c instanceof Container )
+ {
+ enableComponents( (Container) c, isEnabled );
+ }
+ else
+ {
+ c.setEnabled( isEnabled );
+ }
+ }
+ aContainer.setEnabled( isEnabled );
+ }
+
+}
+
+/*
+ * $Log$
+ * Revision 1.2 2006/02/18 23:19:05 cgruber
+ * Update imports and maven dependencies.
+ *
+ * Revision 1.1 2006/02/16 13:22:22 cgruber
+ * Check in all sources in eclipse-friendly maven-enabled packages.
+ *
+ * Revision 1.2 2001/02/17 16:52:05 mpowers
+ * Changes in imports to support building with jdk1.1 collections.
+ *
+ * Revision 1.1.1.1 2000/12/21 15:51:55 mpowers
+ * Contributing wotonomy.
+ *
+ * Revision 1.2 2000/12/20 16:25:46 michael
+ * Added log to all files.
+ *
+ *
+ */
+
diff --git a/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/package.html b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/package.html
new file mode 100644
index 0000000..97f1598
--- /dev/null
+++ b/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/package.html
@@ -0,0 +1,6 @@
+<body>
+<p>
+Contains utilities that ease Swing development
+but don't quite qualify as components.
+</p>
+</body>