/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: WindowFrame.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.Main; import com.sun.electric.database.change.DatabaseChangeEvent; import com.sun.electric.database.change.DatabaseChangeListener; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.hierarchy.Library; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.topology.PortInst; import com.sun.electric.database.variable.ElectricObject; import com.sun.electric.database.variable.VarContext; import com.sun.electric.technology.Technology; import com.sun.electric.tool.Client; import com.sun.electric.tool.Job; import com.sun.electric.tool.simulation.Stimuli; import com.sun.electric.tool.user.*; import com.sun.electric.tool.user.dialogs.PreferencesFrame; import com.sun.electric.tool.user.menus.FileMenu; import com.sun.electric.tool.user.menus.WindowMenu; import com.sun.electric.tool.user.waveform.WaveformWindow; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.EventListener; import java.util.Iterator; import java.util.List; import java.util.Observable; import java.beans.PropertyVetoException; import javax.swing.*; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; import javax.swing.tree.MutableTreeNode; /** * This class defines an edit window, with a cell explorer on the left side. */ public class WindowFrame extends Observable { /** offset between newly created windows */ private static final int WINDOW_OFFSET = 0; // was 150 /** the nature of the main window part (from above) */ private WindowContent content; /** the split pane that shows explorer and edit. */ private JSplitPane js; /** the internal frame (if MDI). */ private JInternalFrame jif = null; /** the top-level frame (if SDI). */ private TopLevel jf = null; /** the internalframe listener */ private InternalWindowsEvents internalWindowsEvents; /** the window event listener */ private WindowsEvents windowsEvents; /** the side bar (explorer, components, etc) */ private JTabbedPane sideBar; /** true if sidebar is on the left */ private boolean sideBarOnLeft; /** the explorer tab */ private ExplorerTree explorerTab; /** the component tab */ private PaletteFrame paletteTab; /** the layers tab */ private LayerTab layersTab; /** true if this window is finished */ private boolean finished = false; /** the index of this window */ private int index; /** indicator of the most recent used window */ private int usageClock; /** the dynamic menu to hold WindowMenus */ private JMenu dynamicMenu; /** the overall usage counter */ private static int usageCounter = 0; /** the unique index number of all windows */ private static int windowIndexCounter = 0; /** the offset of each new windows from the last */ private static int windowOffset = 0; /** the list of all windows on the screen */ private static List<WindowFrame> windowList = new ArrayList<WindowFrame>(); /** the current windows. */ private static WindowFrame curWindowFrame = null; /** current mouse listener */ public static MouseListener curMouseListener = ClickZoomWireListener.theOne; /** current mouse motion listener */ public static MouseMotionListener curMouseMotionListener = ClickZoomWireListener.theOne; /** current mouse wheel listener */ public static MouseWheelListener curMouseWheelListener = ClickZoomWireListener.theOne; /** current key listener */ public static KeyListener curKeyListener = ClickZoomWireListener.theOne; /** * Use by highlight tools to bring the frame to the front. * @param wf */ public static void showFrame(WindowFrame wf) { //if (TopLevel.isMDIMode()) { JInternalFrame jif = wf.getInternalFrame(); try { jif.setIcon(false); jif.setSelected(true); } catch (PropertyVetoException e) {} if (!jif.isVisible()) { jif.toFront(); Main.addToDesktop(jif); } else jif.toFront(); //} else { // JFrame jf = (JFrame) wf.getFrame(); // jf.setState(Frame.NORMAL); // if (!jf.isVisible()) { // jf.toFront(); // jf.setVisible(true); // } else // jf.toFront(); //} } public static void repaintAllWindows() { for(Iterator<WindowFrame> it = getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); WindowContent content = wf.getContent(); content.fullRepaint(); } for(Iterator<WindowFrame> it = getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); wf.loadComponentMenuForTechnology(); } PreferencesFrame.updateLayerPreferencesColors(); } /** library tree updater */ static { new LibraryTreeUpdater(); } public static class DisplayAttributes { /** the window scale */ public final double scale; /** the window offset */ public final double offX, offY; /** path to cell being edited (down-in-place) */ public final List<NodeInst> inPlaceDescent; public DisplayAttributes() { this(1, 0, 0, Collections.<NodeInst>emptyList()); } public DisplayAttributes(EditWindow wnd) { this(wnd.getScale(), wnd.getOffset().getX(), wnd.getOffset().getY(), wnd.getInPlaceEditNodePath()); } public DisplayAttributes(double scale, double offX, double offY, List<NodeInst> inPlaceDescent) { this.scale = scale; this.offX = offX; this.offY = offY; this.inPlaceDescent = new ArrayList<NodeInst>(inPlaceDescent); } public AffineTransform getIntoCellTransform() { AffineTransform intoCell = new AffineTransform(); for (NodeInst ni : inPlaceDescent) intoCell.preConcatenate(ni.rotateIn(ni.translateIn())); return intoCell; } public AffineTransform getOutofCellTransform() { AffineTransform outofCell = new AffineTransform(); for (NodeInst ni : inPlaceDescent) outofCell.concatenate(ni.translateOut(ni.rotateOut())); return outofCell; } } //******************************** CONSTRUCTION ******************************** // constructor public WindowFrame() { index = windowIndexCounter++; cellHistory = new ArrayList<CellHistory>(); cellHistoryLocation = -1; } /** * Method to set the dynamic menu where * @param menu */ public void setDynamicMenu(JMenu menu) {dynamicMenu = menu;} public JMenu getDynamicMenu() { return dynamicMenu; } /** * Method to create a new circuit-editing window on the screen that displays a Cell. * @param cell the cell to display. * @return the WindowFrame that shows the Cell. */ public static WindowFrame createEditWindow(final Cell cell) { final WindowFrame frame = new WindowFrame(); if (cell != null && cell.getView().isTextView()) { TextWindow tWnd = new TextWindow(cell, frame); frame.finishWindowFrameInformation(tWnd, cell); tWnd.fillScreen(); } else { EditWindow eWnd = EditWindow.CreateElectricDoc(cell, frame, null); frame.finishWindowFrameInformation(eWnd, cell); eWnd.fillScreen(); eWnd.fullRepaint(); } removeUIBinding(frame.js, KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0)); removeUIBinding(frame.js, KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0)); if (cell != null) { SwingUtilities.invokeLater(new Runnable() { public void run() { frame.explorerTab.openLibraryInExplorerTree(cell.getLibrary(), cell, true); } }); } return frame; } /** * Method to get the unique index number of this WindowFrame. * @return the unique index number of this WindowFrame. */ public int getIndex() { return index; } /** * Method to find the WindowFrame associated with an index number. * @param index the index number. * @return the associated WindowFrame (null if none found). */ public static WindowFrame findFromIndex(int index) { for (WindowFrame wf: windowList) if (wf.index == index) return wf; return null; } /** * Method to finish with frame pointers. */ public void finishWindowFrameInformation(WindowContent wnd, Cell cell) { buildWindowStructure(wnd, cell, null); setCurrentWindowFrame(this); populateJFrame(); // Adding into WindowMenu WindowMenu.setDynamicMenus(); } /***************************************************************************** * 3D Stuff * *****************************************************************************/ /** * Method to access 3D view and highligh elements if view is available */ public static void show3DHighlight() { for(Iterator<WindowFrame> it = getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); wf.setChanged(); wf.notifyObservers(wf.getContent()); wf.clearChanged(); } } /** * Method to notify 3D view about change of Layer visibility */ public void set3DLayerVisibility(LayerVisibility lv) { setChanged(); notifyObservers(lv); clearChanged(); } /***************************************************************************** * END OF 3D Stuff * *****************************************************************************/ /** * Method to create a new waveform window on the screen given the simulation data. * @param sd the simulation data to use in the waveform window. * @return the WindowFrame that shows the waveforms. */ public static WindowFrame createWaveformWindow(Stimuli sd) { WindowFrame frame = new WindowFrame(); WaveformWindow wWnd = new WaveformWindow(sd, frame); frame.finishWindowFrameInformation(wWnd, sd.getCell()); wWnd.fillScreen(); // open the "SIGNALS" part of the explorer panel SwingUtilities.invokeLater(new Runnable() { public void run() { WindowFrame.wantToOpenCurrentLibrary(false, null); }}); return frame; } /** * Method to update all technology popup selectors. * Called when a new technology has been created. */ public static void updateTechnologyLists() { for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); wf.paletteTab.loadTechnologies(false); wf.layersTab.loadTechnologies(false); } } /** * Class to handle changes the "Technology" selection of either the Layer tab or the Component tab of the side bar. */ public static class CurTechControlListener implements ActionListener { private WindowFrame wf; CurTechControlListener(WindowFrame wf) { this.wf = wf; } public void actionPerformed(ActionEvent evt) { JComboBox source = (JComboBox)evt.getSource(); String techName = (String)source.getSelectedItem(); Technology tech = Technology.findTechnology(techName); if (tech != null) { // change the technology Job.getExtendedUserInterface().setCurrentTechnology(tech); wf.getPaletteTab().loadForTechnology(tech, wf); wf.getLayersTab().updateLayersTab(); } } } public List<MutableTreeNode> loadDefaultExplorerTree() { MutableTreeNode libraryExplorerNode = ExplorerTreeModel.makeLibraryTree(); return Collections.singletonList(libraryExplorerNode); } private Dimension buildWindowStructure(WindowContent content, Cell cell, GraphicsConfiguration gc) { this.content = content; // the left half: an explorer tree in a scroll pane explorerTab = new ExplorerTree(content.loadExplorerTrees()); JScrollPane scrolledTree = new JScrollPane(explorerTab); // make a tabbed list of panes on the left sideBar = new JTabbedPane(); // Only Mac version will align tabs on the left. The text orientation is vertical by default on Mac if (Client.isOSMac()) sideBar.setTabPlacement(JTabbedPane.LEFT); paletteTab = PaletteFrame.newInstance(this); layersTab = new LayerTab(this); loadComponentMenuForTechnology(); sideBar.add("Components", paletteTab.getTechPalette()); sideBar.add("Explorer", scrolledTree); sideBar.add("Layers", layersTab); sideBar.setSelectedIndex(User.getDefaultWindowTab()); sideBar.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { JTabbedPane tp = (JTabbedPane)evt.getSource(); User.setDefaultWindowTab(tp.getSelectedIndex()); } }); // initialize the frame String cellDescription = (cell == null) ? "no cell" : cell.describe(false); Dimension sz = createJFrame(cellDescription, gc); windowOffset += 70; if (windowOffset > 300) windowOffset = 0; // put them together into the split pane js = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); sideBarOnLeft = !User.isSideBarOnRight(); if (sideBarOnLeft) { js.setLeftComponent(sideBar); js.setRightComponent(content.getPanel()); js.setDividerLocation(200); } else { js.setLeftComponent(content.getPanel()); js.setRightComponent(sideBar); js.setDividerLocation(sz.width - 200); } // accumulate a list of current windows synchronized(windowList) { windowList.add(this); } return sz; } /** * Method to create a JFrame that will hold all the Components in this WindowFrame. * @param title the window title. * @param gc the GraphicsConfiguration (which screen the window is on). * @return the size of the frame. */ private Dimension createJFrame(String title, GraphicsConfiguration gc) { Dimension scrnSize = Main.getScreenSize(); Dimension frameSize = new Dimension(scrnSize.width * 4 / 5, scrnSize.height * 6 / 8); //if (TopLevel.isMDIMode()) //{ jif = new JInternalFrame(title, true, true, true, true); jif.setSize(frameSize); jif.setLocation(windowOffset+WINDOW_OFFSET, windowOffset); jif.setAutoscrolls(true); jif.setFrameIcon(TopLevel.getFrameIcon()); //} else //{ // jf = new TopLevel("Electric - " + title, new Rectangle(frameSize), this, gc, true); // Dimension preferredSize = User.getDefaultWindowSize(); // if (preferredSize != null) frameSize = preferredSize; // jf.setSize(frameSize); // Point preferredLoc = User.getDefaultWindowPos(); // jf.setLocation(windowOffset+WINDOW_OFFSET+preferredLoc.x, windowOffset+preferredLoc.y); //} return frameSize; } /** * Set the Technology popup in the component tab and the layers tab to the current technology. */ public void loadComponentMenuForTechnology() { Technology tech = Technology.getCurrent(); if (content.getCell() != null) { tech = content.getCell().getTechnology(); } //Technology tech = Technology.findTechnology(User.getDefaultTechnology()); paletteTab.loadForTechnology(tech, this); layersTab.updateLayersTab(); } /** * Populate the JFrame with the Components */ private void populateJFrame() { //if (TopLevel.isMDIMode()) //{ jif.getContentPane().add(js); internalWindowsEvents = new InternalWindowsEvents(this); jif.addInternalFrameListener(internalWindowsEvents); // add tool bar as listener so it can find out state of cell history in EditWindow // jif.addInternalFrameListener(TopLevel.getCurrentJFrame().getToolBar()); //content.getPanel().addPropertyChangeListener(TopLevel.getTopLevel().getToolBar()); // content.getPanel().addPropertyChangeListener(EditWindow.propGoBackEnabled, TopLevel.getCurrentJFrame().getToolBar()); // content.getPanel().addPropertyChangeListener(EditWindow.propGoForwardEnabled, TopLevel.getCurrentJFrame().getToolBar()); // frame.jif.moveToFront(); Main.addToDesktop(jif); //} else //{ // jf.getContentPane().add(js); // windowsEvents = new WindowsEvents(this); // jf.addWindowListener(windowsEvents); // jf.addWindowFocusListener(windowsEvents); // add tool bar as listener so it can find out state of cell history in EditWindow // content.getPanel().addPropertyChangeListener(EditWindow.propGoBackEnabled, ((TopLevel)jf).getToolBar()); // content.getPanel().addPropertyChangeListener(EditWindow.propGoForwardEnabled, ((TopLevel)jf).getToolBar()); /*if (!Job.BATCHMODE)*/ //jf.setVisible(true); //} } public Component getComponent() { return jif; //return jf; } /** * Method to return the usage clock of this WindowFrame. * Each activation of the WindowFrame increments the value, so the WindowFrame * with the highest value is the one most recently activated. * @return the usage clock of this WindowFrame. */ public int getUsageClock() { return usageClock; } /** * Depopulate the JFrame. Currently this is only used in SDI mode when * moving a WindowFrame from one display to another. A new JFrame on the * new display must be created and populated with the WindowFrame Components. * To do so, those components must first be removed from the old frame. */ private void depopulateJFrame() { assert SwingUtilities.isEventDispatchThread(); //if (TopLevel.isMDIMode()) { jif.getContentPane().remove(js); jif.removeInternalFrameListener(internalWindowsEvents); Main.removeFromDesktop(jif); //} else { // jf.getContentPane().remove(js); // jf.removeWindowListener(windowsEvents); // jf.removeWindowFocusListener(windowsEvents); //} } //******************************** WINDOW CONTROL ******************************** /** * Method to show a cell in the right-part of this WindowFrame. * Handles both circuit cells and text cells. * @param cell the Cell to display. */ public void setCellWindow(Cell cell, CellHistory history) { if (cell != null && cell.getView().isTextView()) { // want a TextWindow here if (!(getContent() instanceof TextWindow)) { getContent().finished(); content = new TextWindow(cell, this); int i = js.getDividerLocation(); if (sideBarOnLeft) js.setRightComponent(content.getPanel()); else js.setLeftComponent(content.getPanel()); js.setDividerLocation(i); content.fillScreen(); if (history == null) addToHistory(cell, VarContext.globalContext, null); currentCellChanged(); return; } } else { // want an EditWindow here if (!(getContent() instanceof EditWindow)) { getContent().finished(); Component c = js.getLeftComponent(); if (sideBarOnLeft) c = js.getRightComponent(); Dimension sz = c.getSize(); content = EditWindow.CreateElectricDoc(cell, this, sz); int i = js.getDividerLocation(); if (sideBarOnLeft) js.setRightComponent(content.getPanel()); else js.setLeftComponent(content.getPanel()); js.setDividerLocation(i); content.fillScreen(); if (history == null) addToHistory(cell, VarContext.globalContext, null); currentCellChanged(); loadComponentMenuForTechnology(); return; } } // proper window type already there: switch cells DisplayAttributes da = null; VarContext upperContext = VarContext.globalContext; if (history != null) { da = history.da; upperContext = history.context; } content.setCell(cell, upperContext, da); currentCellChanged(); loadComponentMenuForTechnology(); // Adding into WindowMenu WindowMenu.setDynamicMenus(); if (history == null) addToHistory(cell, VarContext.globalContext, null); } public void moveEditWindow(GraphicsConfiguration gc) { //if (true) return; // only valid in SDI mode //jf.setVisible(false); // hide old Frame //jf.getFocusOwner().setFocusable(false); //System.out.println("Set unfocasable: "+jf.getFocusOwner()); //depopulateJFrame(); // remove all components from old Frame //TopLevel oldFrame = jf; //Cell cell = content.getCell(); // get current cell //String cellDescription = (cell == null) ? "no cell" : cell.describe(false); // new title //createJFrame(cellDescription, gc); // create new Frame //populateJFrame(); // populate new Frame //fireCellHistoryStatus(); // update tool bar history buttons //oldFrame.finished(); // clear and garbage collect old Frame } //******************************** EXPLORER PART ******************************** /** * Method to force the explorer tree to show the current library or signals list. * @param openLib true to open the current library, false to open the signals list. * @param cell cell to select in ExplorerTree */ public static void wantToOpenCurrentLibrary(boolean openLib, Cell cell) { for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); wf.explorerTab.openLibraryInExplorerTree(Library.getCurrent(), cell, openLib); } } /** * Method to request that the library tree be reloaded. */ public static void wantToRedoLibraryTree() { for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); wf.explorerTab.redoContentTrees(wf.content.loadExplorerTrees()); } } public void wantToRedoSignalTree() { if (!SwingUtilities.isEventDispatchThread()) { SwingUtilities.invokeLater(new Runnable() { public void run() { wantToRedoSignalTree(); } }); return; } explorerTab.redoContentTrees(content.loadExplorerTrees()); } public static void setSideBarLocation(boolean onLeft) { WindowFrame wf = getCurrentWindowFrame(); if (wf.sideBarOnLeft == onLeft) return; wf.sideBarOnLeft = onLeft; wf.js.setLeftComponent(null); wf.js.setRightComponent(null); Dimension sz = wf.js.getSize(); int loc = sz.width - wf.js.getDividerLocation(); wf.js.setDividerLocation(loc); if (onLeft) { wf.js.setLeftComponent(wf.sideBar); wf.js.setRightComponent(wf.content.getPanel()); } else { wf.js.setLeftComponent(wf.content.getPanel()); wf.js.setRightComponent(wf.sideBar); } } //******************************** INTERFACE ******************************** /** * Method to get the content of this window. * The content is the object in the right side (EditWindow, WaveformWindow, etc.) * @return the content of this window. */ public WindowContent getContent() { return content; } /** * Method to get the current WindowFrame. If there is no current * WindowFrame, then it will create a new EditWindow window frame. * @return the current WindowFrame. */ public static WindowFrame getCurrentWindowFrame() { return getCurrentWindowFrame(true); } /** * Method to get the current WindowFrame. If 'makeNewFrame' is true, * then this will make a new EditWindow window frame if there is no * current frame. If 'makeNewFrame' is false, this will return null * if there is no current WindowFrame. * @param makeNewFrame whether or not to make a new WindowFrame if no current frame * @return the current WindowFrame. May return null if 'makeNewFrame' is false. */ public static WindowFrame getCurrentWindowFrame(boolean makeNewFrame) { synchronized(windowList) { if ((curWindowFrame == null) && makeNewFrame) { for (WindowFrame wf: windowList) { // get last in list curWindowFrame = wf; } if (curWindowFrame == null) curWindowFrame = createEditWindow(null); } return curWindowFrame; } } /** * Method to set the current listener that responds to clicks in any window. * There is a single listener in effect everywhere, usually controlled by the toolbar. * @param listener the new lister to be in effect. */ public static void setListener(EventListener listener) { curMouseListener = (MouseListener)listener; curMouseMotionListener = (MouseMotionListener)listener; curMouseWheelListener = (MouseWheelListener)listener; curKeyListener = (KeyListener)listener; } /** * Method to get the current listener that responds to clicks in any window. * There is a single listener in effect everywhere, usually controlled by the toolbar. * @return the current listener. */ public static EventListener getListener() { return curMouseListener; } /** * Method to return the current Cell. */ public static Cell getCurrentCell() { assert SwingUtilities.isEventDispatchThread(); WindowFrame wf = WindowFrame.getCurrentWindowFrame(false); if (wf == null) return null; return wf.getContent().getCell(); } /** * Method to insist on a current Cell. * Prints an error message if there is no current Cell. * @return the current Cell in the current Library. * Returns NULL if there is no current Cell. */ public static Cell needCurCell() { Cell curCell = getCurrentCell(); if (curCell == null) { System.out.println("There is no current cell for this operation. To create one, use the 'New Cell' command from the 'Cell' menu."); } return curCell; } /** * Method to set the cursor that is displayed in the WindowFrame. * @param cursor the cursor to display here. */ public void setCursor(Cursor cursor) { //jf.setCursor(cursor); //js.setCursor(cursor); //content.setCursor(cursor); } public static void removeLibraryReferences(Library lib) { for (Iterator<WindowFrame> it = getWindows(); it.hasNext(); ) { WindowFrame wf = it.next(); WindowContent content = wf.getContent(); Cell cell = content.getCell(); if (cell != null && cell.getLibrary() == lib) content.setCell(null, null, null); content.fullRepaint(); } } /** * Method to request focus on this window */ public void requestFocus() { if (!SwingUtilities.isEventDispatchThread()) { SwingUtilities.invokeLater(new Runnable() { public void run() { requestFocusUnsafe(); } }); return; } requestFocusUnsafe(); } private void requestFocusUnsafe() { if (jif != null) { jif.toFront(); try { jif.setSelected(true); } catch (java.beans.PropertyVetoException e) {} } if (jf != null) { jf.toFront(); jf.requestFocus(); } } public boolean isFocusOwner() { if (jif != null) return jif.isSelected(); if (jf != null) return jf.isFocusOwner(); return false; } /** * Method to set the current WindowFrame. * @param wf the WindowFrame to make current. */ private static void setCurrentWindowFrame(WindowFrame wf) { synchronized(windowList) { if (wf.finished) return; // don't do anything, the window is finished curWindowFrame = wf; // set this to be last in list windowList.remove(wf); windowList.add(wf); } if (wf != null) { wf.currentCellChanged(); if (wf.getContent() != null) { Highlighter highlighter = wf.getContent().getHighlighter(); if (highlighter != null) highlighter.gainedFocus(); } } wantToRedoTitleNames(); } private void currentCellChanged() { if (this != getCurrentWindowFrame(false)) return; Cell cell = getContent().getCell(); if (cell != null) { cell.getLibrary().setCurCell(cell); // if auto-switching technology, do it autoTechnologySwitch(cell, this); } } public static void wantToRedoTitleNames() { // rebuild window titles for (Iterator<WindowFrame> it = getWindows(); it.hasNext(); ) { WindowFrame w = it.next(); WindowContent content = w.getContent(); if (content != null) content.setWindowTitle(); } WindowMenu.setDynamicMenus(); } public void setWindowSize(Rectangle frameRect) { Dimension frameSize = new Dimension(frameRect.width, frameRect.height); //if (TopLevel.isMDIMode()) //{ jif.setSize(frameSize); jif.setLocation(frameRect.x, frameRect.y); //} else //{ // jf.setSize(frameSize); // jf.setLocation(frameRect.x, frameRect.y); // jf.validate(); //} } /** * Method to record that this WindowFrame has been closed. * This method is called from the event handlers on the windows. */ public void finished() { // if this was called from the code, instead of an event handler, // make sure we're not visible anymore //if (TopLevel.isMDIMode()) { jif.setVisible(false); jif.dispose(); //} // remove references to this synchronized(windowList) { if (windowList.size() <= 1 && !Client.isOSMac()) { FileMenu.quitCommand(); //JOptionPane.showMessageDialog(TopLevel.getCurrentJFrame(), // "Cannot close the last window"); return; } windowList.remove(this); finished = true; if (curWindowFrame == this) curWindowFrame = null; } // tell EditWindow it's finished content.finished(); explorerTab.setModel(null); WindowMenu.setDynamicMenus(); // if SDI mode, TopLevel enclosing frame is closing, dispose of it //jf.finished(); } /** * Method to return the WaveformWindow associated with this frame. * @return the WaveformWindow associated with this frame. */ public WaveformWindow getWaveformWindow() { return (WaveformWindow)content; } /** * Method to return the Explorer tab associated with this WindowFrame. * @return the Explorer tab associated with this WindowFrame. */ public ExplorerTree getExplorerTab() { return explorerTab; } /** * Method to return the component palette associated with this WindowFrame. * @return the component palette associated with this WindowFrame. */ public PaletteFrame getPaletteTab() { return paletteTab; } /** * Method to return the layer visibility tab associated with this WindowFrame. * @return the layer visibility tab associated with this WindowFrame. */ public LayerTab getLayersTab() { return layersTab; } /** * Method to return the TopLevel associated with this WindowFrame. * In SDI mode this returns this WindowFrame's TopLevel Frame. * In MDI mode there is only one TopLevel frame, so this method will * return that Frame. * @return the TopLevel associated with this WindowFrame. */ public Container getFrame() { //if (TopLevel.isMDIMode()) { return Main.getCurrentJFrame(); //} //return jf; } /** * Method to return the JInternalFrame associated with this WindowFrame. * In SDI mode this returns null. * @return the JInternalFrame associated with this WindowFrame. */ public JInternalFrame getInternalFrame() { //if (TopLevel.isMDIMode()) { return jif; //} //return null; } /** * Returns true if this window frame or it's components generated * this event. * @param e the event generated * @return true if this window frame or it's components generated this * event, false otherwise. */ public boolean generatedEvent(java.awt.AWTEvent e) { if (e instanceof InternalFrameEvent) { JInternalFrame source = ((InternalFrameEvent)e).getInternalFrame(); if (source == jif) return true; return false; } return false; } /** * Method to return the number of WindowFrames. * @return the number of WindowFrames. */ public static int getNumWindows() { synchronized(windowList) { return windowList.size(); } } /** * Method to return an Iterator over all WindowFrames. * @return an Iterator over all WindowFrames. */ public static Iterator<WindowFrame> getWindows() { ArrayList<WindowFrame> listCopy = new ArrayList<WindowFrame>(); synchronized(windowList) { listCopy.addAll(windowList); } return listCopy.iterator(); } /** * Centralized version of naming windows. Might move it to class * that would replace WindowContext * @param cell the cell in the window. * @param prefix a prefix for the title. */ public String composeTitle(Cell cell, String prefix, int pageNo) { // StringBuffer should be more efficient StringBuffer title = new StringBuffer(); if (cell != null && cell.isLinked()) { title.append(prefix + cell.libDescribe()); if (cell.isMultiPage()) { title.append(" - Page " + (pageNo+1)); } Library curLib = Library.getCurrent(); if (cell.getLibrary() != curLib && curLib != null) title.append(" - Current library: " + curLib.getName()); } else title.append("***NONE***"); return (title.toString()); } /** * Method to set the description on the window frame */ public void setTitle(String title) { String curTitle = getTitle(); if (title.equals(curTitle)) return; //if (TopLevel.isMDIMode()) { if (jif != null) jif.setTitle(title); //} else { // if (jf != null) jf.setTitle(title); //} } /** * Method to get the description on the window frame */ public String getTitle() { //if (TopLevel.isMDIMode()) { if (jif != null) return jif.getTitle(); //} else { // if (jf != null) return jf.getTitle(); //} return ""; } private static void removeUIBinding(JComponent comp, KeyStroke key) { removeUIBinding(comp.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT), key); removeUIBinding(comp.getInputMap(JComponent.WHEN_FOCUSED), key); removeUIBinding(comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW), key); } private static void removeUIBinding(InputMap map, KeyStroke key) { if (map == null) return; map.remove(key); removeUIBinding(map.getParent(), key); } /** * Method to automatically switch to the proper technology for a Cell. * @param cell the cell being displayed. * If technology auto-switching is on, make sure the right technology is displayed * for the Cell. */ public static void autoTechnologySwitch(Cell cell, WindowFrame wf) { if (cell.getView().isTextView()) return; Technology tech = cell.getTechnology(); if (tech != null && tech != Technology.getCurrent()) { if (User.isAutoTechnologySwitch()) { Job.getExtendedUserInterface().setCurrentTechnology(tech); wf.getPaletteTab().setSelectedItem(tech.getTechName()); wf.getLayersTab().setSelectedTechnology(tech); } } } // ************************** Cell History Traversal ************************************* /** List of CellHistory objects */ private List<CellHistory> cellHistory; /** Location in history (points to valid location) */ private int cellHistoryLocation; /** History limit */ private static final int cellHistoryLimit = 20; /** * Class to track CellHistory and associated values. */ public static class CellHistory { /** cell */ private Cell cell; /** context */ private VarContext context; /** the port that is highlighted */ private PortInst selPort; /** display attributes (scale, in-place, etc.) */ private DisplayAttributes da; /** highlights */ private List<Highlight> highlights; /** highlight offset*/ private double offX, offY; public Cell getCell() { return cell; } public VarContext getContext() { return context; } public void setContext(VarContext context) { this.context = context; } public void setSelPort(PortInst selPort) { this.selPort = selPort; } public DisplayAttributes getDisplayAttributes() { return da; } public void setDisplayAttributes(DisplayAttributes da) { this.da = da; } } /** * Method to return the list of cell histories associated with this WindowFrame. * @return the list of cell histories associated with this WindowFrame. */ public List<CellHistory> getCellHistoryList() { return cellHistory; } /** * Method to return the current position in the cell history list. * @return the current position in the cell history list. */ public int getCellHistoryLocation() { return cellHistoryLocation; } /** * Go back in history list. */ public void cellHistoryGoBack() { // at start of history if (cellHistoryLocation <= 0) return; setCellByHistory(cellHistoryLocation-1); } /** * Go forward in history list. */ public void cellHistoryGoForward() { if (cellHistoryLocation >= (cellHistory.size() - 1)) return; // at end of history setCellByHistory(cellHistoryLocation+1); } /** Returns true if we can go back in history list, false otherwise */ private boolean cellHistoryCanGoBack() { if (cellHistoryLocation > 0) return true; return false; } /** Returns true if we can go forward in history list, false otherwise */ private boolean cellHistoryCanGoForward() { if (cellHistoryLocation < cellHistory.size() - 1) return true; return false; } /** * Method to updates back/forward button states. * Used when new tool bar is created with existing edit window * (when moving windows across displays). */ private void fireCellHistoryStatus() { ToolBar toolBar = getToolBar(); if (toolBar == null) return; toolBar.updateCellHistoryStatus(cellHistoryCanGoBack(), cellHistoryCanGoForward()); } /** * Method to return the ToolBar associated with this WindowFrame. * In SDI mode this returns this WindowFrame's ToolBar. * In MDI mode there is only one ToolBar, so this method will * return that ToolBar. * @return the TooolBar associated with this WindowFrame. */ private ToolBar getToolBar() { Main topLevel = (Main) getFrame(); return topLevel != null ? topLevel.getToolBar() : null; } /** * Adds to cellHistory record list. * Should only be called via non-history traversing modifications * to history. (such as Edit->New Cell). */ public void addToHistory(Cell cell, VarContext context, DisplayAttributes da) { if (cell == null) return; CellHistory history = new CellHistory(); history.cell = cell; history.context = context; if (da == null) da = new DisplayAttributes(); history.da = da; // when user has moved back through history, and then edits a new cell, // get rid of forward history if (cellHistoryLocation < (cellHistory.size() - 1)) { // inserting into middle of history: get rid of history forward of this for(int i=cellHistory.size()-1; i>cellHistoryLocation; i--) cellHistory.remove(i); } // update history cellHistory.add(history); cellHistoryLocation = cellHistory.size() - 1; // adjust if we are over the limit if (cellHistoryLocation > cellHistoryLimit) { cellHistory.remove(0); cellHistoryLocation--; } fireCellHistoryStatus(); //showHistory("add to history"); } //private void showHistory(String why) //{ // System.out.println("AFTER "+why+", CELL HISTORY:"); // for(int i=0; i<cellHistory.size(); i++) // { // CellHistory ch = cellHistory.get(i); // System.out.print(" HISTORY "+i); // if (i == cellHistoryLocation) System.out.print("**"); // System.out.println(" cell="+ch.cell.describe(false)+" scale="+ch.da.scale); // } //} /** * Records current cell state into history * Assumes record pointed to by cellHistoryLocation is * history record for the current cell/context. */ public void saveCurrentCellHistoryState() { if (cellHistoryLocation < 0) return; if (content instanceof EditWindow) { EditWindow wnd = (EditWindow)content; CellHistory current = cellHistory.get(cellHistoryLocation); current.context = wnd.getVarContext(); // save zoom and pan // save down-in-place information current.da = new DisplayAttributes(wnd); // save selected port current.selPort = null; Highlighter highlighter = wnd.getHighlighter(); if (highlighter.getNumHighlights() == 1) { Highlight h = highlighter.getOneHighlight(); if (h != null) { ElectricObject eobj = h.getElectricObject(); if (eobj instanceof PortInst) current.selPort = (PortInst)eobj; } } // save highlighting current.highlights = new ArrayList<Highlight>(); Cell cell = content.getCell(); for (Highlight h : highlighter.getHighlights()) { if (h.getCell() == cell) current.highlights.add(h); } Point2D off = highlighter.getHighlightOffset(); if (off != null) { current.offX = off.getX(); current.offY = off.getY(); } } //showHistory("save state"); } /** Restores cell state from history record */ public void setCellByHistory(int location) { // get cell history to go to CellHistory history = cellHistory.get(location); // see if cell still valid part of database. If not, nullify entry if (history.cell == null || !history.cell.isLinked()) { history.cell = null; history.context = VarContext.globalContext; history.selPort = null; history.da = new WindowFrame.DisplayAttributes(); history.highlights = new ArrayList<Highlight>(); history.offX = history.offY = 0; } // update current cell setCellWindow(history.cell, history); if (history.cell != null && !history.cell.getView().isTextView()) { EditWindow wnd = (EditWindow)content; if (history.selPort != null) { wnd.getHighlighter().addElectricObject(history.selPort, history.cell); } else if (history.highlights != null) { wnd.getHighlighter().setHighlightList(history.highlights); wnd.getHighlighter().setHighlightOffset((int)history.offX, (int)history.offY); } } content.fullRepaint(); // point to new location *after* calling setCell, since setCell updates by current location cellHistoryLocation = location; fireCellHistoryStatus(); //showHistory("left/right buttons"); } /** * Method to find a CellHistory index that shows a given cell and context. * @param cell the cell to find. * @param context the cell context to find. * @return the CellHistory index associated with that cell/context * (-1 if not found). */ public int findCellHistoryIndex(Cell cell, VarContext context) { for (int i=cellHistory.size()-1; i>-1; i--) { CellHistory history = cellHistory.get(i); if (history.cell == cell && history.context.equals(context)) return i; } return -1; } //******************************** HANDLERS FOR WINDOW EVENTS ******************************** /** * This class handles activation and close events for JFrame objects (used in SDI mode). */ private class WindowsEvents extends WindowAdapter { /** A weak reference to the WindowFrame */ WeakReference<WindowFrame> wf; WindowsEvents(WindowFrame wf) { super(); this.wf = new WeakReference<WindowFrame>(wf); } public void windowActivated(WindowEvent evt) { WindowFrame realWF = wf.get(); realWF.usageClock = usageCounter++; WindowFrame.setCurrentWindowFrame(realWF); realWF.fireCellHistoryStatus(); // update tool bar history buttons } public void windowClosing(WindowEvent evt) { wf.get().finished(); } } /** * This class handles activation and close events for JInternalFrame objects (used in MDI mode). */ private class InternalWindowsEvents extends InternalFrameAdapter { /** A weak reference to the WindowFrame */ WeakReference<WindowFrame> wf; InternalWindowsEvents(WindowFrame wf) { super(); this.wf = new WeakReference<WindowFrame>(wf); } public void internalFrameClosing(InternalFrameEvent evt) { (wf.get()).finished(); } public void internalFrameActivated(InternalFrameEvent evt) { WindowFrame realWF = wf.get(); realWF.usageClock = usageCounter++; WindowFrame.setCurrentWindowFrame(realWF); realWF.fireCellHistoryStatus(); // update tool bar history buttons } } /** * Database change listener that updates library trees when needed */ private static class LibraryTreeUpdater implements DatabaseChangeListener { private LibraryTreeUpdater() { UserInterfaceMain.addDatabaseChangeListener(this); } public void databaseChanged(DatabaseChangeEvent e) { if (e.cellTreeChanged()) wantToRedoLibraryTree(); } } }