/** * Copyright (c) 2002-2005, Simone Bordet * All rights reserved. * * This software is distributable under the BSD license. * See the terms of the BSD license in the documentation provided with this software. */ package foxtrot.examples; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import foxtrot.Job; import foxtrot.Worker; /** * An example of how to use progress indication with Foxtrot. * The main advantage is that there is no more need to create a separate thread * for the progressive operation, but just use the Foxtrot API. * And, of course, with Foxtrot the GUI can be interrupted in any moment. * * @version $Revision: 1.6 $ */ public class ProgressExample extends JFrame { private JButton button; private JProgressBar bar; private boolean running; private boolean taskInterrupted; public static void main(String[] args) { ProgressExample example = new ProgressExample(); example.setVisible(true); } public ProgressExample() { super("Foxtrot Example"); button = new JButton("Run Task !"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (running) onCancelClicked(); else onRunClicked(); } }); bar = new JProgressBar(); bar.setStringPainted(true); setDefaultCloseOperation(EXIT_ON_CLOSE); Container c = getContentPane(); c.setLayout(new BorderLayout(0, 0)); JPanel main = new JPanel(new BorderLayout(0, 0)); main.setBorder(new EmptyBorder(35, 35, 35, 35)); c.add(main, BorderLayout.CENTER); JPanel p = new JPanel(new BorderLayout(20, 20)); p.add(bar, BorderLayout.NORTH); p.add(button, BorderLayout.SOUTH); main.add(p); setSize(300, 200); Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); Dimension size = getSize(); int x = (screen.width - size.width) >> 1; int y = (screen.height - size.height) >> 1; setLocation(x, y); } private void onRunClicked() { // We are running running = true; // We just started, set the task as not interrupted, to // clear any eventual previous status setTaskInterrupted(false); // We will execute a long operation, change the text signaling // that the user can interrupt the operation button.setText("Cancel"); // getData() will block until the heavy operation is finished // and the AWT-Swing events will be dequeued and processed ArrayList list = getData(); // Restore the button's text button.setText("Run Task !"); // We're not running anymore running = false; // getData() finished or was interrupted ? // If was interrupted we get back a null list if (list != null) { // Task completed successfully, do whatever useful with the list // For example, populate a JComboBox // The reader will finish this part :) DefaultComboBoxModel model = new DefaultComboBoxModel(list.toArray()); } } private void onCancelClicked() { // Here if we want to interrupt the Task setTaskInterrupted(true); } private ArrayList getData() { return (ArrayList)Worker.post(new Job() { public Object run() { ArrayList list = new ArrayList(); StringBuffer buffer = new StringBuffer(); // A repetitive operation that updates a progress bar. int max = 20; for (int i = 1; i <= max; ++i) { // Simulate a heavy operation to retrieve data try { Thread.sleep(250); } catch (InterruptedException ignored) { } // Populate the data structure Object data = new Object(); list.add(data); // Prepare the progress bar string buffer.setLength(0); buffer.append("Step ").append(i).append(" of ").append(max); if (isTaskInterrupted()) { buffer.append(" - Interrupted !"); update(i, max, buffer.toString()); break; } else { // Update the progress bar update(i, max, buffer.toString()); } } if (isTaskInterrupted()) { // Task is interrupted, clean the half-populated data structure // and return from the Task list.clear(); return null; } else { return list; } } }); } private void update(final int index, final int max, final String string) { // This method is called by the Foxtrot Worker thread, but I want to // update the GUI, so I use SwingUtilities.invokeLater, as the Task // is not finished yet. SwingUtilities.invokeLater(new Runnable() { public void run() { bar.setMaximum(max); bar.setValue(index); bar.setString(string); } }); } private synchronized boolean isTaskInterrupted() { // Called from the Foxtrot Worker Thread. // Must be synchronized, since the variable taskInterrupted is accessed from 2 threads. // While it is easier just to change the variable value without synchronizing, it is possible // that the Foxtrot worker thread doesn't see the change (it may cache the value of the variable // in a registry). return taskInterrupted; } private synchronized void setTaskInterrupted(boolean value) { // Called from the AWT Event Dispatch Thread. // See comments above on why it must be synchronized. taskInterrupted = value; } }