/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.gui; import jalview.util.MessageManager; import java.awt.BorderLayout; import java.awt.Component; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Hashtable; import java.util.Map; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; /** * A class to manage multiple progress bars embedded in a JPanel. */ /* * Refactored from code duplicated in AlignFrame, PCAPanel, WebserviceInfo. */ public class ProgressBar implements IProgressIndicator { /* * Progress bars in progress, keyed by any arbitrary long value */ Map progressBars; /* * Optional handlers for the progress bars */ Map progressBarHandlers; /* * The panel containing the progress bars - must have GridLayout */ private JPanel statusPanel; /* * Optional label where a status update message can be written on completion * of progress */ private JLabel statusBar; /** * Constructor. Note that the container of the progress bars, and the status * bar to which an optional completion message is written, should be unchanged * for the lifetime of this object for consistent behaviour. * * @param container * the panel holding the progress bars; must have GridLayout manager * @param statusBar * an optional place to write a message when a progress bar is * removed */ public ProgressBar(JPanel container, JLabel statusBar) { if (container == null) { throw new NullPointerException(); } if (!GridLayout.class .isAssignableFrom(container.getLayout().getClass())) { throw new IllegalArgumentException("Container must have GridLayout"); } this.statusPanel = container; this.statusBar = statusBar; this.progressBars = new Hashtable<>(); this.progressBarHandlers = new Hashtable<>(); } /** * Returns true if any progress bars are still active * * @return */ @Override public boolean operationInProgress() { if (progressBars != null && progressBars.size() > 0) { return true; } return false; } /** * First call for a given id will show the message as a new progress bar. A * second call with the same id will remove it. The 'removal' message is * written to the status bar field (if neither is null). * * To avoid progress bars being left orphaned, ensure their removal is * performed in a finally block if there is any risk of an error during * execution. */ @Override public void setProgressBar(final String message, final long id) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JPanel progressPanel = progressBars.get(id); if (progressPanel != null) { /* * Progress bar is displayed for this id - remove it now, and any handler */ progressBars.remove(id); if (message != null && statusBar != null) { statusBar.setText(message); } if (progressBarHandlers.containsKey(id)) { progressBarHandlers.remove(id); } removeRow(progressPanel); } else { /* * No progress bar for this id - add one now */ progressPanel = new JPanel(new BorderLayout(10, 5)); JProgressBar progressBar = new JProgressBar(); progressBar.setIndeterminate(true); progressPanel.add(new JLabel(message), BorderLayout.WEST); progressPanel.add(progressBar, BorderLayout.CENTER); addRow(progressPanel); progressBars.put(id, progressPanel); } refreshLayout(); } }); } /** * Lays out progress bar container hierarchy */ protected void refreshLayout() { /* * lay out progress bar container hierarchy */ Component root = SwingUtilities.getRoot(statusPanel); if (root != null) { root.validate(); } } /** * Remove one row with a progress bar, in a thread-safe manner * * @param progressPanel */ protected void removeRow(JPanel progressPanel) { synchronized (statusPanel) { statusPanel.remove(progressPanel); GridLayout layout = (GridLayout) statusPanel.getLayout(); layout.setRows(layout.getRows() - 1); statusPanel.remove(progressPanel); } } /** * Add one row with a progress bar, in a thread-safe manner * * @param progressPanel */ protected void addRow(JPanel progressPanel) { synchronized (statusPanel) { GridLayout layout = (GridLayout) statusPanel.getLayout(); layout.setRows(layout.getRows() + 1); statusPanel.add(progressPanel); } } /** * Add a 'Cancel' handler for the given progress bar id. This should be called * _after_ setProgressBar to have any effect. */ @Override public void registerHandler(final long id, final IProgressIndicatorHandler handler) { final IProgressIndicator us = this; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final JPanel progressPanel = progressBars.get(id); if (progressPanel == null) { System.err.println( "call setProgressBar before registering the progress bar's handler."); return; } /* * Nothing useful to do if not a Cancel handler */ if (!handler.canCancel()) { return; } progressBarHandlers.put(id, handler); JButton cancel = new JButton( MessageManager.getString("action.cancel")); cancel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { handler.cancelActivity(id); us.setProgressBar(MessageManager .formatMessage("label.cancelled_params", new Object[] { ((JLabel) progressPanel.getComponent(0)).getText() }), id); } }); progressPanel.add(cancel, BorderLayout.EAST); refreshLayout(); } }); } }