0001: /*
0002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI for
0003: * visualizing and manipulating spatial features with geometry and attributes.
0004: *
0005: * Copyright (C) 2003 Vivid Solutions
0006: *
0007: * This program is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU General Public License as published by the Free
0009: * Software Foundation; either version 2 of the License, or (at your option)
0010: * any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful, but WITHOUT
0013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
0015: * more details.
0016: *
0017: * You should have received a copy of the GNU General Public License along with
0018: * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
0019: * Place - Suite 330, Boston, MA 02111-1307, USA.
0020: *
0021: * For more information, contact:
0022: *
0023: * Vivid Solutions Suite #1A 2328 Government Street Victoria BC V8T 5G5 Canada
0024: *
0025: * (250)385-6040 www.vividsolutions.com
0026: */
0027: package com.vividsolutions.jump.workbench.ui;
0028:
0029: import com.vividsolutions.jts.geom.Envelope;
0030: import com.vividsolutions.jts.util.Assert;
0031: import com.vividsolutions.jump.I18N;
0032: import com.vividsolutions.jump.util.Block;
0033: import com.vividsolutions.jump.util.CollectionUtil;
0034: import com.vividsolutions.jump.util.StringUtil;
0035: import com.vividsolutions.jump.workbench.WorkbenchContext;
0036: import com.vividsolutions.jump.workbench.model.Category;
0037: import com.vividsolutions.jump.workbench.model.CategoryEvent;
0038: import com.vividsolutions.jump.workbench.model.FeatureEvent;
0039: import com.vividsolutions.jump.workbench.model.Layer;
0040: import com.vividsolutions.jump.workbench.model.LayerEvent;
0041: import com.vividsolutions.jump.workbench.model.LayerEventType;
0042: import com.vividsolutions.jump.workbench.model.LayerListener;
0043: import com.vividsolutions.jump.workbench.model.LayerManager;
0044: import com.vividsolutions.jump.workbench.model.LayerManagerProxy;
0045: import com.vividsolutions.jump.workbench.model.Layerable;
0046: import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
0047: import com.vividsolutions.jump.workbench.model.Task;
0048: import com.vividsolutions.jump.workbench.model.UndoableEditReceiver;
0049: import com.vividsolutions.jump.workbench.model.WMSLayer;
0050: import com.vividsolutions.jump.workbench.plugin.*;
0051: import com.vividsolutions.jump.workbench.plugin.EnableCheck;
0052: import com.vividsolutions.jump.workbench.plugin.PlugIn;
0053: import com.vividsolutions.jump.workbench.plugin.PlugInContext;
0054: import com.vividsolutions.jump.workbench.plugin.ThreadedPlugIn;
0055: import com.vividsolutions.jump.workbench.ui.plugin.CloneWindowPlugIn;
0056: import com.vividsolutions.jump.workbench.ui.plugin.FeatureInstaller;
0057: import com.vividsolutions.jump.workbench.ui.renderer.style.ChoosableStyle;
0058: import com.vividsolutions.jump.workbench.ui.task.TaskMonitorManager;
0059: import java.awt.BorderLayout;
0060: import java.awt.Color;
0061: import java.awt.Component;
0062: import java.awt.Dialog;
0063: import java.awt.Dimension;
0064: import java.awt.Graphics;
0065: import java.awt.GridBagConstraints;
0066: import java.awt.GridBagLayout;
0067: import java.awt.Insets;
0068: import java.awt.Window;
0069: import java.awt.event.ActionEvent;
0070: import java.awt.event.ActionListener;
0071: import java.awt.event.ComponentEvent;
0072: import java.awt.event.KeyEvent;
0073: import java.awt.event.KeyListener;
0074: import java.awt.event.WindowEvent;
0075: import java.beans.PropertyChangeEvent;
0076: import java.beans.PropertyChangeListener;
0077: import java.beans.PropertyVetoException;
0078: import java.text.DecimalFormat;
0079: import java.util.*;
0080: import java.util.ArrayList;
0081: import java.util.Collection;
0082: import java.util.Collections;
0083: import java.util.Date;
0084: import java.util.HashSet;
0085: import java.util.Iterator;
0086: import java.util.Map;
0087: import java.util.Set;
0088: import javax.swing.*;
0089: import javax.swing.BorderFactory;
0090: import javax.swing.ImageIcon;
0091: import javax.swing.JDesktopPane;
0092: import javax.swing.JFrame;
0093: import javax.swing.JInternalFrame;
0094: import javax.swing.JLabel;
0095: import javax.swing.JMenu;
0096: import javax.swing.JMenuBar;
0097: import javax.swing.JMenuItem;
0098: import javax.swing.JOptionPane;
0099: import javax.swing.JPanel;
0100: import javax.swing.SwingConstants;
0101: import javax.swing.SwingUtilities;
0102: import javax.swing.Timer;
0103: import javax.swing.event.InternalFrameAdapter;
0104: import javax.swing.event.InternalFrameEvent;
0105: import javax.swing.event.InternalFrameListener;
0106: import javax.swing.event.MenuEvent;
0107: import javax.swing.event.PopupMenuEvent;
0108: import javax.swing.event.PopupMenuListener;
0109:
0110: /**
0111: * This class is responsible for the main window of the JUMP application.
0112: */
0113: public class WorkbenchFrame extends JFrame implements
0114: LayerViewPanelContext, ViewportListener {
0115: BorderLayout borderLayout1 = new BorderLayout();
0116: JLabel coordinateLabel = new JLabel();
0117: JMenuBar menuBar = new JMenuBar();
0118: JMenu fileMenu = (JMenu) FeatureInstaller.installMnemonic(
0119: new JMenu(MenuNames.FILE), menuBar);
0120: JMenuItem exitMenuItem = FeatureInstaller
0121: .installMnemonic(new JMenuItem(I18N
0122: .get("ui.WorkbenchFrame.exit")), fileMenu);
0123: GridBagLayout gridBagLayout1 = new GridBagLayout();
0124: JLabel messageLabel = new JLabel();
0125: JPanel statusPanel = new JPanel();
0126: JLabel timeLabel = new JLabel();
0127: //<<TODO:FEATURE>> Before JUMP Workbench closes, prompt the user to save
0128: // any
0129: //unsaved layers [Jon Aquino]
0130: WorkbenchToolBar toolBar;
0131: JMenu windowMenu = (JMenu) FeatureInstaller.installMnemonic(
0132: new JMenu(MenuNames.WINDOW), menuBar);
0133:
0134: private DecimalFormat memoryFormat = new DecimalFormat("###,###");
0135:
0136: private TitledPopupMenu categoryPopupMenu = new TitledPopupMenu() {
0137: {
0138: addPopupMenuListener(new PopupMenuListener() {
0139: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
0140: LayerNamePanel panel = ((LayerNamePanelProxy) getActiveInternalFrame())
0141: .getLayerNamePanel();
0142: setTitle((panel.selectedNodes(Category.class)
0143: .size() != 1) ? ("("
0144: + panel.selectedNodes(Category.class)
0145: .size() + " categories selected)")
0146: : ((Category) panel.selectedNodes(
0147: Category.class).iterator().next())
0148: .getName());
0149: }
0150:
0151: public void popupMenuWillBecomeInvisible(
0152: PopupMenuEvent e) {
0153: }
0154:
0155: public void popupMenuCanceled(PopupMenuEvent e) {
0156: }
0157: });
0158: }
0159: };
0160: private JDesktopPane desktopPane = new JDesktopPane() {
0161: {
0162: // Simple workaround for the following JUMP bug: if you maximize one JInternalFrame, then all
0163: // JInternalFrames get maximized (including attribute windows, undesirably). The workaround is
0164: // to use the DefaultDesktopManager instead of the one installed by the Windows L&F
0165: // (the WindowsDesktopManager). (Uwe Dalluege noticed that the problem occurred with the
0166: // Windows L&F but not the Metal L&F -- this observation led me to the solution).
0167: // [Jon Aquino 2005-07-04]
0168: setDesktopManager(new DefaultDesktopManager());
0169: }
0170: };
0171: //<<TODO:REMOVE>> Actually we're not using the three optimization
0172: // parameters
0173: //below. Remove. [Jon Aquino]
0174: private int envelopeRenderingThreshold = 500;
0175: private HTMLFrame outputFrame = new HTMLFrame(this ) {
0176: public void setTitle(String title) {
0177: //Don't allow the title of the output frame to be changed.
0178: }
0179:
0180: {
0181: super .setTitle(I18N.get("ui.WorkbenchFrame.output"));
0182: }
0183: };
0184: private ImageIcon icon;
0185: private TitledPopupMenu layerNamePopupMenu = new TitledPopupMenu() {
0186: {
0187: addPopupMenuListener(new PopupMenuListener() {
0188: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
0189: LayerNamePanel panel = ((LayerNamePanelProxy) getActiveInternalFrame())
0190: .getLayerNamePanel();
0191: setTitle((panel.selectedNodes(Layer.class).size() != 1) ? ("("
0192: + panel.selectedNodes(Layer.class).size()
0193: + " "
0194: + I18N
0195: .get("ui.WorkbenchFrame.layers-selected") + ")")
0196: : ((Layerable) panel.selectedNodes(
0197: Layer.class).iterator().next())
0198: .getName());
0199: }
0200:
0201: public void popupMenuWillBecomeInvisible(
0202: PopupMenuEvent e) {
0203: }
0204:
0205: public void popupMenuCanceled(PopupMenuEvent e) {
0206: }
0207: });
0208: }
0209: };
0210: private TitledPopupMenu wmsLayerNamePopupMenu = new TitledPopupMenu() {
0211: {
0212: addPopupMenuListener(new PopupMenuListener() {
0213: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
0214: LayerNamePanel panel = ((LayerNamePanelProxy) getActiveInternalFrame())
0215: .getLayerNamePanel();
0216: setTitle((panel.selectedNodes(WMSLayer.class)
0217: .size() != 1) ? ("("
0218: + panel.selectedNodes(WMSLayer.class)
0219: .size()
0220: + " "
0221: + I18N
0222: .get("ui.WorkbenchFrame.wms-layers-selected") + ")")
0223: : ((Layerable) panel.selectedNodes(
0224: WMSLayer.class).iterator().next())
0225: .getName());
0226: }
0227:
0228: public void popupMenuWillBecomeInvisible(
0229: PopupMenuEvent e) {
0230: }
0231:
0232: public void popupMenuCanceled(PopupMenuEvent e) {
0233: }
0234: });
0235: }
0236: };
0237: private LayerNamePanelListener layerNamePanelListener = new LayerNamePanelListener() {
0238: public void layerSelectionChanged() {
0239: toolBar.updateEnabledState();
0240: }
0241: };
0242: //Here is a small patch to JUMP to avoid creating a StringBuffer every
0243: //coordinate change (which could be many thoustands). Replace the innter
0244: //class in WorkbenchFrame.java with the following. I am assuming only one
0245: //thread can call the listener at a time. If that is untrue please
0246: // synchronize
0247: //cursorPositionChanged().
0248: //
0249: //Sheldon Young 2004-01-30
0250: private LayerViewPanelListener layerViewPanelListener = new LayerViewPanelListener() {
0251: // Avoid creating an expensive StringBuffer when the cursor position
0252: // changes.
0253: private StringBuffer positionStatusBuf = new StringBuffer("(");
0254:
0255: public void cursorPositionChanged(String x, String y) {
0256: positionStatusBuf.setLength(1);
0257: positionStatusBuf.append(x).append(", ").append(y).append(
0258: ")");
0259: coordinateLabel.setText(positionStatusBuf.toString());
0260: }
0261:
0262: public void selectionChanged() {
0263: toolBar.updateEnabledState();
0264: }
0265:
0266: public void fenceChanged() {
0267: toolBar.updateEnabledState();
0268: }
0269:
0270: public void painted(Graphics graphics) {
0271: }
0272: };
0273: //<<TODO:NAMING>> This name is not clear [Jon Aquino]
0274: private int maximumFeatureExtentForEnvelopeRenderingInPixels = 10;
0275: //<<TODO:NAMING>> This name is not clear [Jon Aquino]
0276: private int minimumFeatureExtentForAnyRenderingInPixels = 2;
0277: private StringBuffer log = new StringBuffer();
0278: private int taskSequence = 1;
0279: private WorkbenchContext workbenchContext;
0280: private JLabel memoryLabel = new JLabel();
0281: private String lastStatusMessage = "";
0282: private Set choosableStyleClasses = new HashSet();
0283: private JLabel wmsLabel = new JLabel();
0284: private ArrayList easyKeyListeners = new ArrayList();
0285: private Map nodeClassToLayerNamePopupMenuMap = CollectionUtil
0286: .createMap(new Object[] { Layer.class, layerNamePopupMenu,
0287: WMSLayer.class, wmsLayerNamePopupMenu,
0288: Category.class, categoryPopupMenu });
0289: private int positionIndex = -1;
0290: private int primaryInfoFrameIndex = -1;
0291:
0292: public WorkbenchFrame(String title, ImageIcon icon,
0293: final WorkbenchContext workbenchContext) throws Exception {
0294: setTitle(title);
0295: new Timer(1000, new ActionListener() {
0296: public void actionPerformed(ActionEvent e) {
0297: memoryLabel
0298: .setText(getMBCommittedMemory()
0299: + " MB "
0300: + I18N
0301: .get("ui.WorkbenchFrame.committed-memory"));
0302: memoryLabel.setToolTipText(LayerManager
0303: .layerManagerCount()
0304: + " "
0305: + I18N.get("ui.WorkbenchFrame.layer-manager")
0306: + StringUtil
0307: .s(LayerManager.layerManagerCount()));
0308: }
0309: }).start();
0310: this .workbenchContext = workbenchContext;
0311: this .icon = icon;
0312: toolBar = new WorkbenchToolBar(workbenchContext);
0313: toolBar.setTaskMonitorManager(new TaskMonitorManager());
0314: try {
0315: jbInit();
0316: configureStatusLabel(messageLabel, 300);
0317: configureStatusLabel(coordinateLabel, 150);
0318: configureStatusLabel(timeLabel, 200);
0319: configureStatusLabel(wmsLabel, 100);
0320: } catch (Exception e) {
0321: e.printStackTrace();
0322: }
0323: new RecursiveKeyListener(this ) {
0324: public void keyTyped(KeyEvent e) {
0325: for (Iterator i = easyKeyListeners.iterator(); i
0326: .hasNext();) {
0327: KeyListener l = (KeyListener) i.next();
0328: l.keyTyped(e);
0329: }
0330: }
0331:
0332: public void keyPressed(KeyEvent e) {
0333: for (Iterator i = new ArrayList(easyKeyListeners)
0334: .iterator(); i.hasNext();) {
0335: KeyListener l = (KeyListener) i.next();
0336: l.keyPressed(e);
0337: }
0338: }
0339:
0340: public void keyReleased(KeyEvent e) {
0341: for (Iterator i = new ArrayList(easyKeyListeners)
0342: .iterator(); i.hasNext();) {
0343: KeyListener l = (KeyListener) i.next();
0344: l.keyReleased(e);
0345: }
0346: }
0347: };
0348: installKeyboardShortcutListener();
0349: }
0350:
0351: /**
0352: * Unlike #add(KeyListener), listeners registered using this method are
0353: * notified when KeyEvents occur on this frame's child components. Note:
0354: * Bug: KeyListeners registered using this method may receive events
0355: * multiple times.
0356: *
0357: * @see #addKeyboardShortcut
0358: */
0359: public void addEasyKeyListener(KeyListener l) {
0360: easyKeyListeners.add(l);
0361: }
0362:
0363: public void removeEasyKeyListener(KeyListener l) {
0364: easyKeyListeners.remove(l);
0365: }
0366:
0367: public String getMBCommittedMemory() {
0368: long totalMemory = Runtime.getRuntime().totalMemory();
0369: long freeMemory = Runtime.getRuntime().freeMemory();
0370: long usedMemory = totalMemory - freeMemory;
0371: double usedMemoryInMB = usedMemory / (1024 * 1024d);
0372: String memoryStr = memoryFormat.format(usedMemoryInMB);
0373: return memoryStr;
0374: }
0375:
0376: /**
0377: * @param newEnvelopeRenderingThreshold
0378: * the number of on-screen features above which envelope
0379: * rendering should occur
0380: */
0381: public void setEnvelopeRenderingThreshold(
0382: int newEnvelopeRenderingThreshold) {
0383: envelopeRenderingThreshold = newEnvelopeRenderingThreshold;
0384: }
0385:
0386: public void setMaximumFeatureExtentForEnvelopeRenderingInPixels(
0387: int newMaximumFeatureExtentForEnvelopeRenderingInPixels) {
0388: maximumFeatureExtentForEnvelopeRenderingInPixels = newMaximumFeatureExtentForEnvelopeRenderingInPixels;
0389: }
0390:
0391: public void log(String message) {
0392: log.append(new Date() + " " + message
0393: + System.getProperty("line.separator"));
0394: }
0395:
0396: public String getLog() {
0397: return log.toString();
0398: }
0399:
0400: public void setMinimumFeatureExtentForAnyRenderingInPixels(
0401: int newMinimumFeatureExtentForAnyRenderingInPixels) {
0402: minimumFeatureExtentForAnyRenderingInPixels = newMinimumFeatureExtentForAnyRenderingInPixels;
0403: }
0404:
0405: public void displayLastStatusMessage() {
0406: setStatusMessage(lastStatusMessage);
0407: }
0408:
0409: public void setStatusMessage(String message) {
0410: lastStatusMessage = message;
0411: setStatusBarText(message);
0412: setStatusBarTextHighlighted(false, null);
0413: }
0414:
0415: private void setStatusBarText(String message) {
0416: //<<TODO:IMPROVE>> Treat null messages like "" [Jon Aquino]
0417: messageLabel.setText((message == "") ? " " : message);
0418: messageLabel.setToolTipText(message);
0419: //Make message at least a space so that status bar won't collapse [Jon
0420: // Aquino]
0421: }
0422:
0423: /**
0424: * To highlight a message, call #warnUser.
0425: */
0426: private void setStatusBarTextHighlighted(boolean highlighted,
0427: Color color) {
0428: //Use #coordinateLabel rather than (unattached) dummy label because
0429: //dummy label's background does not change when L&F changes. [Jon
0430: // Aquino]
0431: messageLabel.setForeground(highlighted ? Color.black
0432: : coordinateLabel.getForeground());
0433: messageLabel.setBackground(highlighted ? color
0434: : coordinateLabel.getBackground());
0435: }
0436:
0437: public void setTimeMessage(String message) {
0438: //<<TODO:IMPROVE>> Treat null messages like "" [Jon Aquino]
0439: timeLabel.setText((message == "") ? " " : message);
0440: //Make message at least a space so that status bar won't collapse [Jon
0441: // Aquino]
0442: }
0443:
0444: public JInternalFrame getActiveInternalFrame() {
0445: return desktopPane.getSelectedFrame();
0446: }
0447:
0448: public JInternalFrame[] getInternalFrames() {
0449: return desktopPane.getAllFrames();
0450: }
0451:
0452: public TitledPopupMenu getCategoryPopupMenu() {
0453: return categoryPopupMenu;
0454: }
0455:
0456: public WorkbenchContext getContext() {
0457: return workbenchContext;
0458: }
0459:
0460: public JDesktopPane getDesktopPane() {
0461: return desktopPane;
0462: }
0463:
0464: public int getEnvelopeRenderingThreshold() {
0465: return envelopeRenderingThreshold;
0466: }
0467:
0468: public TitledPopupMenu getLayerNamePopupMenu() {
0469: return layerNamePopupMenu;
0470: }
0471:
0472: public TitledPopupMenu getWMSLayerNamePopupMenu() {
0473: return wmsLayerNamePopupMenu;
0474: }
0475:
0476: public LayerViewPanelListener getLayerViewPanelListener() {
0477: return layerViewPanelListener;
0478: }
0479:
0480: public Map getNodeClassToPopupMenuMap() {
0481: return nodeClassToLayerNamePopupMenuMap;
0482: }
0483:
0484: public LayerNamePanelListener getLayerNamePanelListener() {
0485: return layerNamePanelListener;
0486: }
0487:
0488: public int getMaximumFeatureExtentForEnvelopeRenderingInPixels() {
0489: return maximumFeatureExtentForEnvelopeRenderingInPixels;
0490: }
0491:
0492: public int getMinimumFeatureExtentForAnyRenderingInPixels() {
0493: return minimumFeatureExtentForAnyRenderingInPixels;
0494: }
0495:
0496: public HTMLFrame getOutputFrame() {
0497: return outputFrame;
0498: }
0499:
0500: public WorkbenchToolBar getToolBar() {
0501: return toolBar;
0502: }
0503:
0504: public void activateFrame(JInternalFrame frame) {
0505: try {
0506: if (frame.isIcon()) {
0507: frame.setIcon(false);
0508: }
0509: frame.moveToFront();
0510: frame.requestFocus();
0511: frame.setSelected(true);
0512: if (!(frame instanceof TaskFrame)) {
0513: frame.setMaximum(false);
0514: }
0515: } catch (PropertyVetoException e) {
0516: warnUser(StringUtil.stackTrace(e));
0517: }
0518: }
0519:
0520: /**
0521: * If internalFrame is a LayerManagerProxy, the close behaviour will be
0522: * altered so that the user is prompted if it is the last window on the
0523: * LayerManager.
0524: */
0525: public void addInternalFrame(final JInternalFrame internalFrame) {
0526: addInternalFrame(internalFrame, false, true);
0527: }
0528:
0529: public void addInternalFrame(final JInternalFrame internalFrame,
0530: boolean alwaysOnTop, boolean autoUpdateToolBar) {
0531: if (internalFrame instanceof LayerManagerProxy) {
0532: setClosingBehaviour((LayerManagerProxy) internalFrame);
0533: installTitleBarModifiedIndicator((LayerManagerProxy) internalFrame);
0534: }
0535: //<<TODO:IMPROVE>> Listen for when the frame closes, and when it does,
0536: //activate the topmost frame. Because Swing does not seem to do this
0537: //automatically. [Jon Aquino]
0538: internalFrame.setFrameIcon(icon);
0539: //Call JInternalFrame#setVisible before JDesktopPane#add; otherwise,
0540: // the
0541: //TreeLayerNamePanel starts too narrow (100 pixels or so) for some
0542: // reason.
0543: //<<TODO>>Investigate. [Jon Aquino]
0544: internalFrame.setVisible(true);
0545: desktopPane.add(internalFrame,
0546: alwaysOnTop ? JLayeredPane.PALETTE_LAYER
0547: : JLayeredPane.DEFAULT_LAYER);
0548: if (autoUpdateToolBar) {
0549: internalFrame
0550: .addInternalFrameListener(new InternalFrameListener() {
0551: public void internalFrameActivated(
0552: InternalFrameEvent e) {
0553: toolBar.updateEnabledState();
0554: //Associate current cursortool with the new frame [Jon
0555: // Aquino]
0556: toolBar.reClickSelectedCursorToolButton();
0557: }
0558:
0559: public void internalFrameClosed(
0560: InternalFrameEvent e) {
0561: toolBar.updateEnabledState();
0562: }
0563:
0564: public void internalFrameClosing(
0565: InternalFrameEvent e) {
0566: toolBar.updateEnabledState();
0567: }
0568:
0569: public void internalFrameDeactivated(
0570: InternalFrameEvent e) {
0571: toolBar.updateEnabledState();
0572: }
0573:
0574: public void internalFrameDeiconified(
0575: InternalFrameEvent e) {
0576: toolBar.updateEnabledState();
0577: }
0578:
0579: public void internalFrameIconified(
0580: InternalFrameEvent e) {
0581: toolBar.updateEnabledState();
0582: }
0583:
0584: public void internalFrameOpened(
0585: InternalFrameEvent e) {
0586: toolBar.updateEnabledState();
0587: }
0588: });
0589: //Call #activateFrame *after* adding the listener. [Jon Aquino]
0590: activateFrame(internalFrame);
0591: position(internalFrame);
0592: }
0593: }
0594:
0595: private void installTitleBarModifiedIndicator(
0596: final LayerManagerProxy internalFrame) {
0597: final JInternalFrame i = (JInternalFrame) internalFrame;
0598: new Block() {
0599: //Putting updatingTitle in a Block is better than making it an
0600: //instance variable, because this way there is one updatingTitle
0601: // for each
0602: //internal frame, rather than one for all internal frames. [Jon
0603: // Aquino]
0604: private boolean updatingTitle = false;
0605:
0606: private void updateTitle() {
0607: if (updatingTitle) {
0608: return;
0609: }
0610: updatingTitle = true;
0611: try {
0612: String newTitle = i.getTitle();
0613: if (newTitle.charAt(0) == '*') {
0614: newTitle = newTitle.substring(1);
0615: }
0616: if (!internalFrame.getLayerManager()
0617: .getLayersWithModifiedFeatureCollections()
0618: .isEmpty()) {
0619: newTitle = '*' + newTitle;
0620: }
0621: i.setTitle(newTitle);
0622: } finally {
0623: updatingTitle = false;
0624: }
0625: }
0626:
0627: public Object yield() {
0628: internalFrame.getLayerManager().addLayerListener(
0629: new LayerListener() {
0630: public void layerChanged(LayerEvent e) {
0631: if ((e.getType() == LayerEventType.METADATA_CHANGED)
0632: || (e.getType() == LayerEventType.REMOVED)) {
0633: updateTitle();
0634: }
0635: }
0636:
0637: public void categoryChanged(CategoryEvent e) {
0638: }
0639:
0640: public void featuresChanged(FeatureEvent e) {
0641: }
0642: });
0643: i.addPropertyChangeListener(
0644: JInternalFrame.TITLE_PROPERTY,
0645: new PropertyChangeListener() {
0646: public void propertyChange(
0647: PropertyChangeEvent e) {
0648: updateTitle();
0649: }
0650: });
0651: return null;
0652: }
0653: }.yield();
0654: }
0655:
0656: private void setClosingBehaviour(final LayerManagerProxy proxy) {
0657: final JInternalFrame internalFrame = (JInternalFrame) proxy;
0658: internalFrame
0659: .setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
0660: internalFrame
0661: .addInternalFrameListener(new InternalFrameAdapter() {
0662: public void internalFrameClosing(
0663: InternalFrameEvent e) {
0664: internalFrameCloseHandler.close(internalFrame);
0665: }
0666: });
0667: }
0668:
0669: private Collection getInternalFramesAssociatedWith(
0670: LayerManager layerManager) {
0671: ArrayList internalFramesAssociatedWithLayerManager = new ArrayList();
0672: JInternalFrame[] internalFrames = getInternalFrames();
0673: for (int i = 0; i < internalFrames.length; i++) {
0674: if (internalFrames[i] instanceof LayerManagerProxy
0675: && (((LayerManagerProxy) internalFrames[i])
0676: .getLayerManager() == layerManager)) {
0677: internalFramesAssociatedWithLayerManager
0678: .add(internalFrames[i]);
0679: }
0680: }
0681: return internalFramesAssociatedWithLayerManager;
0682: }
0683:
0684: // added by [mmichaud 2007-06-03]
0685: // Return TaskFrame s using the same layerManager
0686: private Collection getTaskFramesAssociatedWith(
0687: LayerManager layerManager) {
0688: ArrayList taskFramesAssociatedWithLayerManager = new ArrayList();
0689: JInternalFrame[] internalFrames = getInternalFrames();
0690: for (int i = 0; i < internalFrames.length; i++) {
0691: if (internalFrames[i] instanceof TaskFrame
0692: && (((TaskFrame) internalFrames[i])
0693: .getLayerManager() == layerManager)) {
0694: taskFramesAssociatedWithLayerManager
0695: .add(internalFrames[i]);
0696: }
0697: }
0698: return taskFramesAssociatedWithLayerManager;
0699: }
0700:
0701: // added by [mmichaud 2007-06-03]
0702: // Return every InternalFrame associated with taskFrame (taskFrame is excluded)
0703: private Collection getInternalFramesAssociatedWith(
0704: TaskFrame taskFrame) {
0705: ArrayList internalFramesAssociatedWithTaskFrame = new ArrayList();
0706: JInternalFrame[] internalFrames = getInternalFrames();
0707: for (int i = 0; i < internalFrames.length; i++) {
0708: if (internalFrames[i] instanceof TaskFrameProxy
0709: && (((TaskFrameProxy) internalFrames[i])
0710: .getTaskFrame() == taskFrame)
0711: && internalFrames[i] != taskFrame) {
0712: internalFramesAssociatedWithTaskFrame
0713: .add(internalFrames[i]);
0714: }
0715: }
0716: return internalFramesAssociatedWithTaskFrame;
0717: }
0718:
0719: public TaskFrame addTaskFrame() {
0720: TaskFrame f = addTaskFrame(createTask());
0721: return f;
0722: }
0723:
0724: public Task createTask() {
0725: Task task = new Task();
0726: //LayerManager shouldn't automatically add categories in its
0727: // constructor.
0728: //Sometimes we want to create a LayerManager with no categories
0729: //(e.g. in OpenProjectPlugIn). [Jon Aquino]
0730: task.getLayerManager().addCategory(
0731: StandardCategoryNames.WORKING);
0732: task.getLayerManager()
0733: .addCategory(StandardCategoryNames.SYSTEM);
0734: task.setName(I18N.get("ui.WorkbenchFrame.task") + " "
0735: + taskSequence++);
0736: return task;
0737: }
0738:
0739: public TaskFrame addTaskFrame(Task task) {
0740: return addTaskFrame(new TaskFrame(task, workbenchContext));
0741: }
0742:
0743: public TaskFrame addTaskFrame(TaskFrame taskFrame) {
0744: taskFrame.getTask().getLayerManager().addLayerListener(
0745: new LayerListener() {
0746: public void featuresChanged(FeatureEvent e) {
0747: }
0748:
0749: public void categoryChanged(CategoryEvent e) {
0750: toolBar.updateEnabledState();
0751: }
0752:
0753: public void layerChanged(LayerEvent layerEvent) {
0754: toolBar.updateEnabledState();
0755: }
0756: });
0757: addInternalFrame(taskFrame);
0758: taskFrame.getLayerViewPanel().getLayerManager()
0759: .getUndoableEditReceiver().add(
0760: new UndoableEditReceiver.Listener() {
0761: public void undoHistoryChanged() {
0762: toolBar.updateEnabledState();
0763: }
0764:
0765: public void undoHistoryTruncated() {
0766: toolBar.updateEnabledState();
0767: log(I18N
0768: .get("ui.WorkbenchFrame.undo-history-was-truncated"));
0769: }
0770: });
0771: return taskFrame;
0772: }
0773:
0774: public void flash(final HTMLFrame frame) {
0775: final Color originalColor = frame.getBackgroundColor();
0776: new Timer(100, new ActionListener() {
0777: private int tickCount = 0;
0778:
0779: public void actionPerformed(ActionEvent e) {
0780: try {
0781: tickCount++;
0782: frame
0783: .setBackgroundColor(((tickCount % 2) == 0) ? originalColor
0784: : Color.yellow);
0785: if (tickCount == 2) {
0786: Timer timer = (Timer) e.getSource();
0787: timer.stop();
0788: }
0789: } catch (Throwable t) {
0790: handleThrowable(t);
0791: }
0792: }
0793: }).start();
0794: }
0795:
0796: private void flashStatusMessage(final String message,
0797: final Color color) {
0798: new Timer(100, new ActionListener() {
0799: private int tickCount = 0;
0800:
0801: public void actionPerformed(ActionEvent e) {
0802: tickCount++;
0803: //This message is important, so overwrite whatever is on the
0804: // status bar. [Jon Aquino]
0805: setStatusBarText(message);
0806: setStatusBarTextHighlighted((tickCount % 2) == 0, color);
0807: if (tickCount == 4) {
0808: Timer timer = (Timer) e.getSource();
0809: timer.stop();
0810: }
0811: }
0812: }).start();
0813: }
0814:
0815: /**
0816: * Can be called regardless of whether the current thread is the AWT event
0817: * dispatch thread.
0818: *
0819: * @param t
0820: * Description of the Parameter
0821: */
0822: public void handleThrowable(final Throwable t) {
0823: log(StringUtil.stackTrace(t));
0824: Component parent = this ;
0825: Window[] ownedWindows = getOwnedWindows();
0826: for (int i = 0; i < ownedWindows.length; i++) {
0827: if (ownedWindows[i] instanceof Dialog
0828: && ownedWindows[i].isVisible()
0829: && ((Dialog) ownedWindows[i]).isModal()) {
0830: parent = ownedWindows[i];
0831: break;
0832: }
0833: }
0834: handleThrowable(t, parent);
0835: }
0836:
0837: public static void handleThrowable(final Throwable t,
0838: final Component parent) {
0839: t.printStackTrace(System.err);
0840: SwingUtilities.invokeLater(new Runnable() {
0841: public void run() {
0842: ErrorDialog.show(parent, StringUtil.toFriendlyName(t
0843: .getClass().getName()), toMessage(t),
0844: StringUtil.stackTrace(t));
0845: }
0846: });
0847: }
0848:
0849: private ArrayList lastFiveThrowableDates = new ArrayList() {
0850: public boolean add(Object o) {
0851: if (size() == 5) {
0852: remove(0);
0853: }
0854: return super .add(o);
0855: }
0856: };
0857:
0858: public static String toMessage(Throwable t) {
0859: String message;
0860: if (t.getLocalizedMessage() == null) {
0861: message = I18N
0862: .get("ui.WorkbenchFrame.no-description-was-provided");
0863: } else if (t.getLocalizedMessage().toLowerCase().indexOf(
0864: I18N.get("ui.WorkbenchFrame.side-location-conflict")) > -1) {
0865: message = t.getLocalizedMessage()
0866: + " -- "
0867: + I18N
0868: .get("ui.WorkbenchFrame.check-for-invalid-geometries");
0869: } else {
0870: message = t.getLocalizedMessage();
0871: }
0872: return message + " ("
0873: + StringUtil.toFriendlyName(t.getClass().getName())
0874: + ")";
0875: }
0876:
0877: public boolean hasInternalFrame(JInternalFrame internalFrame) {
0878: JInternalFrame[] frames = desktopPane.getAllFrames();
0879: for (int i = 0; i < frames.length; i++) {
0880: if (frames[i] == internalFrame) {
0881: return true;
0882: }
0883: }
0884: return false;
0885: }
0886:
0887: public void removeInternalFrame(JInternalFrame internalFrame) {
0888: //Looks like #closeFrame is the proper way to remove an internal
0889: // frame.
0890: //It will activate the next frame. [Jon Aquino]
0891: desktopPane.getDesktopManager().closeFrame(internalFrame);
0892: }
0893:
0894: public void warnUser(String warning) {
0895: log(I18N.get("ui.WorkbenchFrame.warning") + ": " + warning);
0896: flashStatusMessage(warning, Color.yellow);
0897: }
0898:
0899: public void zoomChanged(Envelope modelEnvelope) {
0900: toolBar.updateEnabledState();
0901: }
0902:
0903: void exitMenuItem_actionPerformed(ActionEvent e) {
0904: closeApplication();
0905: }
0906:
0907: void this _componentShown(ComponentEvent e) {
0908: try {
0909: //If the first internal frame is not a TaskWindow (as may be the
0910: // case in
0911: //custom workbenches), #updateEnabledState() will ensure that the
0912: //cursor-tool buttons are disabled. [Jon Aquino]
0913: toolBar.updateEnabledState();
0914: } catch (Throwable t) {
0915: handleThrowable(t);
0916: }
0917: }
0918:
0919: void this _windowClosing(WindowEvent e) {
0920: closeApplication();
0921: }
0922:
0923: void windowMenu_menuSelected(MenuEvent e) {
0924: //<<TODO:MAINTAINABILITY>> This algorithm is not robust. It assumes
0925: // the Window
0926: //menu has exactly one "regular" menu item (newWindowMenuItem). [Jon
0927: // Aquino]
0928: int addedMenuItems = 3; //[sstein: new menus: options + log + separator]
0929: int allItems = windowMenu.getItemCount();
0930: String t = windowMenu.getItem(addedMenuItems).getText();
0931: String name = AbstractPlugIn
0932: .createName(CloneWindowPlugIn.class);
0933: boolean isWindowCloneClass = t.equals(name);
0934: if (windowMenu.getItemCount() > addedMenuItems
0935: && windowMenu.getItem(addedMenuItems) != null
0936: && isWindowCloneClass) {
0937: JMenuItem newWindowMenuItem = windowMenu
0938: .getItem(addedMenuItems);
0939: //windowMenu.removeAll();
0940: int loopsdone = 0;
0941: for (int itemIdx = addedMenuItems; itemIdx < allItems; itemIdx++) {
0942: //-- subtract since number of window-menu-items shrinks
0943: try {
0944: //String tt = windowMenu.getItem(itemIdx-loopsdone).getText();
0945: //-- [sstein:] an exception for the line above is thrown if the item is a separator
0946: // then, getText seems not to work
0947: windowMenu.remove(itemIdx - loopsdone);
0948: loopsdone = loopsdone + 1;
0949: } catch (Exception exc) {
0950: //eat -- should be thrown caused by a separator
0951: System.out
0952: .println("WorkbenchFrame: exception in windowMenu_Selected() ");
0953: }
0954: }
0955: windowMenu.add(newWindowMenuItem);
0956: windowMenu.addSeparator();
0957: } else {
0958: //ezLink doesn't have a Clone Window menu [Jon Aquino]
0959: windowMenu.removeAll();
0960: }
0961: final JInternalFrame[] frames = desktopPane.getAllFrames();
0962: for (int i = 0; i < frames.length; i++) {
0963: JMenuItem menuItem = new JMenuItem();
0964: //Increase truncation threshold from 20 to 40, for eziLink [Jon
0965: // Aquino]
0966: menuItem.setText(GUIUtil.truncateString(frames[i]
0967: .getTitle(), 40));
0968: associate(menuItem, frames[i]);
0969: windowMenu.add(menuItem);
0970: }
0971: if (windowMenu.getItemCount() == addedMenuItems) {
0972: //For ezLink [Jon Aquino]
0973: windowMenu.add(new JMenuItem("("
0974: + I18N.get("ui.WorkbenchFrame.no-windows") + ")"));
0975: }
0976: }
0977:
0978: private void associate(JMenuItem menuItem,
0979: final JInternalFrame frame) {
0980: menuItem.addActionListener(new ActionListener() {
0981: public void actionPerformed(ActionEvent e) {
0982: try {
0983: activateFrame(frame);
0984: } catch (Throwable t) {
0985: handleThrowable(t);
0986: }
0987: }
0988: });
0989: }
0990:
0991: private void closeApplication() {
0992: applicationExitHandler.exitApplication(this );
0993: }
0994:
0995: private Collection getLayersWithModifiedFeatureCollections() {
0996: ArrayList layersWithModifiedFeatureCollections = new ArrayList();
0997: for (Iterator i = getLayerManagers().iterator(); i.hasNext();) {
0998: LayerManager layerManager = (LayerManager) i.next();
0999: layersWithModifiedFeatureCollections.addAll(layerManager
1000: .getLayersWithModifiedFeatureCollections());
1001: }
1002: return layersWithModifiedFeatureCollections;
1003: }
1004:
1005: private Collection getLayerManagers() {
1006: //Multiple windows may point to the same LayerManager, so use
1007: //a Set. [Jon Aquino]
1008: HashSet layerManagers = new HashSet();
1009: JInternalFrame[] internalFrames = getInternalFrames();
1010: for (int i = 0; i < internalFrames.length; i++) {
1011: if (internalFrames[i] instanceof LayerManagerProxy) {
1012: layerManagers
1013: .add(((LayerManagerProxy) internalFrames[i])
1014: .getLayerManager());
1015: }
1016: }
1017: return layerManagers;
1018: }
1019:
1020: private void configureStatusLabel(JLabel label, int width) {
1021: label.setMinimumSize(new Dimension(width, (int) label
1022: .getMinimumSize().getHeight()));
1023: label.setMaximumSize(new Dimension(width, (int) label
1024: .getMaximumSize().getHeight()));
1025: label.setPreferredSize(new Dimension(width, (int) label
1026: .getPreferredSize().getHeight()));
1027: }
1028:
1029: private void jbInit() throws Exception {
1030: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
1031: this .setIconImage(icon.getImage());
1032: this
1033: .addComponentListener(new java.awt.event.ComponentAdapter() {
1034: public void componentShown(ComponentEvent e) {
1035: this _componentShown(e);
1036: }
1037: });
1038: this .getContentPane().setLayout(borderLayout1);
1039: this .addWindowListener(new java.awt.event.WindowAdapter() {
1040: public void windowClosing(WindowEvent e) {
1041: this _windowClosing(e);
1042: }
1043: });
1044: this .setJMenuBar(menuBar);
1045: //This size is chosen so that when the user hits the Info tool, the
1046: // window
1047: //fits between the lower edge of the TaskFrame and the lower edge of
1048: // the
1049: //WorkbenchFrame. See the call to #setSize in InfoFrame. [Jon Aquino]
1050: setSize(900, 665);
1051: //OUTLINE_DRAG_MODE is excruciatingly slow in JDK 1.4.1, so don't use
1052: // it.
1053: //(although it's supposed to be fixed in 1.4.2, which has not yet been
1054: //released). (see Sun Java Bug ID 4665237). [Jon Aquino]
1055: //desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
1056: messageLabel.setOpaque(true);
1057: memoryLabel.setText("jLabel1");
1058: wmsLabel.setHorizontalAlignment(SwingConstants.LEFT);
1059: wmsLabel.setText(" ");
1060: this .getContentPane().add(statusPanel, BorderLayout.SOUTH);
1061: exitMenuItem
1062: .addActionListener(new java.awt.event.ActionListener() {
1063: public void actionPerformed(ActionEvent e) {
1064: exitMenuItem_actionPerformed(e);
1065: }
1066: });
1067: windowMenu
1068: .addMenuListener(new javax.swing.event.MenuListener() {
1069: public void menuCanceled(MenuEvent e) {
1070: }
1071:
1072: public void menuDeselected(MenuEvent e) {
1073: }
1074:
1075: public void menuSelected(MenuEvent e) {
1076: windowMenu_menuSelected(e);
1077: }
1078: });
1079: coordinateLabel.setBorder(BorderFactory
1080: .createLoweredBevelBorder());
1081: wmsLabel.setBorder(BorderFactory.createLoweredBevelBorder());
1082: coordinateLabel.setText(" ");
1083: statusPanel.setLayout(gridBagLayout1);
1084: statusPanel.setBorder(BorderFactory.createRaisedBevelBorder());
1085: messageLabel
1086: .setBorder(BorderFactory.createLoweredBevelBorder());
1087: messageLabel.setText(" ");
1088: timeLabel.setBorder(BorderFactory.createLoweredBevelBorder());
1089: timeLabel.setText(" ");
1090: memoryLabel.setBorder(BorderFactory.createLoweredBevelBorder());
1091: memoryLabel.setText(" ");
1092: menuBar.add(fileMenu);
1093: menuBar.add(windowMenu);
1094: getContentPane().add(toolBar, BorderLayout.NORTH);
1095: getContentPane().add(desktopPane, BorderLayout.CENTER);
1096: fileMenu.addSeparator();
1097: fileMenu.add(exitMenuItem);
1098: statusPanel.add(coordinateLabel, new GridBagConstraints(5, 1,
1099: 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
1100: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
1101: 0, 0));
1102: statusPanel.add(timeLabel, new GridBagConstraints(2, 1, 1, 1,
1103: 0.0, 0.0, GridBagConstraints.CENTER,
1104: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
1105: 0, 0));
1106: statusPanel.add(messageLabel, new GridBagConstraints(1, 1, 1,
1107: 1, 0.0, 0.0, GridBagConstraints.CENTER,
1108: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
1109: 0, 0));
1110: //Give memoryLabel the 1.0 weight. All the rest should have their
1111: // sizes
1112: //configured using #configureStatusLabel. [Jon Aquino]
1113: statusPanel.add(memoryLabel, new GridBagConstraints(3, 1, 1, 1,
1114: 1.0, 0.0, GridBagConstraints.CENTER,
1115: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
1116: 0, 0));
1117: statusPanel.add(wmsLabel, new GridBagConstraints(4, 1, 1, 1,
1118: 0.0, 0.0, GridBagConstraints.WEST,
1119: GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
1120: }
1121:
1122: private void position(JInternalFrame internalFrame) {
1123: final int STEP = 5;
1124: GUIUtil.Location location = null;
1125: if (internalFrame instanceof PrimaryInfoFrame) {
1126: primaryInfoFrameIndex++;
1127: int offset = (primaryInfoFrameIndex % 3) * STEP;
1128: location = new GUIUtil.Location(offset, true, offset, true);
1129: } else {
1130: positionIndex++;
1131: int offset = (positionIndex % 5) * STEP;
1132: location = new GUIUtil.Location(offset, false, offset,
1133: false);
1134: }
1135: GUIUtil.setLocation(internalFrame, location, desktopPane);
1136: }
1137:
1138: /**
1139: * Fundamental Style classes (like BasicStyle, VertexStyle, and LabelStyle)
1140: * cannot be removed, and are thus excluded from the choosable Style
1141: * classes.
1142: */
1143: public Set getChoosableStyleClasses() {
1144: return Collections.unmodifiableSet(choosableStyleClasses);
1145: }
1146:
1147: public void addChoosableStyleClass(Class choosableStyleClass) {
1148: Assert.isTrue(ChoosableStyle.class
1149: .isAssignableFrom(choosableStyleClass));
1150: choosableStyleClasses.add(choosableStyleClass);
1151: }
1152:
1153: private HashMap keyCodeAndModifiersToPlugInAndEnableCheckMap = new HashMap();
1154:
1155: /**
1156: * Adds a keyboard shortcut for a plugin. logs plugin exceptions.
1157: *
1158: * note - attaching to keyCode 'a', modifiers =1 will detect shift-A
1159: * events. It will *not* detect caps-lock-'a'. This is due to
1160: * inconsistencies in java.awt.event.KeyEvent. In the unlikely event you
1161: * actually do want to also also attach to caps-lock-'a', then make two
1162: * shortcuts - one to keyCode 'a' and modifiers =1 (shift-A) and one to
1163: * keyCode 'A' and modifiers=0 (caps-lock A).
1164: *
1165: * For more details, see the java.awt.event.KeyEvent class - it has a full
1166: * explaination.
1167: *
1168: * @param keyCode
1169: * What key to attach to (See java.awt.event.KeyEvent)
1170: * @param modifiers 0=
1171: * none, 1=shift, 2= cntrl, 8=alt, 3=shift+cntrl, etc... See the
1172: * modifier mask constants in the Event class
1173: * @param plugIn
1174: * What plugin to execute
1175: * @param enableCheck
1176: * Is the key enabled at the moment?
1177: */
1178: public void addKeyboardShortcut(final int keyCode,
1179: final int modifiers, final PlugIn plugIn,
1180: final EnableCheck enableCheck) {
1181: //Overwrite existing shortcut [Jon Aquino]
1182: keyCodeAndModifiersToPlugInAndEnableCheckMap.put(keyCode + ":"
1183: + modifiers, new Object[] { plugIn, enableCheck });
1184: }
1185:
1186: private void installKeyboardShortcutListener() {
1187: addEasyKeyListener(new KeyListener() {
1188: public void keyTyped(KeyEvent e) {
1189: }
1190:
1191: public void keyReleased(KeyEvent e) {
1192: }
1193:
1194: public void keyPressed(KeyEvent e) {
1195: Object[] plugInAndEnableCheck = (Object[]) keyCodeAndModifiersToPlugInAndEnableCheckMap
1196: .get(e.getKeyCode() + ":" + e.getModifiers());
1197: if (plugInAndEnableCheck == null) {
1198: return;
1199: }
1200: PlugIn plugIn = (PlugIn) plugInAndEnableCheck[0];
1201: EnableCheck enableCheck = (EnableCheck) plugInAndEnableCheck[1];
1202: if (enableCheck != null
1203: && enableCheck.check(null) != null) {
1204: return;
1205: }
1206: //#toActionListener handles checking if the plugIn is a
1207: // ThreadedPlugIn,
1208: //and making calls to UndoableEditReceiver if necessary. [Jon
1209: // Aquino 10/15/2003]
1210: AbstractPlugIn.toActionListener(plugIn,
1211: workbenchContext, new TaskMonitorManager())
1212: .actionPerformed(null);
1213: }
1214: });
1215: }
1216:
1217: //==========================================================================
1218: // Applications (such as EziLink) want to override the default JUMP
1219: // frame closing behaviour and application exit behaviour with their own
1220: // behaviours.
1221: //
1222: InternalFrameCloseHandler internalFrameCloseHandler = new DefaultInternalFrameCloser();
1223: ApplicationExitHandler applicationExitHandler = new DefaultApplicationExitHandler();
1224:
1225: public InternalFrameCloseHandler getInternalFrameCloseHandler() {
1226: return internalFrameCloseHandler;
1227: }
1228:
1229: public void setInternalFrameCloseHandler(
1230: InternalFrameCloseHandler value) {
1231: internalFrameCloseHandler = value;
1232: }
1233:
1234: public ApplicationExitHandler getApplicationExitHandler() {
1235: return applicationExitHandler;
1236: }
1237:
1238: public void setApplicationExitHandler(ApplicationExitHandler value) {
1239: applicationExitHandler = value;
1240: }
1241:
1242: private class DefaultInternalFrameCloser implements
1243: InternalFrameCloseHandler {
1244: public void close(JInternalFrame internalFrame) {
1245: if (internalFrame instanceof TaskFrame) {
1246: closeTaskFrame((TaskFrame) internalFrame);
1247: } else {
1248: GUIUtil.dispose(internalFrame, desktopPane);
1249: }
1250: SwingUtilities.invokeLater(new Runnable() {
1251: public void run() {
1252: System.runFinalization();
1253: System.gc();
1254: }
1255: });
1256: }
1257: }
1258:
1259: private class DefaultApplicationExitHandler implements
1260: ApplicationExitHandler {
1261: public void exitApplication(JFrame mainFrame) {
1262: if (confirmClose(I18N.get("ui.WorkbenchFrame.exit-jump"),
1263: getLayersWithModifiedFeatureCollections())) {
1264: //PersistentBlackboardPlugIn listens for when the workbench is
1265: // hidden [Jon Aquino]
1266: setVisible(false);
1267: //Invoke System#exit after all pending GUI events have been
1268: // fired
1269: //(e.g. the hiding of this WorkbenchFrame) [Jon Aquino]
1270: SwingUtilities.invokeLater(new Runnable() {
1271: public void run() {
1272: System.exit(0);
1273: }
1274: });
1275: }
1276: }
1277: }
1278:
1279: // Method completed by [mmichaud 2007-06-03] to close properly
1280: // internal frames depending on a TaskFrame.
1281: // Maybe this method should take place in TaskFrame instead...
1282: private void closeTaskFrame(TaskFrame taskFrame) {
1283: LayerManager layerManager = taskFrame.getLayerManager();
1284: Collection associatedFrames = getInternalFramesAssociatedWith(taskFrame);
1285: boolean lastTaskFrame = getTaskFramesAssociatedWith(
1286: layerManager).size() == 1;
1287: if (lastTaskFrame) {
1288: Collection modifiedItems = layerManager
1289: .getLayersWithModifiedFeatureCollections();
1290: if (confirmClose(I18N.get("ui.WorkbenchFrame.close-task"),
1291: modifiedItems)) {
1292: // There are other internal frames associated with this task
1293: if (associatedFrames.size() != 0) {
1294: // Confirm you want to close them first
1295: if (confirmClose(
1296: StringUtil
1297: .split(
1298: I18N
1299: .get("ui.WorkbenchFrame.other-internal-frames-depend-on-this-task-frame")
1300: + " "
1301: + I18N
1302: .get("ui.WorkbenchFrame.do-you-want-to-close-them-also"),
1303: 60), I18N
1304: .get("ui.WorkbenchFrame.close-all"))) {
1305: for (java.util.Iterator it = associatedFrames
1306: .iterator(); it.hasNext();) {
1307: GUIUtil.dispose((JInternalFrame) it.next(),
1308: desktopPane);
1309: }
1310:
1311: } else
1312: return; // finally, I don't want to close
1313: }
1314: layerManager.dispose();
1315: taskFrame.getLayerViewPanel().dispose();
1316: taskFrame.getLayerNamePanel().dispose();
1317: GUIUtil.dispose(taskFrame, desktopPane);
1318: } else
1319: return; // finally, I don't want to close
1320: } else {
1321: // There are other internal frames associated with this task
1322: if (associatedFrames.size() != 0) {
1323: // Confirm you want to close them first
1324: if (confirmClose(
1325: StringUtil
1326: .split(
1327: I18N
1328: .get("ui.WorkbenchFrame.other-internal-frames-depend-on-this-task-frame")
1329: + " "
1330: + I18N
1331: .get("ui.WorkbenchFrame.do-you-want-to-close-them-also"),
1332: 60), I18N
1333: .get("ui.WorkbenchFrame.close-all"))) {
1334: for (java.util.Iterator it = associatedFrames
1335: .iterator(); it.hasNext();) {
1336: GUIUtil.dispose((JInternalFrame) it.next(),
1337: desktopPane);
1338: }
1339: } else
1340: return; // finally, I don't want to close
1341: }
1342: taskFrame.getLayerViewPanel().dispose();
1343: taskFrame.getLayerNamePanel().dispose();
1344: GUIUtil.dispose(taskFrame, desktopPane);
1345: }
1346: }
1347:
1348: private boolean confirmClose(String action,
1349: Collection modifiedLayers) {
1350: if (modifiedLayers.isEmpty()) {
1351: return true;
1352: }
1353: JOptionPane pane = new JOptionPane(
1354: StringUtil
1355: .split(
1356: modifiedLayers.size()
1357: + " "
1358: + I18N
1359: .get("ui.WorkbenchFrame.dataset")
1360: + StringUtil.s(modifiedLayers
1361: .size())
1362: + " "
1363: + ((modifiedLayers.size() > 1) ? I18N
1364: .get("ui.WorkbenchFrame.have-been-modified")
1365: : I18N
1366: .get("ui.WorkbenchFrame.has-been-modified"))
1367: + " ("
1368: + ((modifiedLayers.size() > 3) ? "e.g. "
1369: : "")
1370: + StringUtil
1371: .toCommaDelimitedString(new ArrayList(
1372: modifiedLayers)
1373: .subList(
1374: 0,
1375: Math
1376: .min(
1377: 3,
1378: modifiedLayers
1379: .size())))
1380: + "). "
1381: + I18N
1382: .get("ui.WorkbenchFrame.continue")
1383: + "?", 80),
1384: JOptionPane.WARNING_MESSAGE);
1385: pane.setOptions(new String[] { action,
1386: I18N.get("ui.WorkbenchFrame.cancel") });
1387: pane.createDialog(this , "JUMP").setVisible(true);
1388: return pane.getValue().equals(action);
1389: }
1390:
1391: private boolean confirmClose(String question, String action) {
1392: javax.swing.JOptionPane pane = new javax.swing.JOptionPane(
1393: question, javax.swing.JOptionPane.WARNING_MESSAGE);
1394: pane.setOptions(new String[] {
1395: action,
1396: com.vividsolutions.jump.I18N
1397: .get("ui.WorkbenchFrame.cancel") });
1398: pane.createDialog(this , "JUMP").setVisible(true);
1399: return pane.getValue().equals(action);
1400: }
1401: }
|