package immibis.bon.gui; import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import javax.swing.JLabel; import javax.swing.SwingConstants; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import javax.swing.JProgressBar; import java.awt.Insets; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.net.URLConnection; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class DownloadDialog extends JDialog { private static final long serialVersionUID = 1L; private final JPanel contentPanel = new JPanel(); public JLabel label; public JProgressBar progressBar; /** * Create the dialog. */ public DownloadDialog(JFrame owner) { super(owner); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setModalityType(ModalityType.DOCUMENT_MODAL); setTitle("Downloading..."); setBounds(100, 100, 314, 81); getContentPane().setLayout(new BorderLayout()); contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); getContentPane().add(contentPanel, BorderLayout.CENTER); GridBagLayout gbl_contentPanel = new GridBagLayout(); gbl_contentPanel.columnWidths = new int[]{97, 0}; gbl_contentPanel.rowHeights = new int[]{20, 0, 0}; gbl_contentPanel.columnWeights = new double[]{1.0, Double.MIN_VALUE}; gbl_contentPanel.rowWeights = new double[]{1.0, 1.0, Double.MIN_VALUE}; contentPanel.setLayout(gbl_contentPanel); { label = new JLabel("<label>"); label.setHorizontalAlignment(SwingConstants.CENTER); GridBagConstraints gbc_label = new GridBagConstraints(); gbc_label.fill = GridBagConstraints.HORIZONTAL; gbc_label.insets = new Insets(0, 0, 5, 0); gbc_label.gridx = 0; gbc_label.gridy = 0; contentPanel.add(label, gbc_label); } { progressBar = new JProgressBar(); GridBagConstraints gbc_progressBar = new GridBagConstraints(); gbc_progressBar.fill = GridBagConstraints.HORIZONTAL; gbc_progressBar.anchor = GridBagConstraints.BELOW_BASELINE; gbc_progressBar.gridx = 0; gbc_progressBar.gridy = 1; contentPanel.add(progressBar, gbc_progressBar); } setLocationRelativeTo(owner); } public static void download(final JFrame parent, final URL url, final OutputStream out) throws IOException { final DownloadDialog dlg = new DownloadDialog(parent); //String name = url.getPath().substring(url.getPath().lastIndexOf('/')+1); String name = url.toString(); dlg.label.setText("Downloading "+name+"..."); final AtomicReference<Throwable> exception = new AtomicReference<>(null); new Thread() { {setDaemon(true); setName("Downloader thread");} @Override public void run() { try { { // Wait for dialog to be visible. // This is a slightly dumb way to synchronize threads, but I don't // see a better way within Swing. final boolean[] wasVisible = new boolean[] {false}; while(!wasVisible[0]) { EventQueue.invokeAndWait(new Runnable() { @Override public void run() { wasVisible[0] = dlg.isVisible(); } }); } } URLConnection c = url.openConnection(); final int length = c.getContentLength(); EventQueue.invokeLater(new Runnable() { @Override public void run() { if(length == -1) dlg.progressBar.setIndeterminate(true); else dlg.progressBar.setMaximum(length); }; }); class UpdateBarTask implements Runnable { private AtomicBoolean isRunning = new AtomicBoolean(); private AtomicInteger progress = new AtomicInteger(); @Override public void run() { dlg.progressBar.setValue(progress.get()); isRunning.set(false); } } UpdateBarTask updateBarTask = new UpdateBarTask(); try(InputStream in = c.getInputStream()) { byte[] buffer = new byte[65536]; while(true) { int read = in.read(buffer); if(read <= 0) break; out.write(buffer, 0, read); if(length != -1) { updateBarTask.progress.addAndGet(read); if(!updateBarTask.isRunning.getAndSet(true)) EventQueue.invokeLater(updateBarTask); } } } } catch(Throwable t) { exception.set(t); } finally { dlg.setVisible(false); } } }.start(); dlg.setVisible(true); Throwable t = exception.get(); if(t instanceof IOException) throw new IOException(t); else if(t instanceof Error) throw new Error(t); else if(t != null) throw new RuntimeException(t); } }