package edu.kit.pse.ws2013.routekit.views;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Window;
import java.util.AbstractMap;
import java.util.ConcurrentModificationException;
import java.util.LinkedList;
import java.util.Map.Entry;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import edu.kit.pse.ws2013.routekit.models.ProgressListener;
import edu.kit.pse.ws2013.routekit.models.ProgressReporter;
/**
* A modal dialog that displays a progress bar and hides itself when the task is
* done.
* <p>
* Intended usage:
* <ol>
* <li>Initialize the {@link ProgressDialog}</li>
* <li>Initialize a {@link ProgressReporter}</li>
* <li>Register this dialog at the reporter via
* {@link ProgressReporter#addProgressListener(ProgressListener)}
* <li>Asynchronously start some task, passing it the reporter</li>
* <li>Make this dialog visible – this blocks until the task finishes</li>
* <li>Do what needs to be done after the task has completed</li>
* </ol>
*
* @author Lucas Werkmeister
*/
public class ProgressDialog extends JDialog implements ProgressListener {
private static final long serialVersionUID = 1L;
private final JProgressBar bar;
private final JTextArea text;
private final LinkedList<Entry<String, Long>> tasks = new LinkedList<>();
private boolean closed = false;
private float progress;
public ProgressDialog(Window owner) {
this(owner, false);
}
public ProgressDialog(Window owner, boolean cancelable) {
super(owner, "Berechne...");
setDefaultCloseOperation(cancelable ? DISPOSE_ON_CLOSE
: DO_NOTHING_ON_CLOSE);
setModal(true);
setResizable(false);
JPanel p = new JPanel(new BorderLayout());
bar = new JProgressBar();
bar.setStringPainted(true);
bar.setPreferredSize(new Dimension(500, 25));
p.add(bar, BorderLayout.CENTER);
text = new JTextArea(10, 50);
text.setEditable(false);
p.add(text, BorderLayout.SOUTH);
setContentPane(p);
pack();
setLocationRelativeTo(owner);
new Thread("ProgressDialog Refresh Thread") {
{
setDaemon(true);
}
@Override
public void run() {
setPriority(MIN_PRIORITY);
do {
repaint();
try {
sleep(1000);
} catch (InterruptedException e) {
// don’t care
}
} while (!closed);
};
}.start();
}
@Override
public void update(Graphics g) {
paint(g);
}
@Override
public void paint(Graphics g) {
String indent = "";
long currentTime = System.currentTimeMillis();
text.setText(null);
try {
for (Entry<String, Long> task : tasks) {
text.append(indent);
indent += " ";
text.append(task.getKey());
int elapsed = (int) ((currentTime - task.getValue()) / 1000);
if (elapsed > 0) {
text.append(" (");
text.append(Integer.toString(elapsed));
text.append("s)");
}
text.append("\n");
}
} catch (ConcurrentModificationException e) {
// don’t care
repaint();
}
super.paint(g);
}
private void updateTitle(String name) {
if (name == null) {
setTitle("Fertig.");
} else {
setTitle(name + "... " + (int) (progress * 100) + "%");
}
}
private void checkCanceled() {
if (closed) {
throw new ThreadDeath();
}
}
@Override
public void startRoot(String name) {
checkCanceled();
bar.setString(name);
}
@Override
public void beginTask(String name) {
checkCanceled();
bar.setString(name);
updateTitle(name);
tasks.addLast(new AbstractMap.SimpleEntry<>(name, System
.currentTimeMillis()));
}
@Override
public void progress(float progress, String name) {
checkCanceled();
this.progress = progress;
bar.setValue((int) (progress * 100));
bar.setString(name);
updateTitle(name);
}
@Override
public void endTask(String name) {
checkCanceled();
bar.setString(name);
tasks.removeLast();
}
@Override
public void finishRoot(String name) {
checkCanceled();
bar.setString(name);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// must be called on the event thread
dispose();
}
});
}
@Override
public void setVisible(boolean b) {
closed = !b;
super.setVisible(b);
}
@Override
public void dispose() {
setVisible(false);
super.dispose();
}
}