/* * org.openmicroscopy.shoola.agents.fsimporter.view.ImporterUI * *------------------------------------------------------------------------------ * Copyright (C) 2006-2014 University of Dundee. All rights reserved. * * * This program 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 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.agents.fsimporter.view; //Java imports import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextPane; import javax.swing.border.BevelBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.Style; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; //Third-party libraries import info.clearthought.layout.TableLayout; import org.apache.commons.collections.CollectionUtils; import org.jdesktop.swingx.JXLabel; import org.jdesktop.swingx.JXPanel; //Application-internal dependencies import org.openmicroscopy.shoola.agents.fsimporter.IconManager; import org.openmicroscopy.shoola.agents.fsimporter.ImporterAgent; import org.openmicroscopy.shoola.agents.fsimporter.actions.GroupSelectionAction; import org.openmicroscopy.shoola.agents.fsimporter.chooser.ImportDialog; import org.openmicroscopy.shoola.agents.fsimporter.util.FileImportComponent; import org.openmicroscopy.shoola.agents.imviewer.view.ImViewer; import org.openmicroscopy.shoola.env.data.model.ImportableFile; import org.openmicroscopy.shoola.env.data.model.ImportableObject; import omero.gateway.SecurityContext; import org.openmicroscopy.shoola.env.ui.TaskBar; import org.openmicroscopy.shoola.env.ui.TopWindow; import org.openmicroscopy.shoola.util.ui.ClosableTabbedPane; import org.openmicroscopy.shoola.util.ui.ClosableTabbedPaneComponent; import org.openmicroscopy.shoola.util.ui.TitlePanel; import org.openmicroscopy.shoola.util.ui.UIUtilities; /** * The {@link Importer}'s View. Displays the on-going import and the finished * ones. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * <small> * (<b>Internal version:</b> $Revision: $Date: $) * </small> * @since 3.0-Beta4 */ class ImporterUI extends TopWindow { /** The text displayed in the header of the dialog.*/ private static final String TEXT_TITLE_DESCRIPTION = "Select data to import and monitor imports."; /** The window's title. */ private static final String TEXT_TITLE = "Import Data"; /** The text displayed to notify the user to refresh. */ private static final String TEXT_REFRESH = "New containers added. Please Refresh"; /** Identifies the style of the document.*/ private static final String STYLE = "StyleName"; /** The maximum number of characters in the debug text.*/ private static final int MAX_CHAR = 2000; /** Reference to the model. */ private ImporterModel model; /** Reference to the control. */ private ImporterControl controller; /** The total of imports. */ private int total; /** The component displaying the various imports. */ private ClosableTabbedPane tabs; /** Flag used to indicate that the component has been displayed or not. */ private boolean initialized; /** The identifier of the UI element. */ private int uiElementID; /** Keeps track of the imports. */ private Map<Integer, ImporterUIElement> uiElements; /** The controls bar. */ private JComponent controlsBar; /** The component indicating to refresh the containers view.*/ private JXLabel messageLabel; /** The menu displaying the groups the user is a member of. */ private JPopupMenu personalMenu; /** The debug text.*/ private JTextPane debugTextPane; /** * Creates the component hosting the debug text. * * @return See above. */ private JComponent createDebugTab() { debugTextPane = new JTextPane(); debugTextPane.setEditable(false); StyledDocument doc = (StyledDocument) debugTextPane.getDocument(); Style style = doc.addStyle(STYLE, null); StyleConstants.setForeground(style, Color.black); StyleConstants.setFontFamily(style, "SansSerif"); StyleConstants.setFontSize(style, 12); StyleConstants.setBold(style, false); JScrollPane sp = new JScrollPane(debugTextPane); sp.getVerticalScrollBar().addAdjustmentListener( new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { try { debugTextPane.setCaretPosition( debugTextPane.getDocument().getLength()); } catch (IllegalArgumentException ex) { // } } } ); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(sp, BorderLayout.CENTER); return panel; } /** * Sets the defaults of the specified menu item. * * @param item The menu item. */ private void initMenuItem(JMenuItem item) { item.setBorder(null); } /** * Brings up the <code>ManagePopupMenu</code>on top of the specified * component at the specified location. * * @param c The component that requested the po-pup menu. * @param p The point at which to display the menu, relative to the * <code>component</code>'s coordinates. */ private void showPersonalMenu(Component c, Point p) { if (p == null) return; if (c == null) throw new IllegalArgumentException("No component."); personalMenu = new JPopupMenu(); personalMenu.setBorder( BorderFactory.createBevelBorder(BevelBorder.RAISED)); List<GroupSelectionAction> l = controller.getUserGroupAction(); Iterator<GroupSelectionAction> i = l.iterator(); GroupSelectionAction a; JCheckBoxMenuItem item; ButtonGroup buttonGroup = new ButtonGroup(); long id = model.getGroupId(); while (i.hasNext()) { a = i.next(); item = new JCheckBoxMenuItem(a); item.setEnabled(true); item.setSelected(a.isSameGroup(id)); initMenuItem(item); buttonGroup.add(item); personalMenu.add(item); } personalMenu.show(c, p.x, p.y); } /** * Builds and lays out the controls. * * @return See above. */ private JPanel buildControls() { JPanel p = new JPanel(); p.add(new JButton(controller.getAction(ImporterControl.RETRY_BUTTON))); p.add(Box.createHorizontalStrut(5)); p.add(new JButton(controller.getAction(ImporterControl.SEND_BUTTON))); p.add(Box.createHorizontalStrut(5)); p.add(new JButton(controller.getAction(ImporterControl.CANCEL_BUTTON))); p.add(Box.createHorizontalStrut(5)); if (!model.isMaster()) { p.add(new JButton(controller.getAction( ImporterControl.CLOSE_BUTTON))); } return UIUtilities.buildComponentPanelRight(p); } /** Builds and lays out the UI. */ private void buildGUI() { Container container = getContentPane(); container.setLayout(new BorderLayout(0, 0)); IconManager icons = IconManager.getInstance(); TitlePanel tp = new TitlePanel(TEXT_TITLE, "", TEXT_TITLE_DESCRIPTION, icons.getIcon(IconManager.IMPORT_48)); JXPanel p = new JXPanel(); JXPanel lp = new JXPanel(); lp.setLayout(new FlowLayout(FlowLayout.LEFT)); lp.add(messageLabel); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); p.add(tp); p.add(lp); p.setBackgroundPainter(tp.getBackgroundPainter()); lp.setBackgroundPainter(tp.getBackgroundPainter()); container.add(p, BorderLayout.NORTH); container.add(tabs, BorderLayout.CENTER); container.add(controlsBar, BorderLayout.SOUTH); } /** Displays the window. */ private void display() { if (initialized) return; initialized = true; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setSize(6*(screenSize.width/10), 8*(screenSize.height/10)); } /** Initializes the components. */ private void initComponents() { messageLabel = new JXLabel(); messageLabel.setText(TEXT_REFRESH); messageLabel.setVisible(false); messageLabel.setFont(messageLabel.getFont().deriveFont(Font.BOLD)); controlsBar = buildControls(); controlsBar.setVisible(false); uiElementID = 0; uiElements = new LinkedHashMap<Integer, ImporterUIElement>(); tabs = new ClosableTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT); tabs.setAlignmentX(LEFT_ALIGNMENT); tabs.addPropertyChangeListener(controller); tabs.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { controlsBar.setVisible(tabs.getSelectedIndex() != 0); controller.getAction( ImporterControl.RETRY_BUTTON).setEnabled( hasFailuresToReupload()); controller.getAction( ImporterControl.SEND_BUTTON).setEnabled( hasFailuresToSend()); } }); } /** * Helper method to create the <code>File</code> menu. * * @return See above. */ private JMenu createFileMenu() { JMenu menu = new JMenu("File"); menu.setMnemonic(KeyEvent.VK_F); menu.add(new JMenuItem(controller.getAction(ImporterControl.LOG_OFF))); menu.add(new JMenuItem(controller.getAction(ImporterControl.EXIT))); return menu; } /** * Creates the menu bar. * * @return The menu bar. */ private JMenuBar createMenuBar() { TaskBar tb = ImporterAgent.getRegistry().getTaskBar(); JMenuBar bar = tb.getTaskBarMenuBar(); if (!model.isMaster()) return bar; JMenu[] existingMenus = new JMenu[bar.getMenuCount()]; for (int i = 0; i < existingMenus.length; i++) { existingMenus[i] = bar.getMenu(i); } bar.removeAll(); bar.add(createFileMenu()); for (int i = 0; i < existingMenus.length; i++) { if (i != TaskBar.FILE_MENU) bar.add(existingMenus[i]); } return bar; } /** Packs the window and resizes it if the screen is too small. */ private void packWindow() { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension size = getSize(); int width = (int) (screenSize.width); int height = (int) (screenSize.height); int w = size.width-10; int h = size.height-10; boolean reset = false; if (w > width) { reset = true; } if (h > height) { reset = true; h = height; } if (reset) { setSize(h, h); } } /** Creates a new instance. */ ImporterUI() { super(TEXT_TITLE); } /** * Links this View to its Controller. * * @param model The Model. * @param controller The Controller. */ void initialize(ImporterModel model, ImporterControl controller) { this.model = model; this.controller = controller; total = 1; initComponents(); setJMenuBar(createMenuBar()); buildGUI(); setName("importer window"); } /** * Displays or hides the message indicating to refresh the location view. * * @param show Pass <code>true</code> to show, <code>false</code> to hide. */ void showRefreshMessage(boolean show) { messageLabel.setVisible(show); } /** * Adds the chooser to the tab. * * @param chooser The component to add. */ void addComponent(ImportDialog chooser) { if (chooser == null) return; tabs.insertTab("Select Data to Import", null, chooser, "", 0); //if in debug mode insert the debug section Boolean debugEnabled = (Boolean) ImporterAgent.getRegistry().lookup("/options/Debug"); if (debugEnabled != null && debugEnabled.booleanValue()) { IconManager icons = IconManager.getInstance(); ClosableTabbedPaneComponent c = new ClosableTabbedPaneComponent(1, "Debug Text", icons.getIcon(IconManager.DEBUG)); c.setCloseVisible(false); c.setClosable(false); double[][] tl = {{TableLayout.FILL}, {TableLayout.FILL}}; c.setLayout(new TableLayout(tl)); c.add(createDebugTab(), "0, 0"); tabs.insertClosableComponent(c); } selectChooser(); pack(); } /** Indicates to the select the import chooser. */ void selectChooser() { tabs.setSelectedIndex(0); } /** * Returns the collection of files that could not be imported. * * @return See above. */ List<FileImportComponent> getMarkedFiles() { Component[] comps = tabs.getComponents(); if (comps == null || comps.length == 0) return null; List<FileImportComponent> list = new ArrayList<FileImportComponent>(); List<FileImportComponent> l; ImporterUIElement element = getSelectedPane(); if(element == null) return null; l = element.getMarkedFiles(); if (l != null && l.size() > 0) list.addAll(l); return list; } /** * Adds the component to the display. * * @param object The component to add. */ ImporterUIElement addImporterElement(ImportableObject object) { if (object == null) return null; int n = tabs.getComponentCount(); String title = "Import #"+total; ImporterUIElement element = new ImporterUIElement(controller, model, this, uiElementID, n, title, object); tabs.insertTab(title, element.getImportIcon(), element, "", total); total++; uiElements.put(uiElementID, element); uiElementID++; if (!isVisible()) { setVisible(true); display(); } return element; } /** Resets the import.*/ void reset() { int n = tabs.getTabCount(); for (int i = 1; i < n; i++) { tabs.remove(i); } uiElements.clear(); } /** * Modifies the icon corresponding to the specified component when * the import has ended. * * @param element The element to handle. */ void onImportEnded(ImporterUIElement element) { if (element == null) return; element.onImportEnded(); if (tabs.getSelectedComponent() == element) { tabs.setIconAt(tabs.getSelectedIndex(), element.getImportIcon()); } else { Component[] components = tabs.getComponents(); int index = -1; for (int i = 0; i < components.length; i++) { if (components[i] == element) { index = i; } } if (index >= 0) tabs.setIconAt(index, element.getImportIcon()); } } /** * Sets the selected pane when the import start. * * @param element The element to select. * @param startImport Pass <code>true</code> to start the import, * <code>false</code> otherwise. */ void setSelectedPane(ImporterUIElement element, boolean startImport) { int n = tabs.getComponentCount(); if (n == 0 || element == null) return; if (tabs.getSelectedComponent() == element) return; Component[] components = tabs.getComponents(); int index = -1; for (int i = 0; i < components.length; i++) { if (components[i] == element) { index = i; tabs.setSelectedComponent(element); } } if (startImport) { Icon icon = element.startImport(tabs); if (index >=0) tabs.setIconAt(index, icon); } } /** * Returns the selected pane. * * @return See above. */ ImporterUIElement getSelectedPane() { if (tabs.getSelectedIndex() > 0 && tabs.getSelectedComponent() instanceof ImporterUIElement) return (ImporterUIElement) tabs.getSelectedComponent(); return null; } /** * Returns the UI element corresponding to the passed index. * * @param index The identifier of the component * @return See above. */ ImporterUIElement getUIElement(int index) { return uiElements.get(index); } /** * Returns the first element with data to import. * * @return See above. */ ImporterUIElement getElementToStartImportFor() { if (uiElements.size() == 0) return null; Iterator<ImporterUIElement> i = uiElements.values().iterator(); ImporterUIElement element; while (i.hasNext()) { element = i.next(); if (!element.hasStarted()) return element; } return null; } /** * Removes the specified element. Returns the element or <code>null</code>. * * @param object The object to remove. * @return See above. */ ImporterUIElement removeImportElement(Object object) { Iterator<ImporterUIElement> i = uiElements.values().iterator(); ImporterUIElement element; ImporterUIElement found = null; while (i.hasNext()) { element = i.next(); if (element == object) { found = element; break; } } if (found != null) uiElements.remove(found.getID()); return found; } /** * Returns the elements with data to import. * * @return See above. */ Collection<ImporterUIElement> getImportElements() { return uiElements.values(); } /** * Returns <code>true</code> if there is an on-going import for a given * group or one schedule. * * @return See above. */ boolean hasOnGoingImportFor(SecurityContext ctx) { Collection<ImporterUIElement> values = uiElements.values(); Iterator<ImporterUIElement> i = values.iterator(); ImporterUIElement elt; ImportableObject data; List<ImportableFile> files; ImportableFile f; Iterator<ImportableFile> j; while (i.hasNext()) { elt = i.next(); data = elt.getData(); files = data.getFiles(); if (CollectionUtils.isNotEmpty(files) && !elt.isDone()) { j = files.iterator(); while (j.hasNext()) { f = j.next(); if (f.getGroup().getId() == ctx.getGroupID()) { return true; } } } } return false; } /** * Returns <code>true</code> if errors to send, <code>false</code> * otherwise. * * @return See above. */ boolean hasFailuresToSend() { ImporterUIElement element = getSelectedPane(); if (element == null) return false; return element.hasFailuresToSend(); } /** * Brings up the menu on top of the specified component at * the specified location. * * @param menuID The id of the menu. * @param c The component that requested the pop-up menu. * @param p The point at which to display the menu, relative to the * <code>component</code>'s coordinates. */ void showMenu(int menuID, Component c, Point p) { switch (menuID) { case Importer.PERSONAL_MENU: showPersonalMenu(c, p); } } /** * Returns <code>true</code> if the agent is the entry point * <code>false</code> otherwise. * * @return See above. */ boolean isMaster() { return model.isMaster(); } /** * Adds the text to the debug pane. * * @param text The text to display. */ void appendDebugText(String text) { if (debugTextPane == null) return; StyledDocument doc = (StyledDocument) debugTextPane.getDocument(); try { doc.insertString(doc.getLength(), text, doc.getStyle(STYLE)); if (doc.getLength() > MAX_CHAR) doc.remove(0, doc.getLength()-MAX_CHAR); } catch (Exception e) { //ignore } } /** * Returns the collection of files that could not be imported. * * @return See above. */ List<FileImportComponent> getFilesToReimport() { ImporterUIElement pane = getSelectedPane(); if (pane == null) return null; return pane.getFilesToReupload(); } /** * Returns <code>true</code> if file to re-upload, <code>false</code> * otherwise. * * @return See above. */ boolean hasFailuresToReupload() { ImporterUIElement element = getSelectedPane(); if (element == null) return false; return element.hasFailuresToReupload(); } /** * Overridden to the set the location of the {@link ImViewer}. * @see TopWindow#setOnScreen() */ public void setOnScreen() { packWindow(); UIUtilities.centerAndShow(this); } }