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