This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 204553
Collapse All | Expand All

(-)a/api.progress/apichanges.xml (-1 / +13 lines)
Lines 106-113 Link Here
106
    </apidefs>
106
    </apidefs>
107
107
108
    <!-- ACTUAL CHANGES BEGIN HERE: -->
108
    <!-- ACTUAL CHANGES BEGIN HERE: -->
109
  <changes>
110
    <change id="customDialogs">
111
        <api name="progress_api"/>
112
        <summary><code>ProgressUtils</code> class with <code>runOffEventThreadWithCustomDialogContent</code> and <code>runOffEventThreadWithProgressDialog</code> methods were added.</summary>
113
        <version major="1" minor="30"/>
114
        <date day="28" month="8" year="2012"/>
115
        <author login="pflaska	"/>
116
        <compatibility binary="compatible" source="compatible" deprecation="no" deletion="no" addition="yes"/>
117
        <description><code>ProgressUtils</code> class with <code>runOffEventThreadWithCustomDialogContent</code> and <code>runOffEventThreadWithProgressDialog</code> methods were added. These methods allow movement of operations out of AWT thread, showing the waint cursor after one second and a dialog when task is not finished in a three seconds.
118
        </description>
119
        <class package="org.netbeans.api.progress" name="ProgressUtils"/>
120
        <issue number="204553"/>
121
    </change>
109
122
110
  <changes>
111
    <change id="modalRunMethods">
123
    <change id="modalRunMethods">
112
        <api name="progress_api"/>
124
        <api name="progress_api"/>
113
        <summary>Modal progress dialogs</summary>
125
        <summary>Modal progress dialogs</summary>
(-)a/api.progress/manifest.mf (-1 / +1 lines)
Lines 3-7 Link Here
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/progress/module/resources/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/progress/module/resources/Bundle.properties
4
OpenIDE-Module-Recommends: org.netbeans.modules.progress.spi.ProgressUIWorkerProvider, org.netbeans.modules.progress.spi.RunOffEDTProvider
4
OpenIDE-Module-Recommends: org.netbeans.modules.progress.spi.ProgressUIWorkerProvider, org.netbeans.modules.progress.spi.RunOffEDTProvider
5
AutoUpdate-Essential-Module: true
5
AutoUpdate-Essential-Module: true
6
OpenIDE-Module-Specification-Version: 1.29
6
OpenIDE-Module-Specification-Version: 1.30
7
7
(-)a/api.progress/src/org/netbeans/api/progress/ProgressUtils.java (+84 lines)
Lines 47-55 Link Here
47
import java.util.concurrent.FutureTask;
47
import java.util.concurrent.FutureTask;
48
import java.util.concurrent.atomic.AtomicBoolean;
48
import java.util.concurrent.atomic.AtomicBoolean;
49
import java.util.concurrent.atomic.AtomicReference;
49
import java.util.concurrent.atomic.AtomicReference;
50
import javax.swing.JPanel;
50
import javax.swing.SwingUtilities;
51
import javax.swing.SwingUtilities;
51
import org.netbeans.modules.progress.spi.RunOffEDTProvider;
52
import org.netbeans.modules.progress.spi.RunOffEDTProvider;
52
import org.netbeans.modules.progress.spi.RunOffEDTProvider.Progress;
53
import org.netbeans.modules.progress.spi.RunOffEDTProvider.Progress;
54
import org.netbeans.modules.progress.spi.RunOffEDTProvider.Progress2;
53
import org.openide.util.Lookup;
55
import org.openide.util.Lookup;
54
import org.openide.util.RequestProcessor;
56
import org.openide.util.RequestProcessor;
55
import org.openide.util.RequestProcessor.Task;
57
import org.openide.util.RequestProcessor.Task;
Lines 140-145 Link Here
140
    }
142
    }
141
143
142
    /**
144
    /**
145
     * Runs operation out of the event thread, blocking the whole UI. When
146
     * operation takes more than 1s, the method first displays wait cursor.
147
     * If operation will not end in 3s interval, modal dialog with
148
     * progress is shown up.
149
     * If operation is marked with {@link org.openide.util.Cancellable}
150
     * interface, cancel button is part of dialog and can be used 
151
     * to interrupt the operation.
152
     * 
153
     * @param operation  task to perform in the background
154
     * @param dialogTitle dialog title
155
     * @param progress  progress handle. Do not invoke any methods before
156
     *                  passing to this method. Start/progress/finish it
157
     *                  only in {@code operation}
158
     * @param includeDetailLabel  show progress detail label in the dialog
159
     * @param waitCursorAfter amount of time, in milliseconds, after which wait
160
     *                        cursor is shown
161
     * @param dialogAfter amount of time, in milliseconds, after which dialog
162
     *                    is shown
163
     * 
164
     * @since 1.30
165
     */
166
    public static void runOffEventThreadWithProgressDialog(
167
            final Runnable operation,
168
            final String dialogTitle, 
169
            final ProgressHandle progress, 
170
            final boolean includeDetailLabel,
171
            int waitCursorAfter,
172
            int dialogAfter)
173
    {
174
        if (PROVIDER instanceof Progress2) {
175
            Progress2 p = (Progress2) PROVIDER;
176
            p.runOffEventThreadWithProgressDialog(operation, dialogTitle, progress, includeDetailLabel, waitCursorAfter, dialogAfter);
177
        } else {
178
            PROVIDER.runOffEventDispatchThread(operation, progress.getDisplayName(),
179
                    new AtomicBoolean(false),
180
                    true,
181
                    DISPLAY_WAIT_CURSOR_MS,
182
                    DISPLAY_DIALOG_MS);
183
        }
184
    }
185
186
    /**
187
     * Runs operation out of the event thread, blocking the whole UI. When
188
     * operation takes more than 1s, the method first displays wait cursor.
189
     * If operation will not end up in 3s interval, modal dialog with
190
     * {@code content} panel is shown.
191
     * If operation is marked with {@link org.openide.util.Cancellable} 
192
     * interface, cancel button is part of dialog and can be used to 
193
     * interrupt the operation.
194
     * 
195
     * @param operation  task to perform in the background
196
     * @param dialogTitle dialog title
197
     * @param content  panel to be shown in the dialog
198
     * @param waitCursorAfter amount of time, in milliseconds, after which wait
199
     *                        cursor is shown
200
     * @param dialogAfter amount of time, in milliseconds, after which dialog
201
     *                    is shown
202
     * 
203
     * @since 1.30
204
     */
205
    public static void runOffEventThreadWithCustomDialogContent(
206
            final Runnable operation,
207
            final String dialogTitle,
208
            final JPanel content,
209
            int waitCursorAfter,
210
            int dialogAfter)
211
    {
212
        if (PROVIDER instanceof Progress2) {
213
            Progress2 p = (Progress2) PROVIDER;
214
            p.runOffEventThreadWithCustomDialogContent(operation, dialogTitle, content, waitCursorAfter, dialogAfter);
215
        } else {
216
            PROVIDER.runOffEventDispatchThread(operation, 
217
                    dialogTitle, 
218
                    new AtomicBoolean(false),
219
                    true, 
220
                    DISPLAY_WAIT_CURSOR_MS,
221
                    DISPLAY_DIALOG_MS);
222
        }
223
    }
224
    
225
    /**
143
     * Show a modal progress dialog that blocks the main window, while running
226
     * Show a modal progress dialog that blocks the main window, while running
144
     * the passed runnable on a background thread.
227
     * the passed runnable on a background thread.
145
     * <p/>
228
     * <p/>
Lines 249-254 Link Here
249
    private static class Trivial implements RunOffEDTProvider {
332
    private static class Trivial implements RunOffEDTProvider {
250
        private static final RequestProcessor WORKER = new RequestProcessor(ProgressUtils.class.getName());
333
        private static final RequestProcessor WORKER = new RequestProcessor(ProgressUtils.class.getName());
251
334
335
        @Override
252
        public void runOffEventDispatchThread(Runnable operation, String operationDescr, AtomicBoolean cancelOperation, boolean waitForCanceled, int waitCursorAfter, int dialogAfter) {
336
        public void runOffEventDispatchThread(Runnable operation, String operationDescr, AtomicBoolean cancelOperation, boolean waitForCanceled, int waitCursorAfter, int dialogAfter) {
253
            if (SwingUtilities.isEventDispatchThread()) {
337
            if (SwingUtilities.isEventDispatchThread()) {
254
                Task t = WORKER.post(operation);
338
                Task t = WORKER.post(operation);
(-)a/api.progress/src/org/netbeans/modules/progress/spi/RunOffEDTProvider.java (+61 lines)
Lines 43-48 Link Here
43
43
44
import java.util.concurrent.Future;
44
import java.util.concurrent.Future;
45
import java.util.concurrent.atomic.AtomicBoolean;
45
import java.util.concurrent.atomic.AtomicBoolean;
46
import javax.swing.JPanel;
46
import org.netbeans.api.progress.ProgressHandle;
47
import org.netbeans.api.progress.ProgressHandle;
47
import org.netbeans.api.progress.ProgressRunnable;
48
import org.netbeans.api.progress.ProgressRunnable;
48
49
Lines 116-119 Link Here
116
         */
117
         */
117
        public <T> Future<T> showProgressDialogAndRunLater (ProgressRunnable<T> toRun, ProgressHandle handle, boolean includeDetailLabel);
118
        public <T> Future<T> showProgressDialogAndRunLater (ProgressRunnable<T> toRun, ProgressHandle handle, boolean includeDetailLabel);
118
    }
119
    }
120
        
121
    public interface Progress2 extends Progress {
122
        
123
        /**
124
         * Runs operation out of the event thread, blocking the whole UI. When
125
         * operation takes more than 1s, the method first displays wait cursor.
126
         * If operation will not end in 3s interval, modal dialog with
127
         * progress is shown up.
128
         * If operation is marked with {@link org.openide.util.Cancellable}
129
         * interface, cancel button is part of dialog and can be used 
130
         * to interrupt the operation.
131
         * 
132
         * @param operation  task to perform in the background
133
         * @param dialogTitle dialog title
134
         * @param progress  progress handle. Do not invoke any methods before
135
         *                  passing to this method. Start/progress/finish it
136
         *                  only in {@code operation}
137
         * @param includeDetailLabel  show progress detail label in the dialog
138
         * @param waitCursorAfter amount of time, in milliseconds, after which 
139
         *                        wait cursor is shown
140
         * @param dialogAfter amount of time, in milliseconds, after which 
141
         *                    dialog is shown
142
         * 
143
         * @since 1.30
144
         */
145
        public void runOffEventThreadWithProgressDialog(
146
            final Runnable operation, 
147
            final String operationDescr,
148
            final ProgressHandle progress, 
149
            final boolean includeDetailLabel,
150
            int waitCursorAfter,
151
            int dialogAfter);
152
153
154
        /**
155
         * Runs operation out of the event thread, blocking the whole UI. When
156
         * operation takes more than 1s, the method first displays wait cursor.
157
         * If operation will not end up in 3s interval, modal dialog with
158
         * {@code content} panel is shown.
159
         * If operation is marked with {@link org.openide.util.Cancellable} 
160
         * interface, cancel button is part of dialog and can be used to 
161
         * interrupt the operation.
162
         * 
163
         * @param operation  task to perform in the background
164
         * @param dialogTitle dialog title
165
         * @param content  panel to be shown in the dialog
166
         * @param waitCursorAfter amount of time, in milliseconds, after which 
167
         *                        wait cursor is shown
168
         * @param dialogAfter amount of time, in milliseconds, after which 
169
         *                    dialog is shown
170
         * 
171
         * @since 1.30
172
         */
173
        public void runOffEventThreadWithCustomDialogContent(
174
            final Runnable operation,
175
            final String dialogTitle,
176
            final JPanel content,
177
            int waitCursorAfter,
178
            int dialogAfter);
179
        }
119
}
180
}
(-)a/progress.ui/nbproject/project.xml (-1 / +1 lines)
Lines 11-17 Link Here
11
                    <compile-dependency/>
11
                    <compile-dependency/>
12
                    <run-dependency>
12
                    <run-dependency>
13
                        <release-version>1</release-version>
13
                        <release-version>1</release-version>
14
                        <specification-version>1.19</specification-version>
14
                        <specification-version>1.30</specification-version>
15
                    </run-dependency>
15
                    </run-dependency>
16
                </dependency>
16
                </dependency>
17
                <dependency>
17
                <dependency>
(-)a/progress.ui/src/org/netbeans/modules/progress/ui/RunOffEDTImpl.java (-7 / +196 lines)
Lines 64-77 Link Here
64
import org.netbeans.api.progress.ProgressUtils;
64
import org.netbeans.api.progress.ProgressUtils;
65
import org.netbeans.modules.progress.spi.RunOffEDTProvider;
65
import org.netbeans.modules.progress.spi.RunOffEDTProvider;
66
import org.netbeans.modules.progress.spi.RunOffEDTProvider.Progress;
66
import org.netbeans.modules.progress.spi.RunOffEDTProvider.Progress;
67
import org.netbeans.modules.progress.spi.RunOffEDTProvider.Progress2;
67
import org.openide.DialogDescriptor;
68
import org.openide.DialogDescriptor;
68
import org.openide.DialogDisplayer;
69
import org.openide.DialogDisplayer;
69
import org.openide.NotifyDescriptor;
70
import org.openide.NotifyDescriptor;
70
import org.openide.util.Cancellable;
71
import org.openide.util.*;
71
import org.openide.util.Exceptions;
72
import org.openide.util.NbBundle;
73
import org.openide.util.Parameters;
74
import org.openide.util.RequestProcessor;
75
import org.openide.util.RequestProcessor.Task;
72
import org.openide.util.RequestProcessor.Task;
76
import org.openide.util.lookup.ServiceProvider;
73
import org.openide.util.lookup.ServiceProvider;
77
import org.openide.windows.WindowManager;
74
import org.openide.windows.WindowManager;
Lines 81-89 Link Here
81
 * @author Jan Lahoda, Tomas Holy
78
 * @author Jan Lahoda, Tomas Holy
82
 */
79
 */
83
@ServiceProvider(service=RunOffEDTProvider.class, position = 100)
80
@ServiceProvider(service=RunOffEDTProvider.class, position = 100)
84
public class RunOffEDTImpl implements RunOffEDTProvider, Progress {
81
public class RunOffEDTImpl implements RunOffEDTProvider, Progress, Progress2 {
85
82
86
    private static final RequestProcessor WORKER = new RequestProcessor(ProgressUtils.class.getName());
83
    private static final RequestProcessor WORKER = new RequestProcessor(ProgressUtils.class.getName());
84
    private static final RequestProcessor TI_WORKER = new RequestProcessor("TI_" + ProgressUtils.class.getName(), 1, true);
85
    
87
    private static final Map<String, Long> CUMULATIVE_SPENT_TIME = new HashMap<String, Long>();
86
    private static final Map<String, Long> CUMULATIVE_SPENT_TIME = new HashMap<String, Long>();
88
    private static final Map<String, Long> MAXIMAL_SPENT_TIME = new HashMap<String, Long>();
87
    private static final Map<String, Long> MAXIMAL_SPENT_TIME = new HashMap<String, Long>();
89
    private static final Map<String, Integer> INVOCATION_COUNT = new HashMap<String, Integer>();
88
    private static final Map<String, Integer> INVOCATION_COUNT = new HashMap<String, Integer>();
Lines 216-225 Link Here
216
            }
215
            }
217
        }
216
        }
218
    }
217
    }
218
    
219
    @Override
220
    public void runOffEventThreadWithCustomDialogContent(Runnable operation, String dialogTitle, JPanel content, int waitCursorAfter, int dialogAfter)
221
    {
222
        runOffEventThreadCustomDialogImpl(operation, dialogTitle, content, waitCursorAfter, dialogAfter);
223
    }
219
224
225
    @Override
226
    public void runOffEventThreadWithProgressDialog(final Runnable operation, final String operationDescr,
227
            ProgressHandle handle, boolean includeDetailLabel, int waitCursorAfter, int dialogAfter) {
228
        JPanel content = contentPanel(handle, includeDetailLabel);
229
        runOffEventThreadCustomDialogImpl(operation, operationDescr, content, waitCursorAfter, dialogAfter);
230
    }
231
    
232
    private void runOffEventThreadCustomDialogImpl(final Runnable operation, final String operationDescr,
233
            final JPanel contentPanel, int waitCursorAfter, int dialogAfter) {
234
        if (waitCursorAfter < 0) waitCursorAfter = 1000;
235
        if (dialogAfter < 0) dialogAfter = 2000;
236
        
237
        final CountDownLatch latch = new CountDownLatch(1);
238
        final AtomicReference<Dialog> d = new AtomicReference<Dialog>();
239
        final AtomicReference<RequestProcessor.Task> t  = new AtomicReference<RequestProcessor.Task>();
240
        
241
        JDialog dialog = createModalDialog(operation, operationDescr, contentPanel, d, t, operation instanceof Cancellable);
242
243
        final Task rt = TI_WORKER.post(new Runnable() {
244
245
            public @Override void run() {
246
		try {
247
		    operation.run();
248
		} finally {
249
		    latch.countDown();
250
251
		    SwingUtilities.invokeLater(new Runnable() {
252
253
			public @Override void run() {
254
			    Dialog dd = d.get();
255
			    if (dd != null) {
256
				dd.setVisible(false);
257
				dd.dispose();
258
			    }
259
			}
260
		    });
261
		}
262
            }
263
        });
264
        t.set(rt);
265
266
        Window window = null;
267
        Component glassPane = null;
268
        Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
269
        if (focusOwner != null) {
270
            window = SwingUtilities.getWindowAncestor(focusOwner);
271
            if (window != null) {
272
                RootPaneContainer root = (RootPaneContainer) SwingUtilities.getAncestorOfClass(RootPaneContainer.class, focusOwner);
273
                glassPane = root.getGlassPane();   
274
            }
275
        } 
276
        if (window == null || glassPane == null) {
277
            window = WindowManager.getDefault().getMainWindow();
278
            glassPane = ((JFrame) window).getGlassPane();
279
        }
280
        if (waitMomentarily(glassPane, null, waitCursorAfter, latch, window)) {
281
            return;
282
        }
283
284
        Cursor wait = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
285
286
        if (waitMomentarily(glassPane, wait, dialogAfter, latch, window)) {
287
            return;
288
        }
289
290
        d.set(dialog);
291
        if (EventQueue.isDispatchThread()) {
292
            EventQueue.invokeLater(new Runnable() {
293
                @Override
294
                public void run() {
295
                    d.get().setVisible(true);
296
                }
297
            });
298
        } else {
299
            d.get().setVisible(true);
300
        }
301
    }
302
    
220
    private static boolean waitMomentarily(Component glassPane, Cursor wait, int timeout, final CountDownLatch l, Window window) {
303
    private static boolean waitMomentarily(Component glassPane, Cursor wait, int timeout, final CountDownLatch l, Window window) {
221
        Cursor originalWindow = window.getCursor();
304
        Cursor originalWindow = window.getCursor();
222
        Cursor originalGlass = glassPane.getCursor();
305
        Cursor originalGlass = glassPane.getCursor();    
223
306
224
        try {
307
        try {
225
            if (wait != null) {
308
            if (wait != null) {
Lines 412-415 Link Here
412
        }
495
        }
413
496
414
    }
497
    }
498
499
    private static JPanel contentPanel(final ProgressHandle handle, boolean includeDetail) {
500
        // top panel
501
        JPanel contentPanel = new JPanel(new GridBagLayout());
502
        
503
        // main label
504
        GridBagConstraints gridBagConstraints = new GridBagConstraints();
505
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 0);
506
        gridBagConstraints.gridx = 0;
507
        gridBagConstraints.gridy = 0;
508
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
509
        
510
        JLabel mainLabel = ProgressHandleFactory.createMainLabelComponent(handle);
511
        Font f = mainLabel.getFont();
512
        if (f != null) {
513
            mainLabel.setFont(f.deriveFont(Font.BOLD));
514
        }
515
        contentPanel.add(mainLabel, gridBagConstraints);
516
        
517
        // progress bar
518
        gridBagConstraints = new java.awt.GridBagConstraints();
519
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 0);
520
        gridBagConstraints.gridx = 0;
521
        gridBagConstraints.gridy = 1;
522
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
523
        JComponent progressBar = ProgressHandleFactory.createProgressComponent(handle);
524
        contentPanel.add (progressBar, gridBagConstraints);
525
        
526
        if (includeDetail) {
527
            gridBagConstraints = new java.awt.GridBagConstraints();
528
            gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 0);
529
            gridBagConstraints.gridx = 0;
530
            gridBagConstraints.gridy = 2;
531
            gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
532
            JLabel details = ProgressHandleFactory.createDetailLabelComponent(handle);
533
            contentPanel.add(details, gridBagConstraints);
534
        }
535
        
536
        // empty panel - for correct resizing
537
        JPanel emptyPanel = new JPanel();
538
        gridBagConstraints = new java.awt.GridBagConstraints();
539
        gridBagConstraints.gridx = 0;
540
        gridBagConstraints.gridy = includeDetail ? 3 : 2;
541
        gridBagConstraints.weighty = 2.0;
542
        gridBagConstraints.weightx = 2.0;
543
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
544
        contentPanel.add(emptyPanel, gridBagConstraints);
545
        
546
        return contentPanel;
547
    }
548
    
549
    private static JDialog createModalDialog(
550
            final Runnable operation,
551
            final String title,
552
            final JPanel content,
553
            final AtomicReference<Dialog> d,
554
            final AtomicReference<RequestProcessor.Task> task,
555
            final boolean cancelAvail) 
556
    {
557
        assert EventQueue.isDispatchThread();
558
        
559
        JPanel panel = new JPanel(new GridBagLayout());
560
        
561
        GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints();
562
        gridBagConstraints.gridx = 0;
563
        gridBagConstraints.gridy = 0;
564
        gridBagConstraints.weightx = 1.0;
565
        gridBagConstraints.weighty = 1.0;
566
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
567
        
568
        panel.add(content, gridBagConstraints);
569
        
570
        if (cancelAvail) {
571
            JPanel buttonsPanel = new JPanel();
572
            buttonsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
573
            String cancelButton = NbBundle.getMessage(RunOffEDTImpl.class, "RunOffAWT.BTN_Cancel"); //NOI18N
574
            JButton cancel = new JButton(cancelButton);
575
            cancel.addActionListener(new ActionListener() {
576
577
                @Override
578
                public void actionPerformed(ActionEvent e) {
579
                    if (operation instanceof Cancellable) {
580
                        ((Cancellable) operation).cancel();
581
                        task.get().cancel();
582
                        d.get().setVisible(false);
583
                        d.get().dispose();
584
                    }
585
                }
586
            });
587
            buttonsPanel.add(cancel);
588
            gridBagConstraints = new java.awt.GridBagConstraints();
589
            gridBagConstraints.gridx = 0;
590
            gridBagConstraints.gridy = 1;
591
            gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
592
            gridBagConstraints.weightx = 1.0;
593
            panel.add(buttonsPanel, gridBagConstraints);
594
        }
595
        
596
        Frame mainWindow = WindowManager.getDefault().getMainWindow();
597
        final JDialog result = new JDialog(mainWindow, title, true);
598
        result.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
599
        result.setSize(400, 150);
600
        result.setContentPane(panel);
601
        result.setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
602
        return result;
603
    }
415
}
604
}

Return to bug 204553