// ********************************************************************** // // <copyright> // // BBN Technologies // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // </copyright> // ********************************************************************** // // $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/gui/WindowSupport.java,v $ // $RCSfile: WindowSupport.java,v $ // $Revision: 1.14.2.9 $ // $Date: 2007/03/08 19:22:19 $ // $Author: dietrick $ // // ********************************************************************** package com.bbn.openmap.gui; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.image.BufferedImage; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Iterator; import javax.imageio.ImageIO; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; import com.bbn.openmap.Environment; import com.bbn.openmap.event.ListenerSupport; import com.bbn.openmap.util.Debug; /** * The WindowSupport class provides support for managing JFrames or * JInternalFrames for other components. The frame is disposed of when the * window is closed, and recreated when displayInWindow is called. The * WindowSupport remembers size and location changes for the window when it is * recreated. * <p> * * The WindowSupport class now has inner classes that are used to create * different types of windows for components. Dlg is used for JDialogs, which * remain on top of the main application frame even when they don't have focus. * IntrnlFrm is used for JInternalFrames, for windows that get clipped if the * move off the area of the main application. Frm is used for a standard JFrame * which can be obscured by the main application window. Components are free to * specify which WSDisplay type they always want to be display in, or they can * ask for a standard WindowSupport object which will use the static * defaultWindowSupportDisplayType variable to determine the WSDisplay type (for * a consistent feel across the application). The default setting for this * variable is to use the Frm type. */ public class WindowSupport extends ListenerSupport<ComponentListener> implements ComponentListener, ActionListener { protected Component content; protected String title; protected Point componentLocation; protected Dimension componentSize; public final static String DisplayWindowCmd = "displayWindowCmd"; public final static String KillWindowCmd = "killWindowCmd"; protected transient WSDisplay display; protected static Class defaultWindowSupportDisplayType = Frm.class; /** * Create the window support. * * @param content the content to display in the window. * @param windowTitle the title of the window. */ public WindowSupport(Component content, String windowTitle) { super(content); setContent(content); this.title = windowTitle; } public WindowSupport(Component content, WSDisplay display) { super(content); setContent(content); setDisplay(display); if (display != null) { this.title = display.getTitle(); } } /** * Set the location of the window. */ public void setComponentLocation(Point p) { componentLocation = p; } /** * Get the location of the window. */ public Point getComponentLocation() { return componentLocation; } /** * Set the size of the window. */ public void setComponentSize(Dimension dim) { componentSize = dim; } /** * Get the size of the window. */ public Dimension getComponentSize() { return componentSize; } /** * ComponentListener method, new size is noted. */ public void componentResized(ComponentEvent e) { Component source = (Component) e.getSource(); setComponentSize(source.getSize()); Iterator it = iterator(); while (it.hasNext()) { ((ComponentListener) it.next()).componentResized(e); } } /** * ComponentListener method, new location is noted. */ public void componentMoved(ComponentEvent e) { setComponentLocation(((Component) e.getSource()).getLocation()); Iterator it = iterator(); while (it.hasNext()) { ((ComponentListener) it.next()).componentMoved(e); } } /** * ComponentListener method. */ public void componentShown(ComponentEvent e) { Iterator it = iterator(); while (it.hasNext()) { ((ComponentListener) it.next()).componentShown(e); } } /** * ComponentListener method. WindowSupport kills the window when it is * hidden. */ public void componentHidden(ComponentEvent e) { Iterator it = iterator(); while (it.hasNext()) { ((ComponentListener) it.next()).componentHidden(e); } // We need to do this after componentHidden notifications, // otherwise the component never finds out it's been hidden, // it gets removed as a ComponentListener at cleanup. Component source = (Component) e.getSource(); if (display == source) { cleanUp(); } } public void actionPerformed(ActionEvent ae) { String command = ae.getActionCommand(); if (command == KillWindowCmd) { killWindow(); } else if (command == DisplayWindowCmd) { displayInWindow(); } } protected void finalize() { if (Debug.debugging("gc")) { Debug.output("WindowSupport being gc'd"); } } /** * Sets the title of the JInternalFrame/JDialog. */ public void setTitle(String tle) { title = tle; if (display != null) { display.setTitle(tle); } } public String getTitle() { return title; } /** * Sets the content in the JInternalFrame/JDialog. */ public void setContent(Component comp) { if (content instanceof ComponentListener) { removeComponentListener((ComponentListener) content); } content = comp; if (display != null) { display.setContent(comp); } if (content instanceof ComponentListener) { addComponentListener((ComponentListener) content); } } public Component getContent() { return content; } protected int maxHeight = -1; protected int maxWidth = -1; /** * Sets the maximum pixel size of the window. If you don't care about a * particular dimension, set it to be less than zero and the natural size of * the content will be displayed. */ public void setMaxSize(int width, int height) { maxHeight = height; maxWidth = width; } /** * Called when a component hasn't specified what kind of window they want. * If the Environment.useInternalFrames flag isn't set, then the * getDefaultWindowSupportDisplayType() method is called to find out which * WSDisplay type class should be created for the component. IF that returns * null, a Frm is created. * * @param owner * @return WSDisplay */ protected WSDisplay createDisplay(Frame owner) { WSDisplay wsd; if (persistentDisplayType == null && Environment.getBoolean(Environment.UseInternalFrames)) { wsd = new IntrnlFrm(title); } else { Class wTypeClass = persistentDisplayType == null ? getDefaultWindowSupportDisplayType() : persistentDisplayType; if (wTypeClass == Dlg.class) { wsd = new Dlg(owner, title); } else if (wTypeClass == IntrnlFrm.class) { wsd = new IntrnlFrm(title); } else { wsd = new Frm(title); } } setFavIcon(wsd); setDisplay(wsd); return wsd; } /** * Called when a display type is known, and may override the default * settings. A Frm is created if the displayType is not null but not a valid * WSDisplay type. * * @param owner * @param displayType a WSDisplay class to create. If null, the * persistentDisplayType will be used. * @return WSDisplay */ protected WSDisplay createDisplay(Frame owner, Class displayType) { WSDisplay wsd; if (displayType == null) { return createDisplay(owner); } if (displayType == Dlg.class) { wsd = new Dlg(owner, title); } else if (displayType == IntrnlFrm.class) { wsd = new IntrnlFrm(title); } else { wsd = new Frm(title); } setDisplay(wsd); return wsd; } public static Class getDefaultWindowSupportDisplayType() { return defaultWindowSupportDisplayType; } public static void setDefaultWindowSupportDisplayType(Class defaultWindowSupportDisplayType) { WindowSupport.defaultWindowSupportDisplayType = defaultWindowSupportDisplayType; } public void setDisplay(WSDisplay dis) { if (display != null) { display.removeComponentListener(this); } display = dis; if (display != null) { display.addComponentListener(this); display.setContent(modifyContent(content)); } } /** * Subclass method to allow modifications to content, wrappers, etc. This * version just returns comp. */ public Component modifyContent(Component comp) { return comp; } public WSDisplay getDisplay() { return display; } /** * Display the window, and find out what the natural or revised size and * location are for the window. */ public void displayInWindow() { displayInWindow(null); } /** * Display the window, and find out what the natural or revised size and * location are for the window. * * @param owner Frame for JDialog */ public void displayInWindow(Frame owner) { Dimension dim = getComponentSize(); if (dim != null) { content.setSize(dim); } // -1 is a flag for the positioning code to recenter the // -window on the owner if it's not null, for JDialogs. displayInWindow(owner, -1, -1, -1, -1); } /** * Display the window. * * @param x the horizontal pixel location for the window. * @param y the vertical pixel location for the window. * @param width the horizontal size of the window, if less than or equal to * zero the content size will be used. * @param height the vertical size of the window, if less than or equal to * zero the content size will be used. */ public void displayInWindow(int x, int y, int width, int height) { displayInWindow(null, x, y, width, height); } /** * Display the window. * * @param owner Frame for JDialog * @param x the horizontal pixel location for the window. * @param y the vertical pixel location for the window. * @param width the horizontal size of the window, if less than or equal to * zero the content size will be used. * @param height the vertical size of the window, if less than or equal to * zero the content size will be used. */ public void displayInWindow(Frame owner, int x, int y, int width, int height) { displayInWindow(owner, null, x, y, width, height); } /** * Display the window. * * @param owner Frame for JDialog * @param displayType the WSDisplay class to use for the window. * @param x the horizontal pixel location for the window. * @param y the vertical pixel location for the window. * @param width the horizontal size of the window, if less than or equal to * zero the content size will be used. * @param height the vertical size of the window, if less than or equal to * zero the content size will be used. */ public void displayInWindow(Frame owner, Class displayType, int x, int y, int width, int height) { if (content == null) { Debug.message("windowsupport", "WindowSupport asked to display window with null content"); return; } if (x < 0 && y < 0) { // See if we can remember where we were... Point loc = getComponentLocation(); if (loc != null) { x = (int) loc.getX(); y = (int) loc.getY(); } } if (display != null && displayType != null && !display.getClass().getName().equals(displayType.getName())) { display.dispose(); display = null; } if (display == null) { display = createDisplay(owner, displayType); } Container displayWindow = display.getWindow(); checkBounds(displayWindow, x, y, width, height); display.show(x, y); setComponentLocation(displayWindow.getLocation()); setComponentSize(displayWindow.getSize()); } /** * Checks the component's dimensions against the requested values and * against any maximum limits that may have been set in the WindowSupport. * Calls setBounds() on the Component. */ protected void checkBounds(Component comp, int x, int y, int width, int height) { if (comp != null) { if (width <= 0) { width = comp.getWidth(); } if (maxWidth > 0 && width > maxWidth) { width = maxWidth; } if (height <= 0) { height = comp.getHeight(); } if (maxHeight > 0 && height > maxHeight) { height = maxHeight; } comp.setBounds(x, y, width, height); } } /** * For applications, checks where the Environment says the window should be * placed, and then uses the packed height and width to make adjustments. */ protected static void setDefaultPosition(Component comp) { // get starting width and height int w = comp.getWidth(); int h = comp.getHeight(); // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5100801 GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); int width = gd.getDisplayMode().getWidth(); int height = gd.getDisplayMode().getHeight(); int x = width / 2 - w / 2; int y = height / 2 - h / 2; Debug.message("basic", "Screen dimensions are " + gd.getDisplayMode()); // Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); // Debug.message("basic", "Screen dimensions are " + d); // int x = d.width / 2 - w / 2; // int y = d.height / 2 - h / 2; if (Debug.debugging("basic")) { Debug.output("Setting PLG frame X and Y from properties to " + x + " " + y); } // compose the frame, but don't show it here comp.setBounds(x, y, w, h); } /** * Set the window to be hidden and fire a ComponentEvent for * COMPONENT_HIDDEN. Normally, just setting the visibility of the window * would be enough, but we're running into that problem we had with the * layers not firing ComponentEvents when hidden. This method calls * componentHidden, which in turn calls cleanUp. */ public void killWindow() { ComponentEvent ce = null; if (display != null) { ce = display.kill(); } if (ce != null) { componentHidden(ce); } } protected Class persistentDisplayType; /** * Get rid of the window used to display the content. */ protected void cleanUp() { if (display != null) { persistentDisplayType = display.getClass(); WSDisplay wsd = display; setDisplay(null); wsd.dispose(); } // This seems to half-disconnect the window support from the content, // and I am not sure it's necessary to remove the listeners. This was // originally done to make it easier to release memory, but having the // WindowSupport hold on to listeners won't prevent that from happening. // if (content instanceof ComponentListener) { // removeComponentListener((ComponentListener) content); // } } /** * Add a component listener that is interested in hearing about what happens * to the window. */ public void addComponentListener(ComponentListener l) { add(l); } /** * Remove a component listener that was interested in hearing about what * happens to the window. */ public void removeComponentListener(ComponentListener l) { remove(l); } /** * Return the window displaying the content. May be null. */ public Container getWindow() { if (display != null) { return display.getWindow(); } else { return null; } } public static interface WSDisplay { public void setTitle(String title); public String getTitle(); public Container getWindow(); public void setContent(Component content); public void show(int x, int y); public ComponentEvent kill(); public void dispose(); public Dimension getContentSize(); public void addComponentListener(ComponentListener cl); public void removeComponentListener(ComponentListener cl); } public static class IntrnlFrm extends JInternalFrame implements WSDisplay { public IntrnlFrm(String title) { super(title, /* resizable */true, /* closable */true, /* maximizable */true, /* iconifiable */true); setOpaque(true); JLayeredPane desktop = Environment.getInternalFrameDesktop(); Debug.message("windows", "WindowSupport creating internal frame"); if (desktop != null) { desktop.remove(this); desktop.add(this, JLayeredPane.PALETTE_LAYER); } else { Debug.output("WindowSupport: No desktop set for internal frame"); } } public Container getWindow() { return this; } public ComponentEvent kill() { setVisible(false); return new ComponentEvent(this, ComponentEvent.COMPONENT_HIDDEN); } public void setContent(Component content) { Container cp = getContentPane(); // cp.removeAll(); cp.add(content); pack(); } public Dimension getContentSize() { return getContentPane().getSize(); } public void show(int x, int y) { if (!isVisible()) { super.show(); } toFront(); } } public static class Dlg extends JDialog implements WSDisplay { public Dlg(Frame owner, String title) { super(owner, title); Debug.message("windows", "WindowSupport creating frame"); } public Container getWindow() { return this; } public ComponentEvent kill() { setVisible(false); return new ComponentEvent(this, ComponentEvent.COMPONENT_HIDDEN); } public void setContent(Component content) { Container cp = getContentPane(); cp.removeAll(); cp.add(content); pack(); } public Dimension getContentSize() { return getContentPane().getSize(); } public void show(int x, int y) { if (!isVisible()) { Window owner = getOwner(); if (x <= 0 && y <= 0) { if (owner != null) { setLocationRelativeTo(owner); } else if (owner == null) { setDefaultPosition(this); } } } super.setVisible(true); } } public static class Frm extends JFrame implements WSDisplay { public Frm(String title) { super(title); // Need to call this to get the frame to pay attention to requests // on where to locate it if it should be centered on the screen. setLocation(-1, -1); } public Frm(String title, boolean undecorated) { super(title); setUndecorated(undecorated); // Need to call this to get the frame to pay attention to requests // on where to locate it if it should be centered on the screen. setLocation(-1, -1); } public Container getWindow() { return this; } public ComponentEvent kill() { setVisible(false); return new ComponentEvent(this, ComponentEvent.COMPONENT_HIDDEN); } public void setContent(Component content) { Container cp = getContentPane(); cp.removeAll(); cp.add(content); pack(); } public Dimension getContentSize() { return getContentPane().getSize(); } public void show(int x, int y) { if (!isVisible()) { Window owner = getOwner(); if (x <= 0 && y <= 0) { if (owner != null) { setLocationRelativeTo(owner); } else if (owner == null) { setDefaultPosition(this); } } super.setVisible(true); } toFront(); } } /** * Set the FavIcon for the WSDisplay. * * @param wsd */ protected void setFavIcon(WSDisplay wsd) { String iconPath = Environment.get("openmap.favicon"); if (iconPath != null && wsd instanceof Window) { try { BufferedImage favIcon = ImageIO.read(WindowSupport.class.getResourceAsStream(iconPath)); // Java 5 incompatible // ((Window) wsd).setIconImage(favIcon); // Use reflection that'll work if using java 6 and higher. We // can remove all this when OM makes the jump to java 6 or // higher. try { Method siiMethod = wsd.getClass().getMethod("setIconImage", BufferedImage.class); siiMethod.invoke((Window) wsd, favIcon); } catch (NoSuchMethodException e) { Debug.message("windows", e.getMessage()); } catch (SecurityException e) { Debug.message("windows", e.getMessage()); } catch (IllegalAccessException e) { Debug.message("windows", e.getMessage()); } catch (IllegalArgumentException e) { Debug.message("windows", e.getMessage()); } catch (InvocationTargetException e) { Debug.message("windows", e.getMessage()); } } catch (IOException e) { Debug.message("windows", e.getMessage()); } } } }