diff --git a/api.progress/apichanges.xml b/api.progress/apichanges.xml --- a/api.progress/apichanges.xml +++ b/api.progress/apichanges.xml @@ -106,6 +106,30 @@ + + SPI added + + + + + +

+ Added methods to ProgressUtils for invoking a runnable or + similar with + a modal dialog blocking the UI and showing progress: +

    +
  • public static void showProgressDialogAndRun(Runnable operation, String displayName)
  • +
  • public static T showProgressDialogAndRun(final ProgressRunnable operation, final String displayName, boolean includeDetailLabel)
  • +
  • public static void showProgressDialogAndRun(Runnable operation, ProgressHandle progress, boolean includeDetailLabel
  • +
+ Added interface ProgressRunnable for performing background + work, and an SPI class ProgressRunOffEdtProvider which is + implemented by the Progress UI module. +

+
+ +
+ SPI added diff --git a/api.progress/manifest.mf b/api.progress/manifest.mf --- a/api.progress/manifest.mf +++ b/api.progress/manifest.mf @@ -3,5 +3,5 @@ OpenIDE-Module-Localizing-Bundle: org/netbeans/progress/module/resources/Bundle.properties OpenIDE-Module-Recommends: org.netbeans.modules.progress.spi.ProgressUIWorkerProvider, org.netbeans.modules.progress.spi.RunOffEDTProvider AutoUpdate-Essential-Module: true -OpenIDE-Module-Specification-Version: 1.18 +OpenIDE-Module-Specification-Version: 1.19 diff --git a/api.progress/src/org/netbeans/api/progress/ProgressHandle.java b/api.progress/src/org/netbeans/api/progress/ProgressHandle.java --- a/api.progress/src/org/netbeans/api/progress/ProgressHandle.java +++ b/api.progress/src/org/netbeans/api/progress/ProgressHandle.java @@ -197,6 +197,12 @@ LOG.fine(newDisplayName); internal.requestDisplayNameChange(newDisplayName); } + + String getDisplayName() { + synchronized (internal) { + return internal.getDisplayName(); + } + } /** * have the component in custom location, don't include in the status bar. diff --git a/api.progress/src/org/netbeans/api/progress/ProgressRunnable.java b/api.progress/src/org/netbeans/api/progress/ProgressRunnable.java new file mode 100644 --- /dev/null +++ b/api.progress/src/org/netbeans/api/progress/ProgressRunnable.java @@ -0,0 +1,56 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.api.progress; + +/** + * Callable used by ProgressUtils.showProgressDialogAndRun to do background + * work while a modal progress dialog is shown blocking all application windows. + * + * @author Tim Boudreau + */ +public interface ProgressRunnable { + /** + * Perform the background work + * @param handle A progress handle to post background work progress from. + * The handle, when passed in, has had start() and setToIndeterminate() called. + * @return The result of the background computation + */ + public T run(ProgressHandle handle); +} diff --git a/api.progress/src/org/netbeans/api/progress/ProgressUtils.java b/api.progress/src/org/netbeans/api/progress/ProgressUtils.java --- a/api.progress/src/org/netbeans/api/progress/ProgressUtils.java +++ b/api.progress/src/org/netbeans/api/progress/ProgressUtils.java @@ -40,7 +40,9 @@ package org.netbeans.api.progress; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import javax.swing.SwingUtilities; +import org.netbeans.modules.progress.spi.ProgressRunOffEdtProvider; import org.netbeans.modules.progress.spi.RunOffEDTProvider; import org.openide.util.Lookup; import org.openide.util.RequestProcessor; @@ -100,6 +102,100 @@ PROVIDER.runOffEventDispatchThread(operation, operationDescr, cancelOperation, waitForCanceled, waitCursorAfter, dialogAfter); } + /** + * Show a modal progress dialog that blocks the main window, while running + * the passed runnable on a background thread. + *

+ * This method is thread-safe, and will block until the operation has + * completed, regardless of what thread calls this method. + *

+ * Unless you are being passed the runnable or progress handle from foreign + * code (such as in WizardDescriptor.progressInstantiatingIterator), it + * is usually simpler to use the version of this method that takes a + * ProgressCallable. + * + * @param operation A runnable to run in the background + * @param progress A progress handle to create a progress bar for + * @param includeDetailLabel True if the caller will use + * ProgressHandle.progress (String, int), false if not. If true, the + * created dialog will include a label that shows progress details. + */ + public static void showProgressDialogAndRun(Runnable operation, ProgressHandle progress, boolean includeDetailLabel) { + if (PROVIDER instanceof ProgressRunOffEdtProvider) { + ProgressRunOffEdtProvider p = (ProgressRunOffEdtProvider) PROVIDER; + p.showProgressDialogAndRun(operation, progress, includeDetailLabel); + } else { + PROVIDER.runOffEventDispatchThread(operation, progress.getDisplayName(), new AtomicBoolean(false), false, 0, 0); + } + } + + /** + * Show a modal progress dialog that blocks the main window, while running + * the passed runnable on a background thread. + *

+ * This method is thread-safe, and will block until the operation has + * completed, regardless of what thread calls this method. + * + * @param The result type - use Void if no return type needed + * @param operation A runnable-like object which performs work in the + * background, and is passed a ProgressHandle to update progress + * @param displayName The display name for this operation + * @param includeDetailLabel If true, include a lable to show progress + * details (needed only if you plan to call ProgressHandle.setProgress(String, int) + * @return The result of the operation. + */ + public static T showProgressDialogAndRun(final ProgressRunnable operation, final String displayName, boolean includeDetailLabel) { + if (PROVIDER instanceof ProgressRunOffEdtProvider) { + ProgressRunOffEdtProvider p = (ProgressRunOffEdtProvider) PROVIDER; + return p.showProgressDialogAndRun(operation, displayName, includeDetailLabel); + } else { + final AtomicReference ref = new AtomicReference(); + PROVIDER.runOffEventDispatchThread(new Runnable() { + @Override + public void run() { + ProgressHandle handle = ProgressHandleFactory.createHandle(displayName); + handle.start(); + handle.switchToIndeterminate(); + try { + ref.set(operation.run(handle)); + } finally { + handle.finish(); + } + } + }, displayName, new AtomicBoolean(false), true, 0, 0); + return ref.get(); + } + } + + /** + * Show a modal progress dialog that blocks the main window, while running + * the passed runnable on a background thread with an indeterminate-state + * progress bar. + *

+ * This method is thread-safe, and will block until the operation has + * completed, regardless of what thread calls this method. + * . + * @param operation A runnable to run + * @param displayName The display name of the operation, to show in the dialog + */ + public static void showProgressDialogAndRun(Runnable operation, String displayName) { + showProgressDialogAndRun(new RunnableWrapper(operation), displayName, false); + } + + + private static final class RunnableWrapper implements ProgressRunnable { + private final Runnable toRun; + RunnableWrapper(Runnable toRun) { + this.toRun = toRun; + } + + @Override + public Void run(ProgressHandle handle) { + toRun.run(); + return null; + } + } + private static class Trivial implements RunOffEDTProvider { private static final RequestProcessor WORKER = new RequestProcessor(ProgressUtils.class.getName()); diff --git a/api.progress/src/org/netbeans/modules/progress/spi/ProgressRunOffEdtProvider.java b/api.progress/src/org/netbeans/modules/progress/spi/ProgressRunOffEdtProvider.java new file mode 100644 --- /dev/null +++ b/api.progress/src/org/netbeans/modules/progress/spi/ProgressRunOffEdtProvider.java @@ -0,0 +1,79 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.progress.spi; + +import org.netbeans.api.progress.ProgressRunnable; +import org.netbeans.api.progress.ProgressHandle; + +/** + * Extension to RunOffEDTProvider which allows for a modal dialog to + * be shown which contains a progress bar. + * + * @author Tim Boudreau + */ +public interface ProgressRunOffEdtProvider extends RunOffEDTProvider{ + /** + * Show a modal progress dialog that blocks the main window while running + * a background process. This call should block until the work is + * completed. + * + * @param operation A runnable that needs to be run with the UI blocked + * @param handle A progress handle that will be updated to reflect + * the progress of the operation + * @param showDetails If true, a label should be provided in the progress + * dialog to show detailed progress messages + */ + void showProgressDialogAndRun(Runnable operation, ProgressHandle handle, boolean showDetails); + /** + * Show a modal progress dialog that blocks the main window while running + * a background process. This call should block until the work is + * completed. + * + * @param The type of the return value + * @param toRun A ProgressCallable which will be passed a progress handle + * on a background thread, can do its work and (optionally) return a value + * @param displayName The display name of the work being done + * @param includeDetailLabel Show the detail levels. Set to true if the + * caller will use ProgressHandle.progress (String, int) to provide + * detailed progress messages + * @return The result of the call to ProgressRunnable.call() + */ + T showProgressDialogAndRun(ProgressRunnable toRun, String displayName, boolean includeDetailLabel); +} diff --git a/progress.ui/manifest.mf b/progress.ui/manifest.mf --- a/progress.ui/manifest.mf +++ b/progress.ui/manifest.mf @@ -4,5 +4,5 @@ OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/progress/ui/Bundle.properties OpenIDE-Module-Provides: org.netbeans.modules.progress.spi.ProgressUIWorkerProvider, org.netbeans.modules.progress.spi.RunOffEDTProvider AutoUpdate-Essential-Module: true -OpenIDE-Module-Specification-Version: 1.8 +OpenIDE-Module-Specification-Version: 1.9 diff --git a/progress.ui/nbproject/project.xml b/progress.ui/nbproject/project.xml --- a/progress.ui/nbproject/project.xml +++ b/progress.ui/nbproject/project.xml @@ -11,7 +11,7 @@ 1 - 1.18 + 1.19 @@ -54,6 +54,10 @@ org.netbeans.libs.junit4 + + org.netbeans.modules.nbjunit + + diff --git a/progress.ui/src/org/netbeans/modules/progress/ui/RunOffEDTImpl.java b/progress.ui/src/org/netbeans/modules/progress/ui/RunOffEDTImpl.java --- a/progress.ui/src/org/netbeans/modules/progress/ui/RunOffEDTImpl.java +++ b/progress.ui/src/org/netbeans/modules/progress/ui/RunOffEDTImpl.java @@ -38,11 +38,24 @@ */ package org.netbeans.modules.progress.ui; +import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridLayout; +import java.awt.RenderingHints; +import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.CountDownLatch; @@ -51,24 +64,36 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JDialog; import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; +import org.netbeans.api.progress.ProgressRunnable; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.progress.ProgressUtils; import org.netbeans.modules.progress.spi.RunOffEDTProvider; +import org.netbeans.modules.progress.spi.ProgressRunOffEdtProvider; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.Parameters; import org.openide.util.RequestProcessor; +import org.openide.util.Utilities; import org.openide.windows.WindowManager; /** * Default RunOffEDTProvider implementation for ProgressUtils.runOffEventDispatchThread() methods - * @author Jan Lahoda, Tomas Holy + * @author Jan Lahoda, Tomas Holy, Tim Boudreau */ @org.openide.util.lookup.ServiceProvider(service = org.netbeans.modules.progress.spi.RunOffEDTProvider.class, position = 100) -public class RunOffEDTImpl implements RunOffEDTProvider { +public class RunOffEDTImpl implements RunOffEDTProvider, ProgressRunOffEdtProvider { private static final RequestProcessor WORKER = new RequestProcessor(ProgressUtils.class.getName()); private static final Map, Integer> OPERATIONS = new WeakHashMap, Integer>(); @@ -190,4 +215,239 @@ glassPane.setCursor(original); } } + + @Override + public void showProgressDialogAndRun(Runnable operation, ProgressHandle handle, boolean includeDetailLabel) { + AbstractWindowRunner wr = new RunnableWindowRunner(operation, handle, includeDetailLabel); + wr.start(); + if (!EventQueue.isDispatchThread()) { + try { + wr.await(); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + } + } + + @Override + public T showProgressDialogAndRun(ProgressRunnable toRun, String displayName, boolean includeDetailLabel) { + AbstractWindowRunner wr = new ProgressBackgroundRunner(toRun, displayName, includeDetailLabel); + wr.start(); + if (!EventQueue.isDispatchThread()) { + try { + wr.await(); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + } + return wr.getResult(); + } + + private static abstract class AbstractWindowRunner extends WindowAdapter implements Runnable { + private volatile JDialog dlg; + private final boolean includeDetail; + protected final ProgressHandle handle; + private final CountDownLatch latch = new CountDownLatch(1); + private volatile T operationResult; + + AbstractWindowRunner(ProgressHandle handle, boolean includeDetail) { + this.includeDetail = includeDetail; + this.handle = handle; + } + + @Override + public final void windowOpened(WindowEvent e) { + dlg = (JDialog) e.getSource(); + RequestProcessor.getDefault().post(this); + grayOutMainWindow(); + } + + @Override + public final void windowClosed(WindowEvent e) { + ungrayMainWindow(); + latch.countDown(); + } + + final void await() throws InterruptedException { + latch.await(); + } + + final void start() { + if (EventQueue.isDispatchThread()) { + createModalProgressDialog(handle, includeDetail); + } else { + CountDownLatch dlgLatch = new CountDownLatch(1); + DialogCreator dc = new DialogCreator(dlgLatch); + EventQueue.invokeLater (dc); + try { + dlgLatch.await(); + } catch (InterruptedException ex) { + throw new IllegalStateException(ex); + } + } + } + + protected abstract T runBackground(); + + T getResult() { + return operationResult; + } + + @Override + public void run() { + if (!EventQueue.isDispatchThread()) { + try { + operationResult = runBackground(); + } finally { + EventQueue.invokeLater(this); + } + } else { + dlg.setVisible(false); + dlg.dispose(); + } + } + + private final class DialogCreator implements Runnable { + private final CountDownLatch latch; + DialogCreator (CountDownLatch latch) { + this.latch = latch; + } + + @Override + public void run() { + createModalProgressDialog(handle, includeDetail); + latch.countDown(); + } + } + + private JDialog createModalProgressDialog(ProgressHandle handle, boolean includeDetail) { + assert EventQueue.isDispatchThread(); + int edgeGap = Utilities.isMac() ? 12 : 8; + int compGap = Utilities.isMac() ? 9 : 5; + JPanel panel = new JPanel(new GridLayout(includeDetail ? 3 : 2, 1, compGap, compGap)); + JLabel mainLabel = ProgressHandleFactory.createMainLabelComponent(handle); + Font f = mainLabel.getFont(); + if (f != null) { + mainLabel.setFont (f.deriveFont(Font.BOLD)); + } + panel.add(mainLabel); + + JComponent progressBar = ProgressHandleFactory.createProgressComponent(handle); + progressBar.setMinimumSize(new Dimension (400, 32)); + panel.add(progressBar); + + if (includeDetail) { + JLabel details = ProgressHandleFactory.createDetailLabelComponent(handle); + details.setMinimumSize(new Dimension(300, 16)); + panel.add(details); + } + panel.setBorder (BorderFactory.createCompoundBorder( + BorderFactory.createRaisedBevelBorder(), + BorderFactory.createEmptyBorder(edgeGap, edgeGap, edgeGap, edgeGap))); + panel.setMinimumSize(new Dimension(400, 100)); + Frame mainWindow = WindowManager.getDefault().getMainWindow(); + JDialog result = new JDialog(mainWindow, true); + result.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + result.setUndecorated(true); + result.setSize(400, 100); + result.getContentPane().setLayout(new BorderLayout()); + result.getContentPane().add(panel, BorderLayout.CENTER); + result.pack(); + int reqWidth = result.getWidth(); + result.setSize(Math.max (reqWidth, + mainWindow instanceof JFrame ? + ((JFrame) mainWindow).getContentPane().getWidth() / 3 : + mainWindow.getWidth()), result.getHeight()); + result.setLocationRelativeTo(WindowManager.getDefault().getMainWindow()); + result.addWindowListener(this); + result.setVisible(true); + return result; + } + + private Component oldGlassPane; + private void grayOutMainWindow() { + assert EventQueue.isDispatchThread(); + Frame f = WindowManager.getDefault().getMainWindow(); + if (f instanceof JFrame) { + Map hintsMap = (Map)(Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints")); //NOI18N + //Avoid translucent painting on, for example, remote X terminal + if (hintsMap == null || !RenderingHints.VALUE_TEXT_ANTIALIAS_OFF.equals(hintsMap.get(RenderingHints.KEY_TEXT_ANTIALIASING))) { + JFrame jf = (JFrame) f; + TranslucentMask mask = new TranslucentMask(); + oldGlassPane = jf.getGlassPane(); + jf.setGlassPane(mask); + mask.setVisible(true); + mask.setBounds (0, 0, jf.getContentPane().getWidth(), jf.getContentPane().getHeight()); + mask.invalidate(); + mask.revalidate(); + mask.repaint(); + jf.getRootPane().paintImmediately(0, 0, jf.getRootPane().getWidth(), jf.getRootPane().getHeight()); + } + } + } + + private void ungrayMainWindow() { + if (oldGlassPane != null) { + JFrame jf = (JFrame) WindowManager.getDefault().getMainWindow(); + jf.setGlassPane(oldGlassPane); + jf.invalidate(); + jf.repaint(); + } + } + } + + static final class TranslucentMask extends JComponent { //pkg private for tests + TranslucentMask() { + setVisible(false); //so we will trigger a property change + } + + @Override + public boolean isOpaque() { + return false; + } + + @Override + public void paint (Graphics g) { + Graphics2D g2d = (Graphics2D) g; + Color translu = new Color (180, 180, 180, 148); + g2d.setColor(translu); + g2d.fillRect (0, 0, getWidth(), getHeight()); + } + } + + private static final class ProgressBackgroundRunner extends AbstractWindowRunner { + private final ProgressRunnable toRun; + public ProgressBackgroundRunner(ProgressRunnable toRun, String displayName, boolean includeDetail) { + super (ProgressHandleFactory.createHandle(displayName), includeDetail); + this.toRun = toRun; + } + + @Override + protected T runBackground() { + handle.start(); + handle.switchToIndeterminate(); + T result; + try { + result = toRun.run(handle); + } finally { + handle.finish(); + } + return result; + } + } + + private static final class RunnableWindowRunner extends AbstractWindowRunner { + private Runnable toRun; + public RunnableWindowRunner(Runnable toRun, ProgressHandle progress, boolean includeDetail) { + super (progress, includeDetail); + this.toRun = toRun; + } + + @Override + protected Void runBackground() { + toRun.run(); + return null; + } + } + } diff --git a/progress.ui/test/unit/src/org/netbeans/modules/progress/ui/RunOffEDTImplTest.java b/progress.ui/test/unit/src/org/netbeans/modules/progress/ui/RunOffEDTImplTest.java new file mode 100644 --- /dev/null +++ b/progress.ui/test/unit/src/org/netbeans/modules/progress/ui/RunOffEDTImplTest.java @@ -0,0 +1,310 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.progress.ui; + +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.beans.PropertyChangeListener; +import java.util.Map; +import java.util.Set; +import javax.swing.Action; +import javax.swing.JFrame; +import org.netbeans.api.progress.ProgressHandle; +import org.openide.nodes.Node; +import org.openide.util.Exceptions; +import org.openide.windows.Mode; +import org.openide.windows.TopComponent; +import org.openide.windows.TopComponentGroup; +import org.openide.windows.WindowManager; +import org.junit.After; +import static org.junit.Assert.*; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.api.progress.ProgressRunnable; +import org.netbeans.api.progress.ProgressUtils; +import org.netbeans.junit.MockServices; +import org.openide.windows.Workspace; + +/** + * + * @author Tim Boudreau + */ +public class RunOffEDTImplTest { + + public RunOffEDTImplTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + MockServices.setServices(WM.class, RunOffEDTImpl.class); + } + + @After + public void tearDown() { + } + + private static boolean canTestGlassPane() { + Map hintsMap = (Map)(Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints")); //NOI18N + //Avoid translucent painting on, for example, remote X terminal + return hintsMap == null || !RenderingHints.VALUE_TEXT_ANTIALIAS_OFF.equals(hintsMap.get(RenderingHints.KEY_TEXT_ANTIALIASING)); + } + + @Test + public void testShowProgressDialogAndRun_3args_1_EQ() throws Exception { + EventQueue.invokeAndWait(new Runnable() { + + @Override + public void run() { + testShowProgressDialogAndRun_3args_1(); + } + }); + } + + @Test + public void testShowProgressDialogAndRun_3args_2_EQ() throws Exception { + EventQueue.invokeAndWait(new Runnable() { + + @Override + public void run() { + testShowProgressDialogAndRun_3args_2(); + } + }); + } + + @Test + public void testShowProgressDialogAndRun_3args_1() { + assertEquals ("Done", ProgressUtils.showProgressDialogAndRun(new CB(), "Doing Stuff", true)); + } + + @Test + public void testShowProgressDialogAndRun_3args_2() { + final JFrame jf = (JFrame) WindowManager.getDefault().getMainWindow(); + class R implements Runnable { + boolean hasRun; + public void run() { + try { + Thread.sleep(200); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + if (canTestGlassPane()) { + assertTrue (jf.getGlassPane() instanceof RunOffEDTImpl.TranslucentMask); + } + hasRun = true; + } + }; + R r = new R(); + ProgressUtils.showProgressDialogAndRun(r, "Something"); + assertTrue (r.hasRun); + } + + private class CB implements ProgressRunnable { + + @Override + public String run(ProgressHandle handle) { + handle.switchToDeterminate(5); + for (int i= 0; i < 5; i++) { + try { + Thread.sleep(200); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + handle.progress("Job " + i, i); + } + return "Done"; + } + + } + + public static final class WM extends WindowManager { + private final JFrame jf = new JFrame ("Main Window"); + public WM() { + jf.setVisible(true); + } + + @Override + public Mode findMode(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Mode findMode(TopComponent tc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set getModes() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Frame getMainWindow() { + return jf; + } + + @Override + public void updateUI() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected Component createTopComponentManager(TopComponent c) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Workspace createWorkspace(String name, String displayName) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Workspace findWorkspace(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Workspace[] getWorkspaces() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void setWorkspaces(Workspace[] workspaces) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Workspace getCurrentWorkspace() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TopComponentGroup findTopComponentGroup(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener l) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener l) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentOpen(TopComponent tc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentClose(TopComponent tc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentRequestActive(TopComponent tc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentRequestVisible(TopComponent tc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentDisplayNameChanged(TopComponent tc, String displayName) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentHtmlDisplayNameChanged(TopComponent tc, String htmlDisplayName) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentToolTipChanged(TopComponent tc, String toolTip) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentIconChanged(TopComponent tc, Image icon) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected void topComponentActivatedNodesChanged(TopComponent tc, Node[] activatedNodes) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected boolean topComponentIsOpened(TopComponent tc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected Action[] topComponentDefaultActions(TopComponent tc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected String topComponentID(TopComponent tc, String preferredID) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TopComponent findTopComponent(String tcID) { + throw new UnsupportedOperationException("Not supported yet."); + } + } +}