001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.workbench.ui;
033:
034: import java.awt.BorderLayout;
035: import java.awt.Component;
036: import java.awt.event.ActionEvent;
037: import java.awt.event.ActionListener;
038: import java.util.Date;
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.Map;
042: import javax.swing.JInternalFrame;
043: import javax.swing.JPopupMenu;
044: import javax.swing.JSplitPane;
045: import javax.swing.Timer;
046: import javax.swing.event.InternalFrameAdapter;
047: import javax.swing.event.InternalFrameEvent;
048: import com.vividsolutions.jump.workbench.WorkbenchContext;
049: import com.vividsolutions.jump.workbench.model.*;
050: import com.vividsolutions.jump.workbench.ui.cursortool.DummyTool;
051: import com.vividsolutions.jump.workbench.ui.renderer.ImageCachingRenderer;
052: import com.vividsolutions.jump.workbench.ui.renderer.Renderer;
053:
054: import com.vividsolutions.jump.workbench.ui.zoom.ZoomBar;
055:
056: public class TaskFrame extends JInternalFrame implements
057: TaskFrameProxy, CloneableInternalFrame, LayerViewPanelProxy,
058: LayerNamePanelProxy, LayerManagerProxy, SelectionManagerProxy,
059: Task.NameListener {
060:
061: public TaskFrame getTaskFrame() {
062: return this ;
063: }
064:
065: /** @deprecated */
066: private int cloneIndex;
067:
068: private InfoFrame infoFrame = null;
069:
070: private LayerNamePanel layerNamePanel = new DummyLayerNamePanel();
071:
072: private LayerViewPanel layerViewPanel;
073:
074: private Task task;
075:
076: private WorkbenchContext workbenchContext;
077:
078: //private LayerManager layerManager;
079: private JSplitPane splitPane = new JSplitPane();
080:
081: private Timer timer;
082:
083: public TaskFrame(Task task, WorkbenchContext workbenchContext) {
084: this (task, 0, workbenchContext);
085: }
086:
087: public SelectionManager getSelectionManager() {
088: return getLayerViewPanel().getSelectionManager();
089: }
090:
091: private TaskFrame(Task task, int cloneIndex,
092: final WorkbenchContext workbenchContext) {
093: this .task = task;
094: //this.layerManager = task.getLayerManager();
095: this .cloneIndex = cloneIndex;
096: this .workbenchContext = workbenchContext;
097: addInternalFrameListener(new InternalFrameAdapter() {
098: public void internalFrameDeactivated(InternalFrameEvent e) {
099: //Deactivate the current CursorTool. Otherwise, the following
100: // problem
101: //can occur:
102: // -- Start drawing a linestring on a task frame. Don't
103: // double-click
104: // to end the gesture.
105: // -- Open a new task frame. You're still drawing the
106: // linestring!
107: // This shouldn't happen; instead, the drawing should be
108: // cancelled.
109: //[Jon Aquino]
110: layerViewPanel.setCurrentCursorTool(new DummyTool());
111: }
112:
113: public void internalFrameClosed(InternalFrameEvent e) {
114: try {
115: // Code to manage TaskFrame INTERNAL_FRAME_CLOSED event
116: // has been moved to closeTaskFrame method in WorkbenchFrame
117: // I let this method because of the timer.stop [mmichaud]
118: // Maybe the WorkbenchFrame.closeTaskFrame should be moved here...
119: timer.stop();
120: //memoryCleanup();
121: } catch (Throwable t) {
122: workbenchContext.getWorkbench().getFrame()
123: .handleThrowable(t);
124: }
125: }
126:
127: public void internalFrameOpened(InternalFrameEvent e) {
128: //Set the layerNamePanel when the frame is opened, not in the
129: // constructor,
130: //because #createLayerNamePanel may be overriden in a subclass,
131: // and the
132: //subclass has not yet been constructed -- weird things happen,
133: // like variables
134: //are unexpectedly null. [Jon Aquino]
135: splitPane.remove((Component) layerNamePanel);
136: layerNamePanel = createLayerNamePanel();
137: splitPane.add((Component) layerNamePanel,
138: JSplitPane.LEFT);
139: layerNamePanel.addListener(workbenchContext
140: .getWorkbench().getFrame()
141: .getLayerNamePanelListener());
142: }
143:
144: });
145: layerViewPanel = new LayerViewPanel(task.getLayerManager(),
146: workbenchContext.getWorkbench().getFrame());
147:
148: try {
149: jbInit();
150: } catch (Exception e) {
151: e.printStackTrace();
152: }
153:
154: layerViewPanel.addListener(workbenchContext.getWorkbench()
155: .getFrame().getLayerViewPanelListener());
156: layerViewPanel.getViewport().addListener(
157: workbenchContext.getWorkbench().getFrame());
158: task.add(this );
159: installAnimator();
160: }
161:
162: protected LayerNamePanel createLayerNamePanel() {
163: TreeLayerNamePanel treeLayerNamePanel = new TreeLayerNamePanel(
164: this , new LayerTreeModel(this ), this .layerViewPanel
165: .getRenderingManager(), new HashMap());
166: Map nodeClassToPopupMenuMap = this .workbenchContext
167: .getWorkbench().getFrame().getNodeClassToPopupMenuMap();
168: for (Iterator i = nodeClassToPopupMenuMap.keySet().iterator(); i
169: .hasNext();) {
170: Class nodeClass = (Class) i.next();
171: treeLayerNamePanel
172: .addPopupMenu(nodeClass,
173: (JPopupMenu) nodeClassToPopupMenuMap
174: .get(nodeClass));
175: }
176: return treeLayerNamePanel;
177: }
178:
179: //
180: // When the internal frame closes there still seem to be Swing objects
181: // with references to it. Clean up the memory as much as we can. We probably
182: // should not overwrite the dispose() method of JInternalFrame.
183: //
184: //
185: // Code to manage TaskFrame INTERNAL_FRAME_CLOSED event has been moved to
186: // closeTaskFrame method in WorkbenchFrame [mmichaud]
187: // [NOTE] Several JInternalFrames subclasses add listeners here and there
188: // It probably need some clean up
189: /*
190: public void memoryCleanup() {
191: timer.stop();
192:
193: getLayerManager().setFiringEvents(false);
194: getLayerManager().dispose();
195:
196: layerViewPanel.dispose();
197: layerNamePanel.dispose();
198:
199: if (infoFrame != null) {
200: infoFrame.dispose();
201: infoFrame = null;
202: }
203:
204: layerViewPanel = null;
205: layerNamePanel = null;
206: task = null;
207: workbenchContext = null;
208: timer = null;
209: splitPane = null;
210: }
211: */
212:
213: public LayerManager getLayerManager() {
214: return task.getLayerManager();
215: }
216:
217: public InfoFrame getInfoFrame() {
218: if (infoFrame == null || infoFrame.isClosed()) {
219: infoFrame = new PrimaryInfoFrame(workbenchContext, this ,
220: this );
221: }
222: return infoFrame;
223: }
224:
225: public LayerNamePanel getLayerNamePanel() {
226: return layerNamePanel;
227: }
228:
229: public LayerViewPanel getLayerViewPanel() {
230: return layerViewPanel;
231: }
232:
233: public Task getTask() {
234: return task;
235: }
236:
237: private int nextCloneIndex() {
238: String key = getClass().getName() + " - LAST_CLONE_INDEX";
239: task.getLayerManager().getBlackboard().put(key,
240: 1 + task.getLayerManager().getBlackboard().get(key, 0));
241:
242: return task.getLayerManager().getBlackboard().getInt(key);
243: }
244:
245: public JInternalFrame internalFrameClone() {
246: TaskFrame clone = new TaskFrame(task, nextCloneIndex(),
247: workbenchContext);
248: clone.splitPane.setDividerLocation(0);
249: clone.setSize(300, 300);
250:
251: if (task.getLayerManager().size() > 0) {
252: clone.getLayerViewPanel().getViewport().initialize(
253: getLayerViewPanel().getViewport().getScale(),
254: getLayerViewPanel().getViewport()
255: .getOriginInModelCoordinates());
256: clone.getLayerViewPanel().setViewportInitialized(true);
257: }
258:
259: return clone;
260: }
261:
262: public void taskNameChanged(String name) {
263: updateTitle();
264: }
265:
266: //The border around the tree layer panel looks a bit thick under JDK 1.4.
267: //Remedied by removing the split pane's border. [Jon Aquino]
268: private void jbInit() throws Exception {
269: this .setResizable(true);
270: this .setClosable(true);
271: this .setMaximizable(true);
272: this .setIconifiable(true);
273:
274: //Allow some of the background to show so that user sees this is an MDI
275: // app
276: //[Jon Aquino]
277: this .setSize(680, 380);
278: this .getContentPane().setLayout(new BorderLayout());
279: splitPane.setBorder(null);
280: this .getContentPane().add(splitPane, BorderLayout.CENTER);
281: splitPane.add((Component) layerNamePanel, JSplitPane.LEFT);
282: splitPane.add(layerViewPanel, JSplitPane.RIGHT);
283: splitPane.setDividerLocation(200);
284: updateTitle();
285: }
286:
287: private void updateTitle() {
288: String title = task.getName();
289: if (cloneIndex > 0) {
290: title += " (View " + (cloneIndex + 1) + ")";
291: }
292: setTitle(title);
293: }
294:
295: public JSplitPane getSplitPane() {
296: return splitPane;
297: }
298:
299: private void installAnimator() {
300: timer = new Timer(500, new ActionListener() {
301: public void actionPerformed(ActionEvent e) {
302: if (clockedRenderingInProgress()) {
303: repaint();
304: } else if (clocksShown()) {
305: repaint();
306: }
307: }
308:
309: private boolean clockedRenderingInProgress() {
310: for (Iterator i = getLayerManager().getLayerables(
311: Layerable.class).iterator(); i.hasNext();) {
312: Layerable layerable = (Layerable) i.next();
313: if (!layerable.getBlackboard().get(
314: LayerNameRenderer.USE_CLOCK_ANIMATION_KEY,
315: false)) {
316: continue;
317: }
318: Renderer renderer = layerViewPanel
319: .getRenderingManager().getRenderer(
320: layerable);
321: if (renderer != null && renderer.isRendering()) {
322: return true;
323: }
324: }
325: return false;
326: }
327:
328: // Previously we had a flag to keep track of whether
329: // clocks were displayed. However that was not sufficient,
330: // as quick-rendering layers were missed by the timer,
331: // and thus the clock icon, if painted (e.g. by #zoomChanged
332: // in TreeLayerNamePanel), would not be cleared. So here
333: // we do a more thorough check for whether any clocks are
334: // displayed. [Jon Aquino 2005-03-14]
335: private boolean clocksShown() {
336: for (Iterator i = getLayerManager().getLayerables(
337: Layerable.class).iterator(); i.hasNext();) {
338: Layerable layerable = (Layerable) i.next();
339: if (layerable.getBlackboard().get(
340: LayerNameRenderer.PROGRESS_ICON_KEY) != null) {
341: return true;
342: }
343: }
344: return false;
345: }
346: });
347: timer.setCoalesce(true);
348: timer.start();
349: }
350:
351: }
|