001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI for
003: * 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 modify it under
008: * the terms of the GNU General Public License as published by the Free Software
009: * Foundation; either version 2 of the License, or (at your option) any later
010: * version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
014: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
015: * details.
016: *
017: * You should have received a copy of the GNU General Public License along with
018: * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
019: * Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions Suite #1A 2328 Government Street Victoria BC V8T 5G5 Canada
024: *
025: * (250)385-6040 www.vividsolutions.com
026: */
027:
028: package com.vividsolutions.jump.workbench.ui.task;
029:
030: import java.awt.event.ActionEvent;
031: import java.awt.event.ActionListener;
032: import java.awt.event.ComponentAdapter;
033: import java.awt.event.ComponentEvent;
034: import java.awt.event.WindowAdapter;
035: import java.awt.event.WindowEvent;
036: import java.util.Date;
037:
038: import javax.swing.JOptionPane;
039: import javax.swing.Timer;
040:
041: import com.vividsolutions.jump.util.StringUtil;
042: import com.vividsolutions.jump.workbench.plugin.PlugInContext;
043: import com.vividsolutions.jump.workbench.plugin.ThreadedPlugIn;
044: import com.vividsolutions.jump.workbench.ui.GUIUtil;
045: import com.vividsolutions.jump.workbench.ui.WorkbenchFrame;
046:
047: /**
048: *
049: * @TODO :I18N
050: */
051: public class TaskMonitorManager {
052: public TaskMonitorManager() {
053: }
054:
055: /**
056: * Executes the task in a separate thread, reporting progress in a dialog.
057: */
058: public void execute(ThreadedPlugIn plugIn, PlugInContext context) {
059: final TaskMonitorDialog progressDialog = new TaskMonitorDialog(
060: context.getWorkbenchFrame(), context.getErrorHandler());
061: progressDialog.setTitle(plugIn.getName());
062:
063: //Do not refer to context inside the anonymous class, otherwise it (and
064: //the Task it refers to) will not be garbage collected. [Jon Aquino]
065: //<<TODO>> Eliminate TaskWrapper. The Task no longer needs to be
066: //garbage-collectable for the data to be garbage-collected. [Jon
067: // Aquino]
068: final TaskWrapper taskWrapper = new TaskWrapper(plugIn,
069: context, progressDialog);
070: final Thread thread = new Thread(taskWrapper);
071: progressDialog.addWindowListener(new WindowAdapter() {
072: private int attempts = 0;
073:
074: public void windowClosing(WindowEvent e) {
075: if (JOptionPane.NO_OPTION == JOptionPane
076: .showConfirmDialog(
077: progressDialog,
078: StringUtil
079: .split(
080: "Warning: Killing the process may result in data corruption or data loss. "
081: + "Are you sure you want to kill the process?",
082: 80), "Kill Process",
083: JOptionPane.YES_NO_OPTION,
084: JOptionPane.WARNING_MESSAGE)) {
085: return;
086: }
087: attempts++;
088: if (attempts > 1) {
089: // Sometimes the thread seems to take a while to die.
090: // So force the dialog to close if the user has pressed
091: // the close button for the second time.
092: // [Jon Aquino 2005-03-14]
093: progressDialog.setVisible(false);
094: }
095: thread.stop();
096: }
097: });
098: progressDialog.addComponentListener(new ComponentAdapter() {
099: public void componentShown(ComponentEvent e) {
100: //Wait for the dialog to appear before starting the task.
101: // Otherwise the task might possibly finish before the dialog
102: // appeared and the dialog would never close. [Jon Aquino]
103: thread.start();
104: }
105: });
106: GUIUtil.centreOnWindow(progressDialog);
107:
108: Timer timer = timer(new Date(), plugIn, context
109: .getWorkbenchFrame());
110: timer.start();
111:
112: try {
113: progressDialog.setVisible(true);
114: } finally {
115: timer.stop();
116: }
117: }
118:
119: private Timer timer(final Date start, final ThreadedPlugIn plugIn,
120: final WorkbenchFrame workbenchFrame) {
121: return new Timer(1000, new ActionListener() {
122: public void actionPerformed(ActionEvent e) {
123: String message = "";
124: message += StringUtil.toTimeString(new Date().getTime()
125: - start.getTime());
126: message += (" (" + plugIn.getName() + ")");
127: workbenchFrame.setTimeMessage(message);
128: }
129: });
130: }
131:
132: private class TaskWrapper implements Runnable {
133: private ThreadedPlugIn plugIn;
134:
135: private PlugInContext context;
136:
137: private TaskMonitorDialog dialog;
138:
139: public TaskWrapper(ThreadedPlugIn plugIn,
140: PlugInContext context, TaskMonitorDialog dialog) {
141: this .plugIn = plugIn;
142: this .context = context;
143: this .dialog = dialog;
144: }
145:
146: public void run() {
147: Throwable throwable = null;
148:
149: try {
150: plugIn.run(dialog, context);
151: } catch (Throwable t) {
152: throwable = t;
153: } finally {
154: // Hmm - race conditions because we are doing a GUI action
155: // (#setVisible) outside the AWT event thread? Case in point:
156: // AutoConflatePlugIn displays a dialog using #invokeLater,
157: // but timer keeps on running until dialog is closed . . .
158: // [Jon Aquino 2004-09-07]
159: dialog.setVisible(false);
160:
161: if (throwable != null) {
162: context.getErrorHandler()
163: .handleThrowable(throwable);
164: }
165:
166: // Releases references to the data, to facilitate garbage
167: // collection. [Jon Aquino]
168: context = null;
169: }
170: }
171: }
172: }
|