/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program 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 2 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.workbench.ui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.swing.JInternalFrame; import javax.swing.JPopupMenu; import javax.swing.JSplitPane; import javax.swing.Timer; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; import com.vividsolutions.jump.workbench.WorkbenchContext; import com.vividsolutions.jump.workbench.model.*; import com.vividsolutions.jump.workbench.ui.cursortool.DummyTool; import com.vividsolutions.jump.workbench.ui.renderer.ImageCachingRenderer; import com.vividsolutions.jump.workbench.ui.renderer.Renderer; import com.vividsolutions.jump.workbench.ui.zoom.ZoomBar; public class TaskFrame extends JInternalFrame implements TaskFrameProxy, CloneableInternalFrame, LayerViewPanelProxy, LayerNamePanelProxy, LayerManagerProxy, SelectionManagerProxy, Task.NameListener { public TaskFrame getTaskFrame() { return this; } /** @deprecated */ private int cloneIndex; private InfoFrame infoFrame = null; private LayerNamePanel layerNamePanel = new DummyLayerNamePanel(); private LayerViewPanel layerViewPanel; private Task task; private WorkbenchContext workbenchContext; //private LayerManager layerManager; private JSplitPane splitPane = new JSplitPane(); private Timer timer; public TaskFrame(Task task, WorkbenchContext workbenchContext) { this(task, 0, workbenchContext); } public SelectionManager getSelectionManager() { return getLayerViewPanel().getSelectionManager(); } public TaskFrame() { } private TaskFrame(Task task, int cloneIndex, final WorkbenchContext workbenchContext) { this.task = task; //this.layerManager = task.getLayerManager(); this.cloneIndex = cloneIndex; this.workbenchContext = workbenchContext; addInternalFrameListener(new InternalFrameAdapter() { public void internalFrameDeactivated(InternalFrameEvent e) { //Deactivate the current CursorTool. Otherwise, the following // problem //can occur: // -- Start drawing a linestring on a task frame. Don't // double-click // to end the gesture. // -- Open a new task frame. You're still drawing the // linestring! // This shouldn't happen; instead, the drawing should be // cancelled. //[Jon Aquino] layerViewPanel.setCurrentCursorTool(new DummyTool()); } public void internalFrameClosed(InternalFrameEvent e) { try { // Code to manage TaskFrame INTERNAL_FRAME_CLOSED event // has been moved to closeTaskFrame method in WorkbenchFrame // I let this method because of the timer.stop [mmichaud] // Maybe the WorkbenchFrame.closeTaskFrame should be moved here... timer.stop(); //memoryCleanup(); } catch (Throwable t) { workbenchContext.getWorkbench().getFrame().handleThrowable( t); } } public void internalFrameOpened(InternalFrameEvent e) { //Set the layerNamePanel when the frame is opened, not in the // constructor, //because #createLayerNamePanel may be overriden in a subclass, // and the //subclass has not yet been constructed -- weird things happen, // like variables //are unexpectedly null. [Jon Aquino] splitPane.remove((Component) layerNamePanel); layerNamePanel = createLayerNamePanel(); splitPane.add((Component) layerNamePanel, JSplitPane.LEFT); layerNamePanel.addListener(workbenchContext.getWorkbench() .getFrame().getLayerNamePanelListener()); } }); layerViewPanel = new LayerViewPanel(task.getLayerManager(), workbenchContext.getWorkbench().getFrame()); try { jbInit(); } catch (Exception e) { e.printStackTrace(); } layerViewPanel.addListener(workbenchContext.getWorkbench().getFrame() .getLayerViewPanelListener()); layerViewPanel.getViewport().addListener( workbenchContext.getWorkbench().getFrame()); task.add(this); installAnimator(); } protected LayerNamePanel createLayerNamePanel() { TreeLayerNamePanel treeLayerNamePanel = new TreeLayerNamePanel(this, new LayerTreeModel(this), this.layerViewPanel .getRenderingManager(), new HashMap()); Map nodeClassToPopupMenuMap = this.workbenchContext.getWorkbench() .getFrame().getNodeClassToPopupMenuMap(); for (Iterator i = nodeClassToPopupMenuMap.keySet().iterator(); i .hasNext();) { Class nodeClass = (Class) i.next(); treeLayerNamePanel.addPopupMenu(nodeClass, (JPopupMenu) nodeClassToPopupMenuMap.get(nodeClass)); } return treeLayerNamePanel; } // // When the internal frame closes there still seem to be Swing objects // with references to it. Clean up the memory as much as we can. We probably // should not overwrite the dispose() method of JInternalFrame. // // // Code to manage TaskFrame INTERNAL_FRAME_CLOSED event has been moved to // closeTaskFrame method in WorkbenchFrame [mmichaud] // [NOTE] Several JInternalFrames subclasses add listeners here and there // It probably need some clean up /* public void memoryCleanup() { timer.stop(); getLayerManager().setFiringEvents(false); getLayerManager().dispose(); layerViewPanel.dispose(); layerNamePanel.dispose(); if (infoFrame != null) { infoFrame.dispose(); infoFrame = null; } layerViewPanel = null; layerNamePanel = null; task = null; workbenchContext = null; timer = null; splitPane = null; } */ public LayerManager getLayerManager() { return task.getLayerManager(); } public InfoFrame getInfoFrame() { if (infoFrame == null || infoFrame.isClosed()) { infoFrame = new PrimaryInfoFrame(workbenchContext, this, this); } return infoFrame; } public LayerNamePanel getLayerNamePanel() { return layerNamePanel; } public LayerViewPanel getLayerViewPanel() { return layerViewPanel; } public void setTask(Task task) { if (this.task != null) { throw new IllegalStateException("Task is already set"); } else { this.task = task; } } public Task getTask() { return task; } private int nextCloneIndex() { String key = getClass().getName() + " - LAST_CLONE_INDEX"; task.getLayerManager().getBlackboard().put(key, 1 + task.getLayerManager().getBlackboard().get(key, 0)); return task.getLayerManager().getBlackboard().getInt(key); } public JInternalFrame internalFrameClone() { TaskFrame clone = new TaskFrame(task, nextCloneIndex(), workbenchContext); clone.splitPane.setDividerLocation(0); clone.setSize(300, 300); if (task.getLayerManager().size() > 0) { clone.getLayerViewPanel().getViewport().initialize( getLayerViewPanel().getViewport().getScale(), getLayerViewPanel().getViewport() .getOriginInModelCoordinates()); clone.getLayerViewPanel().setViewportInitialized(true); } return clone; } public void taskNameChanged(String name) { updateTitle(); } //The border around the tree layer panel looks a bit thick under JDK 1.4. //Remedied by removing the split pane's border. [Jon Aquino] private void jbInit() throws Exception { this.setResizable(true); this.setClosable(true); this.setMaximizable(true); this.setIconifiable(true); //Allow some of the background to show so that user sees this is an MDI // app //[Jon Aquino] this.setSize(680, 380); this.getContentPane().setLayout(new BorderLayout()); splitPane.setBorder(null); this.getContentPane().add(splitPane, BorderLayout.CENTER); splitPane.add((Component) layerNamePanel, JSplitPane.LEFT); splitPane.add(layerViewPanel, JSplitPane.RIGHT); splitPane.setDividerLocation(200); updateTitle(); } protected void updateTitle() { String title = task.getName(); if (cloneIndex > 0) { title += " (View " + (cloneIndex + 1) + ")"; } setTitle(title); } public JSplitPane getSplitPane() { return splitPane; } protected void installAnimator() { timer = new Timer(500, new ActionListener() { public void actionPerformed(ActionEvent e) { if (clockedRenderingInProgress()) { repaint(); } else if (clocksShown()) { repaint(); } } private boolean clockedRenderingInProgress() { for (Iterator i = getLayerManager().getLayerables( Layerable.class).iterator(); i.hasNext();) { Layerable layerable = (Layerable) i.next(); if (!layerable.getBlackboard().get( LayerNameRenderer.USE_CLOCK_ANIMATION_KEY, false)) { continue; } Renderer renderer = layerViewPanel.getRenderingManager() .getRenderer(layerable); if (renderer != null && renderer.isRendering()) { return true; } } return false; } // Previously we had a flag to keep track of whether // clocks were displayed. However that was not sufficient, // as quick-rendering layers were missed by the timer, // and thus the clock icon, if painted (e.g. by #zoomChanged // in TreeLayerNamePanel), would not be cleared. So here // we do a more thorough check for whether any clocks are // displayed. [Jon Aquino 2005-03-14] private boolean clocksShown() { for (Iterator i = getLayerManager().getLayerables( Layerable.class).iterator(); i.hasNext();) { Layerable layerable = (Layerable) i.next(); if (layerable.getBlackboard().get( LayerNameRenderer.PROGRESS_ICON_KEY) != null) { return true; } } return false; } }); timer.setCoalesce(true); timer.start(); } }