From aedc34d55462a75e329bbf342251ff6504cd117e Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Sun, 19 May 2024 17:56:33 -0400 Subject: Initial import from SVN --- .../net/wotonomy/ui/swing/util/ClassGrabber.java | 126 +++++ .../ui/swing/util/ComponentHighlighter.java | 160 +++++++ .../net/wotonomy/ui/swing/util/GIFEncoder.java | 520 ++++++++++++++++++++ .../wotonomy/ui/swing/util/ObjectInspector.java | 226 +++++++++ .../wotonomy/ui/swing/util/PositionComparator.java | 89 ++++ .../ui/swing/util/StackTraceInspector.java | 457 ++++++++++++++++++ .../ui/swing/util/TextInputRangeChecker.java | 368 +++++++++++++++ .../net/wotonomy/ui/swing/util/WindowGrabber.java | 203 ++++++++ .../wotonomy/ui/swing/util/WindowUtilities.java | 521 +++++++++++++++++++++ .../java/net/wotonomy/ui/swing/util/package.html | 6 + 10 files changed, 2676 insertions(+) create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ClassGrabber.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ComponentHighlighter.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/GIFEncoder.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/ObjectInspector.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/PositionComparator.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/StackTraceInspector.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/TextInputRangeChecker.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowGrabber.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/WindowUtilities.java create mode 100644 projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util/package.html (limited to 'projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/util') 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 write.

+ * + * Three caveats: + * + * + * 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.

+ * 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. + * This may take some time.

+ * + * @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.

+* +* @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.

+* +* 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. +*

+* 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.

+ * + * 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 @@ + +

+Contains utilities that ease Swing development +but don't quite qualify as components. +

+ -- cgit v1.2.3