/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: TopLevel.java * * Copyright (c) 2003 Sun Microsystems and Static Free Software * * Electric(tm) is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Electric(tm) 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool.user.ui; import com.sun.electric.database.text.Pref; import com.sun.electric.database.text.TextUtils; import com.sun.electric.tool.Client; import com.sun.electric.tool.Job; import com.sun.electric.tool.user.ActivityLogger; import com.sun.electric.tool.user.Resources; import com.sun.electric.tool.user.User; import com.sun.electric.tool.user.UserInterfaceMain; import com.sun.electric.tool.user.menus.EMenuBar; import com.sun.electric.tool.user.menus.FileMenu; import com.sun.electric.tool.user.menus.MenuCommands; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JDesktopPane; import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; /** * Class to define a top-level window. * In MDI mode (used by Windows to group multiple documents into a single window) this class is * used for that single top window. * In SDI mode (used elsewhere to give each cell its own window) this class is used many times for each window. */ public class TopLevel extends JFrame { private static final long serialVersionUID = 1L; /** True if in MDI mode, otherwise SDI. */ private static UserInterfaceMain.Mode mode; /** The desktop pane (if MDI). */ private static JDesktopPane desktop = null; /** The main frame (if MDI). */ private static TopLevel topLevel = null; /** The only status bar (if MDI). */ private StatusBar sb = null; /** The size of the screen. */ private static Dimension scrnSize; /** The messagesWindow window. */ private static MessagesWindow messagesWindow; /** The rate of double-clicks. */ private static int doubleClickDelay; /** The cursor being displayed. */ private static Cursor cursor; /** If the busy cursor is overriding the normal cursor */ private static boolean busyCursorOn = false; /** The menu bar */ private EMenuBar.Instance menuBar; /** The tool bar */ private ToolBar toolBar; /** true to resize initial MDI window forces redraw) */ private static final boolean MDIINITIALRESIZE = true; /** * Constructor to build a window. * @param name the title of the window. */ public TopLevel(String name, Rectangle bound, WindowFrame frame, GraphicsConfiguration gc, boolean createStructure) { super(name, gc); setLocation(bound.x, bound.y); setSize(bound.width, bound.height); getContentPane().setLayout(new BorderLayout()); // set an icon on the window setIconImage(getFrameIcon().getImage()); if (createStructure) createStructure(frame); if (isMDIMode()) { addWindowListener(new WindowsEvents()); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addComponentListener(new ReshapeComponentAdapter()); } else { setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); //setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // addWindowFocusListener(EDialog.dialogFocusHandler); } cursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); // For 3D: LightWeight v/s heavy: mixing awt and swing try { javax.swing.JPopupMenu.setDefaultLightWeightPopupEnabled(false); javax.swing.ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false); enableEvents(AWTEvent.WINDOW_EVENT_MASK); } catch (Exception e) { JOptionPane.showMessageDialog(this, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } public void createStructure(WindowFrame frame) { // create the menu bar try{ menuBar = MenuCommands.menuBar().genInstance(); } catch (Exception e) { e.printStackTrace(); } setJMenuBar(menuBar); // create the tool bar toolBar = new ToolBar(); getContentPane().add(toolBar, BorderLayout.NORTH); // create the status bar sb = new StatusBar(frame, false); getContentPane().add(sb, BorderLayout.SOUTH); } /** * Method to return the Icon to use in windows. * @return the Icon to use in windows. */ public static ImageIcon getFrameIcon() { return Resources.getResource(TopLevel.class, "IconElectric.gif"); } public static void InitializeMessagesWindow() { if (isMDIMode()) { String loc = cacheWindowLoc.getString(); Rectangle bound = parseBound(loc); if (bound == null) bound = new Rectangle(scrnSize); if (MDIINITIALRESIZE) bound.width--; // make the desktop desktop = new JDesktopPane(); try{ topLevel = new TopLevel("Electric", bound, null, null, false); } catch (Exception e) { e.printStackTrace(); } topLevel.getContentPane().add(desktop, BorderLayout.CENTER); topLevel.setVisible(true); } messagesWindow = new MessagesWindow(scrnSize); desktop.add(messagesWindow.jf); } /** * Method to initialize the window system with the specified mode. * If mode is null, the mode is implied by the operating system. */ public static void InitializeWindows() { if (isMDIMode()) topLevel.createStructure(null); WindowFrame.createEditWindow(null); FileMenu.updateRecentlyOpenedLibrariesList(); if (MDIINITIALRESIZE && isMDIMode()) { SwingUtilities.invokeLater(new Runnable() { public void run() { Dimension old = topLevel.getSize(); topLevel.setSize(new Dimension(old.width+1, old.height)); }}); } } private static Pref cacheWindowLoc = Pref.makeStringPref("WindowLocation", User.getUserTool().prefs, ""); /** * Method to initialize the window system. */ public static void OSInitialize(UserInterfaceMain.Mode mode) { // setup the size of the screen Toolkit tk = Toolkit.getDefaultToolkit(); scrnSize = tk.getScreenSize(); Object click = tk.getDesktopProperty("awt.multiClickInterval"); if (click == null) doubleClickDelay = 500; else doubleClickDelay = Integer.parseInt(click.toString()); // setup specific look-and-feel UserInterfaceMain.Mode osMode = null; Client.OS os = Client.getOperatingSystem(); try{ switch (os) { case WINDOWS: osMode = UserInterfaceMain.Mode.MDI; scrnSize.height -= 30; UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); break; case UNIX: osMode = UserInterfaceMain.Mode.SDI; //UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); break; case MACINTOSH: osMode = UserInterfaceMain.Mode.SDI; UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.MacLookAndFeel"); break; } } catch(Exception e) {} // set the windowing mode if (mode == null) TopLevel.mode = osMode; else TopLevel.mode = mode; //TopLevel.mode = Mode.MDI; { // default is last used dir if (os == Client.OS.UNIX) { // switch to current dir User.setWorkingDirectory(System.getProperty("user.dir")); } } } private static Rectangle parseBound(String loc) { int lowX = TextUtils.atoi(loc); int commaPos = loc.indexOf(','); if (commaPos < 0) return null; int lowY = TextUtils.atoi(loc.substring(commaPos+1)); int spacePos = loc.indexOf(' '); if (spacePos < 0) return null; int width = TextUtils.atoi(loc.substring(spacePos+1)); int xPos = loc.indexOf('x'); if (xPos < 0) return null; int height = TextUtils.atoi(loc.substring(xPos+1)); return new Rectangle(lowX, lowY, width, height); } /** * Method to tell whether Electric is running in SDI or MDI mode. * SDI is Single Document Interface, where each document appears in its own window. * This is used on UNIX/Linux and on Macintosh. * MDI is Multiple Document Interface, where the main window has all documents in it as subwindows. * This is used on Windows. * @return true if Electric is in MDI mode. */ public static boolean isMDIMode() { return (mode == UserInterfaceMain.Mode.MDI); } /** * Method to return messagesWindow window. * The messagesWindow window runs along the bottom. * @return the messagesWindow window. */ public static MessagesWindow getMessagesWindow() { return messagesWindow; } /** * Method to return status bar associated with this TopLevel. * @return the status bar associated with this TopLevel. */ public StatusBar getStatusBar() { return sb; } /** * Get the tool bar associated with this TopLevel * @return the ToolBar. */ public ToolBar getToolBar() { return toolBar; } /** Get the Menu Bar. Unfortunately named because getMenuBar() already exists */ public EMenuBar.Instance getTheMenuBar() { return menuBar; } /** Get the Menu Bar. Unfortunately named because getMenuBar() already exists */ public EMenuBar getEMenuBar() { return menuBar.getMenuBarGroup(); } /** * Method to return the speed of double-clicks (in milliseconds). * @return the speed of double-clicks (in milliseconds). */ public static int getDoubleClickSpeed() { return doubleClickDelay; } /** * Method to return the size of the screen that Electric is on. * @return the size of the screen that Electric is on. */ public static Dimension getScreenSize() { if (isMDIMode()) { Rectangle bounds = topLevel.getBounds(); Rectangle dBounds = desktop.getBounds(); if (dBounds.width != 0 && dBounds.height != 0) { return new Dimension(dBounds.width, dBounds.height); } return new Dimension(bounds.width-8, bounds.height-96); } return new Dimension(scrnSize); } /** * Method to add an internal frame to the desktop. * This only makes sense in MDI mode, where the desktop has multiple subframes. * @param jif the internal frame to add. */ public static void addToDesktop(JInternalFrame jif) { if (desktop.isVisible() && !Job.isClientThread()) SwingUtilities.invokeLater(new ModifyToDesktopSafe(jif, true)); else (new ModifyToDesktopSafe(jif, true)).run(); } /** * Method to remove an internal frame from the desktop. * This only makes sense in MDI mode, where the desktop has multiple subframes. * @param jif the internal frame to remove. */ public static void removeFromDesktop(JInternalFrame jif) { if (desktop.isVisible() && !Job.isClientThread()) SwingUtilities.invokeLater(new ModifyToDesktopSafe(jif, false)); else (new ModifyToDesktopSafe(jif, false)).run(); } private static class ModifyToDesktopSafe implements Runnable { private JInternalFrame jif; private boolean add; private ModifyToDesktopSafe(JInternalFrame jif, boolean add) { this.jif = jif; this.add = add; } public void run() { if (add) { desktop.add(jif); try { jif.show(); } catch (ClassCastException e) { // Jake Baker keeps getting a ClassCastException here, so for now, let's catch it System.out.println("ERROR: Could not show new window: " + e.getMessage()); } } else { desktop.remove(jif); } } } public static Cursor getCurrentCursor() { return cursor; } public static synchronized void setCurrentCursor(Cursor cursor) { TopLevel.cursor = cursor; setCurrentCursorPrivate(cursor); } private static synchronized void setCurrentCursorPrivate(Cursor cursor) { if (mode == UserInterfaceMain.Mode.MDI) { JFrame jf = TopLevel.getCurrentJFrame(); if (jf != null) jf.setCursor(cursor); } for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); wf.setCursor(cursor); } } public static synchronized List<ToolBar> getToolBars() { ArrayList<ToolBar> toolBars = new ArrayList<ToolBar>(); if (mode == UserInterfaceMain.Mode.MDI) { toolBars.add(topLevel.getToolBar()); } else { for (Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); //toolBars.add(wf.getFrame().getToolBar()); } } return toolBars; } public static synchronized List<EMenuBar.Instance> getMenuBars() { ArrayList<EMenuBar.Instance> menuBars = new ArrayList<EMenuBar.Instance>(); if (mode == UserInterfaceMain.Mode.MDI) { if (topLevel != null) if (topLevel.getTheMenuBar() != null) menuBars.add(topLevel.getTheMenuBar()); } else { for (Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); //menuBars.add(wf.getFrame().getTheMenuBar()); } } return menuBars; } /** * The busy cursor overrides any other cursor. * Call clearBusyCursor to reset to last set cursor */ public static synchronized void setBusyCursor(boolean on) { if (on) { if (!busyCursorOn) setCurrentCursorPrivate(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); busyCursorOn = true; } else { // if the current cursor is a busy cursor, set it to the last normal cursor if (busyCursorOn) setCurrentCursorPrivate(getCurrentCursor()); busyCursorOn = false; } } /** * Method to return the current JFrame on the screen. * @return the current JFrame. */ public static TopLevel getCurrentJFrame() { return getCurrentJFrame(true); } /** * Method to return the current JFrame on the screen. * @param makeNewFrame whether or not to make a new WindowFrame if no current frame * @return the current JFrame. */ public static TopLevel getCurrentJFrame(boolean makeNewFrame) { if (isMDIMode()) return topLevel; WindowFrame wf = WindowFrame.getCurrentWindowFrame(makeNewFrame); if (wf == null) return null; return (TopLevel) wf.getFrame(); } // /** // * Method to set the WindowFrame associated with this top-level window. // * This only makes sense for SDI applications where a WindowFrame is inside of a TopLevel. // * @param wf the WindowFrame to associatd with this. // */ // public void setWindowFrame(WindowFrame wf) { this.wf = wf; } /** * Method called when done with this Frame. Both the menuBar * and toolBar have persistent state in static hash tables to maintain * consistency across different menu bars and tool bars in SDI mode. * Those references must be nullified for garbage collection to reclaim * that memory. This is really for SDI mode, because in MDI mode the * TopLevel is only closed on exit, and all the application memory will be freed. * <p> * NOTE: JFrame does not get garbage collected after dispose() until * some arbitrary point later in time when the garbage collector decides * to free it. */ public void finished() { //System.out.println(this.getClass()+" being disposed of"); // clean up menubar setJMenuBar(null); // TODO: figure out why Swing still sends events to finished menuBars menuBar.finished(); menuBar = null; // clean up toolbar Container container = getContentPane(); if (container != null) container.remove(toolBar); // getContentPane().remove(toolBar); toolBar.finished(); toolBar = null; // clean up scroll bar if (container != null) container.remove(sb); sb.finished(); sb = null; /* Note that this gets called from WindowFrame, and WindowFrame has a reference to EditWindow, so WindowFrame will call wnd.finished(). */ // dispose of myself super.dispose(); } /** * Method to return a list of possible window areas. * On MDI systems, there is just one window areas. * On SDI systems, there is one window areas for each display head on the computer. * @return an array of window areas. */ public static Rectangle [] getWindowAreas() { Rectangle [] areas; if (isMDIMode()) { TopLevel tl = getCurrentJFrame(); Dimension sz = tl.getContentPane().getSize(); areas = new Rectangle[1]; areas[0] = new Rectangle(0, 0, sz.width, sz.height); } else { areas = getDisplays(); } return areas; } /** * Method to return a list of display areas, one for each display head on the computer. * @return an array of display areas. */ public static Rectangle [] getDisplays() { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice [] gs = ge.getScreenDevices(); Rectangle [] areas = new Rectangle[gs.length]; for (int j = 0; j < gs.length; j++) { GraphicsDevice gd = gs[j]; GraphicsConfiguration gc = gd.getDefaultConfiguration(); areas[j] = gc.getBounds(); } return areas; } /** * Print error message <code>msg</code> and stack trace * if <code>print</code> is true. * @param print print error message and stack trace if true * @param msg error message to print */ public static void printError(boolean print, String msg) { if (print) { Throwable t = new Throwable(msg); System.out.println(t.toString()); ActivityLogger.logException(t); } } private static class ReshapeComponentAdapter extends ComponentAdapter { public void componentMoved (ComponentEvent e) { saveLocation(e); } public void componentResized (ComponentEvent e) { saveLocation(e); } private void saveLocation(ComponentEvent e) { TopLevel frame = (TopLevel)e.getSource(); Rectangle bounds = frame.getBounds(); cacheWindowLoc.setString(bounds.getMinX() + "," + bounds.getMinY() + " " + bounds.getWidth() + "x" + bounds.getHeight()); } } /** * This class handles close events for JFrame objects (used in MDI mode to quit). */ private static class WindowsEvents extends WindowAdapter { WindowsEvents() { super(); } public void windowClosing(WindowEvent evt) { FileMenu.quitCommand(); } } }