/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.tools; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import com.rapidminer.gui.ApplicationFrame; import com.rapidminer.gui.tools.dialogs.ButtonDialog; /** * Displays information about all pending {@link ProgressThread}s. * * @author Simon Fischer, Marco Boeck */ public class ProgressThreadDialog extends ButtonDialog { private static final long serialVersionUID = 1L; /** the singleton instance */ private static ProgressThreadDialog INSTANCE; /** Create dialog in EDT */ static { SwingTools.invokeLater(new Runnable() { @Override public void run() { INSTANCE = new ProgressThreadDialog(); } }); } /** mapping between ProgressThread and the UI panel for it */ private static Map<ProgressThread, ProgressThreadDisplay> MAPPING_PG_TO_UI = Collections .synchronizedMap(new HashMap<ProgressThread, ProgressThreadDisplay>()); /** the panel displaying the running and waiting threads */ private JPanel threadPanel; /** if true, the user opened the dialog; false otherwise */ private boolean openedByUser; /** * Private default constructor. */ private ProgressThreadDialog() { super(ApplicationFrame.getApplicationFrame(), "progress_dialog", ModalityType.APPLICATION_MODAL, new Object[] {}); // listener to be notified when the ProgressThread tasks change ProgressThread.addProgressThreadStateListener(new ProgressThreadStateListener() { @Override public void progressThreadStarted(ProgressThread pg) { updatePanelInEDT(); updateUI(); } @Override public void progressThreadQueued(ProgressThread pg) { updatePanelInEDT(); updateUI(); } @Override public void progressThreadFinished(ProgressThread pg) { updatePanelInEDT(); updateUI(); MAPPING_PG_TO_UI.remove(pg); } @Override public void progressThreadCancelled(ProgressThread pg) { updatePanelInEDT(); updateUI(); MAPPING_PG_TO_UI.remove(pg); } /** * Updates the ProgressThread panel in the EDT. */ private void updatePanelInEDT() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { updateThreadPanel(false); } }); } }); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { initGUI(); } }); } /** * Inits the GUI. */ private void initGUI() { JPanel outerPanel = new JPanel(new BorderLayout()); outerPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); threadPanel = new JPanel(new GridBagLayout()); threadPanel.setOpaque(true); threadPanel.setBackground(Color.WHITE); // add thread panel to outer panel JScrollPane scrollPane = new ExtendedJScrollPane(threadPanel); scrollPane.setBorder(null); outerPanel.add(scrollPane, BorderLayout.CENTER); setDefaultSize(ButtonDialog.NORMAL); layoutDefault(outerPanel, makeCancelButton("hide")); setModalExclusionType(ModalExclusionType.APPLICATION_EXCLUDE); setModalityType(ModalityType.APPLICATION_MODAL); } /** * Updates the thread display panel which shows running/queued threads. * * @param forceUpdate * will update even when the dialog is not visible */ private synchronized void updateThreadPanel(boolean forceUpdate) { if (!forceUpdate && !isVisible()) { return; } threadPanel.removeAll(); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1.0; gbc.weighty = 0.0; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.ipady = 10; // add currently running tasks for (ProgressThread currentThread : ProgressThread.getCurrentThreads()) { ProgressThreadDisplay pgPanel = new ProgressThreadDisplay(currentThread, false); threadPanel.add(pgPanel, gbc); MAPPING_PG_TO_UI.put(currentThread, pgPanel); updateProgressMessage(currentThread); updateProgress(currentThread); gbc.gridy += 1; } // add pending tasks for (ProgressThread queuedThread : ProgressThread.getQueuedThreads()) { ProgressThreadDisplay pgPanel = new ProgressThreadDisplay(queuedThread, true); threadPanel.add(pgPanel, gbc); MAPPING_PG_TO_UI.put(queuedThread, pgPanel); gbc.gridy += 1; } // add filler component gbc.gridy += 1; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; threadPanel.add(new JLabel(), gbc); threadPanel.revalidate(); threadPanel.repaint(); } /** * Updates the status bar in the ApplicationFrame and refreshes the dialog if need be. */ private void updateUI() { if (ProgressThread.isEmpty()) { // close dialog if not opened by user if (!openedByUser) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ProgressThreadDialog.this.dispose(); } }); } // hide status bar if (ApplicationFrame.getApplicationFrame() != null) { ApplicationFrame.getApplicationFrame().getStatusBar().setProgress("", 100, 100); } } else { if (!ProgressThread.isForegroundRunning()) { // close dialog if not opened by user if (!openedByUser) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ProgressThreadDialog.this.dispose(); } }); } } Iterator<ProgressThread> iterator = ProgressThread.getCurrentThreads().iterator(); if (iterator.hasNext()) { ProgressThread pg = iterator.next(); // while there is at least one running, take first one and display in status // bar if (ApplicationFrame.getApplicationFrame() != null) { StatusBar statusBar = ApplicationFrame.getApplicationFrame().getStatusBar(); if (pg.isIndeterminate()) { statusBar.setIndeterminateProgress(pg.getName(), 0, 100); } else { statusBar.setProgress(pg.getName(), pg.getDisplay().getCompleted(), pg.getDisplay().getTotal()); } } } else { // no task running, hide status bar if (ApplicationFrame.getApplicationFrame() != null) { ApplicationFrame.getApplicationFrame().getStatusBar().setProgress("", 100, 100); } } } } /** * Updates the progress of the specified ProgressThread. * * @param pg */ public void updateProgress(final ProgressThread pg) { final ProgressThreadDisplay ui = MAPPING_PG_TO_UI.get(pg); if (ui != null) { if (SwingUtilities.isEventDispatchThread()) { ui.setProgress(pg.getDisplay().getCompleted()); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ui.setProgress(pg.getDisplay().getCompleted()); } }); } } updateUI(); } /** * Updates the progress message of the specified ProgressThread. * * @param pg */ public void updateProgressMessage(final ProgressThread pg) { final ProgressThreadDisplay ui = MAPPING_PG_TO_UI.get(pg); if (ui != null) { if (SwingUtilities.isEventDispatchThread()) { ui.setMessage(pg.getDisplay().getMessage()); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ui.setMessage(pg.getDisplay().getMessage()); } }); } } updateUI(); } /** * Sets the dialog to visible. If this was invoked by a user action, set openedByUser to * <code>true</code>; <code>false</code> otherwise. This needed so that the dialog will not * close itself if opened by the user. * * @param openedByUser * @param visible */ public void setVisible(boolean openedByUser, boolean visible) { this.openedByUser = openedByUser; if (visible) { updateThreadPanel(true); setLocationRelativeTo(ApplicationFrame.getApplicationFrame()); } super.setVisible(visible); } @Override public void setVisible(boolean b) { openedByUser = false; if (b) { updateThreadPanel(true); setLocationRelativeTo(ApplicationFrame.getApplicationFrame()); } super.setVisible(b); } /** * Singleton access to the {@link ProgressThreadDialog}. */ public static ProgressThreadDialog getInstance() { return INSTANCE; } }