Lines 43-274
Link Here
|
43 |
*/ |
43 |
*/ |
44 |
package org.netbeans.modules.cnd.utils.ui; |
44 |
package org.netbeans.modules.cnd.utils.ui; |
45 |
|
45 |
|
46 |
import java.awt.Dialog; |
46 |
import java.util.concurrent.ExecutionException; |
47 |
import java.awt.Dimension; |
47 |
import java.util.concurrent.Future; |
48 |
import java.awt.Frame; |
|
|
49 |
import java.awt.Rectangle; |
50 |
import java.awt.Toolkit; |
51 |
import java.awt.Window; |
52 |
import java.awt.event.WindowAdapter; |
53 |
import java.awt.event.WindowEvent; |
54 |
import java.util.concurrent.atomic.AtomicBoolean; |
48 |
import java.util.concurrent.atomic.AtomicBoolean; |
55 |
import javax.swing.JDialog; |
|
|
56 |
import javax.swing.JPanel; |
57 |
import javax.swing.SwingUtilities; |
49 |
import javax.swing.SwingUtilities; |
|
|
50 |
import org.netbeans.api.progress.ProgressHandle; |
51 |
import org.netbeans.api.progress.ProgressRunnable; |
52 |
import org.netbeans.api.progress.ProgressUtils; |
58 |
import org.netbeans.modules.cnd.utils.CndUtils; |
53 |
import org.netbeans.modules.cnd.utils.CndUtils; |
59 |
import org.netbeans.modules.cnd.utils.NamedRunnable; |
54 |
import org.netbeans.modules.cnd.utils.NamedRunnable; |
60 |
import org.openide.util.Cancellable; |
55 |
import org.openide.util.Cancellable; |
|
|
56 |
import org.openide.util.Exceptions; |
61 |
import org.openide.util.RequestProcessor; |
57 |
import org.openide.util.RequestProcessor; |
62 |
import org.openide.util.RequestProcessor.Task; |
|
|
63 |
|
58 |
|
64 |
/** |
59 |
/** |
|
|
60 |
* Utility class for displaying a modal Dialog (effectively blocking (but not |
61 |
* freezing) UI) while running a task in background. |
62 |
* |
63 |
* Current implementation uses org.netbeans.api.progress.ProgressUtils. |
65 |
* |
64 |
* |
66 |
* @author Vladimir Voskresensky |
65 |
* @author Vladimir Voskresensky |
67 |
*/ |
66 |
*/ |
68 |
public class ModalMessageDlg extends javax.swing.JPanel { |
67 |
public final class ModalMessageDlg { |
69 |
private static final RequestProcessor RP = new RequestProcessor(ModalMessageDlg.class.getName(), 4); |
|
|
70 |
|
68 |
|
71 |
/** |
69 |
public interface LongWorker { |
72 |
* allows to display modal dialog with title and message for the period of |
70 |
|
73 |
* long task run |
71 |
void doWork(); |
74 |
* @param parent parent frame or dialog |
72 |
|
75 |
* @param workTask non EDT task to run |
73 |
void doPostRunInEDT(); |
76 |
* @param postEDTTask EDT task to run after closing modal dialog (can be null) |
|
|
77 |
* @param title title of dialog |
78 |
* @param message message in dialog |
79 |
* @return |
80 |
*/ |
81 |
public static void runLongTask(Dialog parent, |
82 |
final Runnable workTask, final Runnable postEDTTask, final Cancellable canceller, |
83 |
String title, String message) { |
84 |
runLongTaskImpl(parent, workTask, postEDTTask, title, message, canceller); |
85 |
} |
74 |
} |
86 |
|
75 |
|
87 |
public static void runLongTask(Frame parent, |
76 |
private ModalMessageDlg() { |
88 |
final Runnable workTask, final Runnable postEDTTask, final Cancellable canceller, |
|
|
89 |
String title, String message) { |
90 |
runLongTaskImpl(parent, workTask, postEDTTask, title, message, canceller); |
91 |
} |
77 |
} |
92 |
|
78 |
|
93 |
/** |
79 |
/** |
|
|
80 |
* Show a modal progress dialog that blocks the main window, while running |
81 |
* the passed runnable on a background thread. |
94 |
* |
82 |
* |
95 |
* @param parent |
83 |
* <p> This method is thread-safe, and will block until the operation has |
96 |
* @param title |
84 |
* completed, regardless of what thread calls this method. </p> |
97 |
* @param message |
85 |
* |
98 |
* @param workTask if workTask is Cancellable => dialog will have Cancel button |
86 |
* @param workTask a Runnable to start in non-EDT thread. Unlike |
99 |
* if not => no Cancel button is shown |
87 |
* {@link ProgressUtils}'s operation this argument is never tested for the |
|
|
88 |
* {@link Cancellable} interface. Use <b>canceller</b> for the task |
89 |
* interruption facility. |
90 |
* @param postEDTTask a task to run in the EDT after the <b>workTask</b> is |
91 |
* processed. <code>null</code> is allowed. <b>NB:</b> The postEDTTask is |
92 |
* called either if this method is invoked from tests or if <b>workTask</b> |
93 |
* was <b>not cancelled</b> by pressing the cancel button. |
94 |
* @param canceller if not <code>null</code> a cancel button is displayed |
95 |
* while <b>workTask</b> is running. <br/> Note that once the cancel button |
96 |
* is pressed, no <b>postEDTTask</b> will be called. |
97 |
* @param title title of a dialog. If <b>message</b> is not null, |
98 |
* <b>title</b> is <b>not used</b>. |
99 |
* @param message message to display. Could be <code>null</code>. In this |
100 |
* case <b>title</b> is used. |
101 |
* |
100 |
*/ |
102 |
*/ |
101 |
public static void runLongTask(Window parent, String title, String message, |
103 |
public static void runLongTask(final Runnable workTask, final Runnable postEDTTask, final Cancellable canceller, |
102 |
final LongWorker workTask, final Cancellable canceller) { |
104 |
final String title, final String message) { |
103 |
|
105 |
|
104 |
final JDialog dialog = createDialog(parent, title); |
106 |
if (invokedFromTests()) { |
|
|
107 |
// The code below is just to preserve the behaviour as it was |
108 |
// before switchig to the ProgressUtils... |
109 |
Future<?> task = RequestProcessor.getDefault().submit(new NamedRunnable(title) { |
110 |
@Override |
111 |
protected void runImpl() { |
112 |
workTask.run(); |
113 |
} |
114 |
}); |
115 |
try { |
116 |
task.get(); |
117 |
} catch (InterruptedException ex) { |
118 |
Exceptions.printStackTrace(ex); |
119 |
} catch (ExecutionException ex) { |
120 |
Exceptions.printStackTrace(ex); |
121 |
} |
122 |
if (postEDTTask != null) { |
123 |
SwingUtilities.invokeLater(postEDTTask); |
124 |
} |
125 |
} else { |
126 |
final MyRP r; |
105 |
|
127 |
|
106 |
final Runnable finalizer = new Runnable() { |
128 |
if (canceller != null) { |
107 |
@Override |
129 |
r = new MyCRP(workTask, canceller, message); |
108 |
public void run() { |
130 |
} else { |
109 |
// hide dialog and run action if successfully connected |
131 |
r = new MyRP(workTask, message); |
110 |
dialog.setVisible(false); |
|
|
111 |
dialog.dispose(); |
112 |
workTask.doPostRunInEDT(); |
113 |
} |
132 |
} |
114 |
}; |
|
|
115 |
|
133 |
|
116 |
JPanel panel; |
134 |
ProgressUtils.showProgressDialogAndRun(r, title, true); |
117 |
if (canceller == null) { |
|
|
118 |
panel = new ModalMessageDlgPane(message); |
119 |
} else { |
120 |
Cancellable wrapper = new Cancellable() { |
121 |
/** is invoked from a separate cancellation thread */ |
122 |
@Override |
123 |
public boolean cancel() { |
124 |
if (canceller.cancel()) { |
125 |
// remember for return value |
126 |
return true; |
127 |
} else { |
128 |
return false; |
129 |
} |
130 |
} |
131 |
}; |
132 |
panel = new ModalMessageDlgCancellablePane(message, wrapper); |
133 |
} |
134 |
addPanel(parent, dialog, panel); |
135 |
|
135 |
|
136 |
RP.post(new NamedRunnable(title) { |
136 |
if (postEDTTask != null && !r.wasCancelled()) { |
137 |
@Override |
137 |
SwingUtilities.invokeLater(workTask); |
138 |
public void runImpl() { |
|
|
139 |
try { |
140 |
workTask.doWork(); |
141 |
} finally { |
142 |
SwingUtilities.invokeLater(finalizer); |
143 |
} |
144 |
} |
138 |
} |
145 |
}); |
|
|
146 |
if (!CndUtils.isStandalone()) { // this means we run in tests |
147 |
dialog.setVisible(true); |
148 |
} |
139 |
} |
149 |
} |
140 |
} |
150 |
|
141 |
|
151 |
private static boolean runLongTaskImpl(Window parent, final Runnable workTask, final Runnable postEDTTask, |
142 |
/** |
152 |
final String title, String message, final Cancellable canceller) { |
143 |
* Show a modal progress dialog that blocks the main window, while running |
153 |
|
144 |
* the passed runnable on a background thread. |
154 |
final JDialog dialog = createDialog(parent, title); |
145 |
* |
155 |
final AtomicBoolean cancelled = new AtomicBoolean(false); |
146 |
* <p> This is almost the same as runLongTask(Dialog, Runnable, Runnable, |
156 |
|
147 |
* Cancellable, String, String), but has one significant difference! </p> |
157 |
final Runnable finalizer = new Runnable() { |
148 |
* |
|
|
149 |
* <p>Not only it uses a {@link LongWorker} instead of two |
150 |
* {@link Runnable}s, but also the {@link LongWorker#doPostRunInEDT()} is |
151 |
* invoked disregarding the cancellation status.</p> |
152 |
* |
153 |
* @see #runLongTask(java.lang.Runnable, java.lang.Runnable, org.openide.util.Cancellable, java.lang.String, java.lang.String) |
154 |
*/ |
155 |
public static void runLongTask(final LongWorker workTask, final Cancellable canceller, |
156 |
final String title, final String message) { |
157 |
runLongTask(new Runnable() { |
158 |
@Override |
158 |
@Override |
159 |
public void run() { |
159 |
public void run() { |
160 |
// hide dialog and run action if successfully connected |
160 |
workTask.doWork(); |
161 |
dialog.setVisible(false); |
|
|
162 |
dialog.dispose(); |
163 |
if (postEDTTask != null && ! cancelled.get()) { |
164 |
postEDTTask.run(); |
165 |
} |
166 |
} |
161 |
} |
167 |
}; |
162 |
}, new Runnable(){ |
168 |
|
163 |
@Override |
169 |
JPanel panel; |
164 |
public void run() { |
170 |
if (canceller == null) { |
165 |
workTask.doPostRunInEDT(); |
171 |
panel = new ModalMessageDlgPane(message); |
166 |
} |
172 |
} else { |
167 |
}, canceller, title, message); |
173 |
Cancellable wrapper = new Cancellable() { |
|
|
174 |
/** is invoked from a separate cancellation thread */ |
175 |
@Override |
176 |
public boolean cancel() { |
177 |
if (canceller.cancel()) { |
178 |
cancelled.set(true); |
179 |
SwingUtilities.invokeLater(finalizer); |
180 |
return true; |
181 |
} else { |
182 |
return false; |
183 |
} |
184 |
} |
185 |
}; |
186 |
panel = new ModalMessageDlgCancellablePane(message, wrapper); |
187 |
} |
188 |
addPanel(parent, dialog, panel); |
189 |
|
190 |
final WindowAdapterImpl windowAdapterImpl = new WindowAdapterImpl(dialog, title, workTask, finalizer); |
191 |
|
192 |
if (CndUtils.isStandalone()) { // this means we run in tests |
193 |
Task task = windowAdapterImpl.submitJob(); |
194 |
task.waitFinished(); |
195 |
} else { |
196 |
dialog.addWindowListener(windowAdapterImpl); |
197 |
dialog.setVisible(true); |
198 |
} |
199 |
|
200 |
return !cancelled.get(); |
201 |
} |
168 |
} |
202 |
|
169 |
|
203 |
private static JDialog createDialog(Window parent, String title) { |
170 |
private static boolean invokedFromTests() { |
204 |
JDialog dialog; |
171 |
return CndUtils.isStandalone(); |
205 |
if (parent == null) { |
|
|
206 |
dialog = new JDialog(); |
207 |
} else if (parent instanceof Frame) { |
208 |
dialog = new JDialog((Frame)parent); |
209 |
} else { |
210 |
assert (parent instanceof Dialog); |
211 |
dialog = new JDialog((Dialog)parent); |
212 |
} |
213 |
dialog.setTitle(title); |
214 |
dialog.setModal(true); |
215 |
return dialog; |
216 |
} |
172 |
} |
217 |
|
173 |
|
218 |
private static void addPanel(Window parent, JDialog dialog, JPanel panel){ |
174 |
private static class MyRP implements ProgressRunnable<Void> { |
219 |
dialog.getContentPane().add(panel); |
|
|
220 |
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); //make sure the dialog is not closed during the project open |
221 |
dialog.pack(); |
222 |
|
175 |
|
223 |
Rectangle bounds = (parent == null) ? |
176 |
private final Runnable task; |
224 |
new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) : parent.getBounds(); |
177 |
private final String message; |
225 |
|
178 |
|
226 |
int middleX = bounds.x + bounds.width / 2; |
179 |
public MyRP(final Runnable task, final String message) { |
227 |
int middleY = bounds.y + bounds.height / 2; |
180 |
this.task = task; |
228 |
|
181 |
this.message = message; |
229 |
Dimension size = dialog.getPreferredSize(); |
|
|
230 |
|
231 |
dialog.setBounds(middleX - size.width / 2, middleY - size.height / 2, size.width, size.height); |
232 |
} |
233 |
|
234 |
public interface LongWorker { |
235 |
void doWork(); |
236 |
void doPostRunInEDT(); |
237 |
} |
238 |
|
239 |
private static class WindowAdapterImpl extends WindowAdapter { |
240 |
|
241 |
private final String title; |
242 |
private final Runnable workTask; |
243 |
private final Runnable finalizer; |
244 |
private final JDialog dialog; |
245 |
|
246 |
public WindowAdapterImpl(JDialog dialog, String title, Runnable workTask, Runnable finalizer) { |
247 |
this.title = title; |
248 |
this.workTask = workTask; |
249 |
this.finalizer = finalizer; |
250 |
this.dialog = dialog; |
251 |
} |
182 |
} |
252 |
|
183 |
|
253 |
@Override |
184 |
@Override |
254 |
public void windowOpened(WindowEvent e) { |
185 |
public Void run(final ProgressHandle handle) { |
255 |
dialog.removeWindowListener(this); |
186 |
if (message != null) { |
256 |
submitJob(); |
187 |
handle.setDisplayName(message); |
|
|
188 |
} |
189 |
task.run(); |
190 |
return null; |
257 |
} |
191 |
} |
258 |
|
192 |
|
259 |
private Task submitJob() { |
193 |
public boolean wasCancelled() { |
260 |
Task task = RP.post(new NamedRunnable(title) { |
194 |
return false; |
|
|
195 |
} |
196 |
} |
261 |
|
197 |
|
262 |
@Override |
198 |
private static final class MyCRP extends MyRP implements Cancellable { |
263 |
public void runImpl() { |
199 |
|
264 |
try { |
200 |
private final Cancellable canceller; |
265 |
workTask.run(); |
201 |
private final AtomicBoolean wasCancelled = new AtomicBoolean(false); |
266 |
} finally { |
202 |
|
267 |
SwingUtilities.invokeLater(finalizer); |
203 |
public MyCRP(final Runnable task, final Cancellable canceller, final String message) { |
268 |
} |
204 |
super(task, message); |
269 |
} |
205 |
this.canceller = canceller; |
270 |
}); |
206 |
} |
271 |
return task; |
207 |
|
|
|
208 |
@Override |
209 |
public boolean cancel() { |
210 |
if(canceller.cancel()) { |
211 |
wasCancelled.set(true); |
212 |
return true; |
213 |
} else { |
214 |
return false; |
215 |
} |
216 |
} |
217 |
|
218 |
@Override |
219 |
public boolean wasCancelled() { |
220 |
return wasCancelled.get(); |
272 |
} |
221 |
} |
273 |
} |
222 |
} |
274 |
} |
223 |
} |