/* * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved. * Copyright (C) 2011 Peransin Nicolas. * Use is subject to license terms. */ package examples; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.RoundRectangle2D; import javax.swing.InputVerifier; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JSeparator; import javax.swing.JToolBar; import javax.swing.Timer; import javax.swing.event.MouseInputAdapter; import javax.swing.event.MouseInputListener; import org.mypsycho.swing.app.Action; import org.mypsycho.swing.app.Application; import org.mypsycho.swing.app.ApplicationContext; import org.mypsycho.swing.app.ApplicationListener; import org.mypsycho.swing.app.FrameView; import org.mypsycho.swing.app.SingleFrameApplication; import org.mypsycho.swing.app.beans.StatusBar; import org.mypsycho.swing.app.beans.TaskMonitor; import org.mypsycho.swing.app.task.Task; import org.mypsycho.swing.app.task.Task.BlockingScope; import org.mypsycho.swing.app.task.Task.InputBlocker; import org.mypsycho.swing.app.utils.SwingHelper; /** * A demo of the {@code @Action} <i>block</i> options for background * task. It's an example of three of the {@code Action.Block} types: * <pre> * @Action(block = Task.BlockingScope.ACTION) * public Task blockAction() { ... } * * @Action(block = Task.BlockingScope.COMPONENT) * public Task blockComponent() { ... } * * @Action(block = Task.BlockingScope.WINDOW) * public Task blockWindow() { ... } * * @Action(block = Task.BlockingScope.APPLICATION) * public Task blockApplication() { ... } * </pre> * The first {@code BlockingScope.ACTION} {@code @Action} disables the * corresponding {@code Action} while {@code blockAction} method runs. * When you press the blockAction button or toolbar-button or menu * item you'll observe that all of the components are disabled. The * {@code BlockingScope.COMPONENT} version only disables the component * that triggered the action. The {@code Block.WINDOW} method * uses a custom {@link Task.InputBlocker inputBlocker} to * temporarily block input to the by making the window's * glass pane visible. And the {@code Task.BlockingScope.APPLICATION} * version pops up a modal dialog for the action's duration. * The blocking dialog's title/message/icon are defined by resources * from the ResourceBundle named {@code BlockingExample1}: * <pre> * BlockingDialog.title = Blocking Application * BlockingDialog.message = Please wait patiently ... * Action.BlockingDialog.icon = wait.png * </pre> * * <p> * All of the actions in this example just sleep for about 2 seconds, * while periodically updating their Task's message/progress properties. * * <p> * This class loads resources from the ResourceBundle called * {@code BlockingExample1}. It depends on the example {@code StatusBar} class. * * * @author Hans Muller (Hans.Muller@Sun.COM) * @see ApplicationContext * @see Application * @see Action * @see Task * @see TaskMonitor * @see StatusBar */ public class BlockingExample1 extends SingleFrameApplication { private StatusBar statusBar = null; private BusyIndicator busyIndicator = null; @Override protected void startup() { statusBar = new StatusBar(this, getContext().getTaskMonitor()); busyIndicator = new BusyIndicator(); // A MenuFrame is a better choice for an more realistic application JFrame f = new JFrame(); // frame.name is set f.setGlassPane(busyIndicator); SwingHelper h = new SwingHelper(f); h.add("toolbar", new JToolBar(), BorderLayout.PAGE_START); h.with("body", new BorderLayout(), BorderLayout.CENTER) .add("space", new JSeparator(), BorderLayout.PAGE_START) .with("buttons", new FlowLayout(FlowLayout.CENTER), BorderLayout.CENTER) .add("action", new JButton()) .add("component", new JButton()) .add("window", new JButton()) .add("application", new JButton()) .back() .back(); h.add("status", statusBar, BorderLayout.PAGE_END); show(new FrameView(this, f)); } /* Progress is interdeterminate for the first 150ms, then * run for another 7500ms, marking progress every 150ms. */ private class DoNothingTask extends Task<Void, Void> { DoNothingTask() { setUserCancellable(true); } @Override protected Void doInBackground() throws InterruptedException { for(int i = 0; i < 50; i++) { message("step", i); Thread.sleep(150L); setProgress(i, 0, 49); } Thread.sleep(150L); return null; } @Override protected void succeeded(Void ignored) { message("succeeded"); } @Override protected void cancelled() { message("cancelled"); } } @Action(block = BlockingScope.ACTION) public Task<?, ?> blockAction() { return new DoNothingTask(); } @Action(block = BlockingScope.COMPONENT) public Task<?, ?> blockComponent() { return new DoNothingTask(); } @Action(block = BlockingScope.WINDOW) public Task<?, ?> blockWindow() { return new DoNothingTask(); } @Action(block = BlockingScope.APPLICATION) public Task<?, ?> blockApplication() { Task<?, ?> task = new DoNothingTask(); task.setInputBlocker(new BusyIndicatorInputBlocker(task)); return task; } public static void main(String[] args) { Application app = new BlockingExample1(); app.addApplicationListener(ApplicationListener.console); app.launch(args); } /* This component is intended to be used as a GlassPane. It's * start method makes this component visible, consumes mouse * and keyboard input, and displays a spinning activity indicator * animation. The stop method makes the component not visible. * The code for rendering the animation was lifted from * org.jdesktop.swingx.painter.BusyPainter. I've made some * simplifications to keep the example small. */ @SuppressWarnings("serial") private static class BusyIndicator extends JComponent implements ActionListener { private int frame = -1; // animation frame index private final int nBars = 8; private final float barWidth = 6; private final float outerRadius = 28; private final float innerRadius = 12; private final int trailLength = 4; private final float barGray = 200f; // shade of gray, 0-255 private final Timer timer = new Timer(65, this); // 65ms = animation rate BusyIndicator() { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); MouseInputListener blockMouseEvents = new MouseInputAdapter() {}; addMouseMotionListener(blockMouseEvents); addMouseListener(blockMouseEvents); InputVerifier retainFocusWhileVisible = new InputVerifier() { public boolean verify(JComponent c) { return !c.isVisible(); } }; setInputVerifier(retainFocusWhileVisible); } public void actionPerformed(ActionEvent ignored) { frame += 1; repaint(); } void start() { setVisible(true); requestFocusInWindow(); timer.start(); } void stop() { setVisible(false); timer.stop(); } @Override protected void paintComponent(Graphics g) { RoundRectangle2D bar = new RoundRectangle2D.Float( innerRadius, -barWidth/2, outerRadius, barWidth, barWidth, barWidth); // x, y, width, height, arc width,arc height double angle = Math.PI * 2.0 / (double)nBars; // between bars Graphics2D g2d = (Graphics2D)g; g2d.translate(getWidth() / 2, getHeight() / 2); g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); for (int i = 0; i < nBars; i++) { // compute bar i's color based on the frame index Color barColor = new Color((int)barGray, (int)barGray, (int)barGray); if (frame != -1) { for(int t = 0; t < trailLength; t++) { if (i == ((frame - t + nBars) % nBars)) { float tlf = (float)trailLength; float pct = 1.0f - ((tlf - t) / tlf); int gray = (int)((barGray - (pct * barGray)) + 0.5f); barColor = new Color(gray, gray, gray); } } } // draw the bar g2d.setColor(barColor); g2d.fill(bar); g2d.rotate(angle); } } } private class BusyIndicatorInputBlocker extends InputBlocker { BusyIndicatorInputBlocker(Task<?,?> task) { super(task, Task.BlockingScope.WINDOW, busyIndicator); } @Override protected void block() { busyIndicator.start(); } @Override protected void unblock() { busyIndicator.stop(); } } }