/* *------------------------------------------------------------------------------ * Copyright (C) 2006-2015 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.treeviewer.browser; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.Point; import java.awt.Rectangle; import java.awt.dnd.DnDConstants; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; //Third-party libraries import org.apache.commons.collections.CollectionUtils; //Application-internal dependencies import org.openmicroscopy.shoola.agents.treeviewer.TreeViewerAgent; import org.openmicroscopy.shoola.agents.treeviewer.actions.BrowserManageAction; import org.openmicroscopy.shoola.agents.treeviewer.cmd.ViewCmd; import org.openmicroscopy.shoola.agents.treeviewer.util.TreeCellRenderer; import org.openmicroscopy.shoola.agents.treeviewer.view.TreeViewer; import org.openmicroscopy.shoola.agents.util.ViewerSorter; import org.openmicroscopy.shoola.agents.util.browser.PartialNameVisitor; import org.openmicroscopy.shoola.agents.util.browser.SmartFolder; import org.openmicroscopy.shoola.agents.util.browser.TreeFileSet; import org.openmicroscopy.shoola.agents.util.browser.TreeImageDisplay; import org.openmicroscopy.shoola.agents.util.browser.TreeImageDisplayVisitor; import org.openmicroscopy.shoola.agents.util.browser.TreeImageNode; import org.openmicroscopy.shoola.agents.util.browser.TreeImageSet; import org.openmicroscopy.shoola.agents.util.browser.TreeImageTimeSet; import org.openmicroscopy.shoola.agents.util.browser.TreeViewerTranslator; import org.openmicroscopy.shoola.agents.util.dnd.DnDTree; import org.openmicroscopy.shoola.agents.util.dnd.ObjectToTransfer; import org.openmicroscopy.shoola.env.LookupNames; import org.openmicroscopy.shoola.env.config.Registry; import org.openmicroscopy.shoola.env.data.FSFileSystemView; import org.openmicroscopy.shoola.util.CommonsLangUtils; import org.openmicroscopy.shoola.util.ui.UIUtilities; import omero.gateway.model.DataObject; import omero.gateway.model.DatasetData; import omero.gateway.model.ExperimenterData; import omero.gateway.model.FileAnnotationData; import omero.gateway.model.FileData; import omero.gateway.model.GroupData; import omero.gateway.model.ImageData; import omero.gateway.model.PlateData; import omero.gateway.model.PlateAcquisitionData; import omero.gateway.model.ProjectData; import omero.gateway.model.ScreenData; import omero.gateway.model.TagAnnotationData; /** * The Browser's View. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @version 2.2 * @since OME2.2 */ class BrowserUI extends JPanel implements PropertyChangeListener { /** The <code>Attachments</code> smart folder. */ private static final int[] VALUES = {TreeFileSet.MOVIE, TreeFileSet.OTHER}; /** The tree hosting the display. */ private DnDTree treeDisplay; /** The tool bar hosting the controls. */ private JToolBar rightMenuBar; /** The tool bar hosting the controls. */ private JToolBar leftMenuBar; /** The Controller. */ private BrowserControl controller; /** The model. */ private BrowserModel model; /** * A {@link ViewerSorter sorter} to order nodes in ascending * alphabetical order. */ private ViewerSorter sorter; /** Reference to the listener. */ private TreeExpansionListener listener; /** Reference to the selection listener. */ private TreeSelectionListener selectionListener; /** Collections of nodes whose <code>enabled</code> flag has to be reset. */ private Set<TreeImageDisplay> nodesToReset; /** Button indicating if the partial name is displayed or not. */ private JToggleButton partialButton; /** Indicates if the <code>control</code> key is down. */ private boolean ctrl; /** Flag indicating if the left mouse button is pressed. */ private boolean leftMouseButton; /** The component displayed at the bottom of the UI. */ private JComponent bottomComponent; /* The tree selection event. */ private TreeSelectionEvent event; /* The time when the latest tree selection event was handled. */ private long eventHandledTime = Long.MIN_VALUE; /* The time when the latest mouse press event occurred. */ private long mousePressedTime; /* If a mouse event delayed handling tree selection, because * a corresponding tree selection event had not yet been seen. */ private boolean delayedHandlingTreeSelection = false; /** Flag indicating if it is a right-click.*/ private boolean rightClickButton; /** Flag indicating if it is a right-click.*/ private boolean rightClickPad; /** Identifies the key event.*/ private int keyEvent; /** * Builds the tool bar. * * @return See above. */ private JPanel buildToolBar() { JPanel bar = new JPanel(); bar.setLayout(new BoxLayout(bar, BoxLayout.X_AXIS)); bar.setBorder(null); JPanel p = new JPanel(); p.setLayout(new FlowLayout(FlowLayout.LEFT)); p.setBorder(null); p.add(leftMenuBar); p.setPreferredSize(leftMenuBar.getPreferredSize()); bar.add(p); p = new JPanel(); p.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 0)); p.setBorder(null); p.add(rightMenuBar); p.setPreferredSize(rightMenuBar.getPreferredSize()); bar.add(p); return bar; } /** Builds and lays out the UI. */ private void buildGUI() { setLayout(new BorderLayout(0, 0)); add(buildToolBar(), BorderLayout.NORTH); add(new JScrollPane(treeDisplay), BorderLayout.CENTER); } /** Helper method to create the menu bar. */ private void createMenuBars() { rightMenuBar = new JToolBar(); rightMenuBar.setBorder(null); rightMenuBar.setRollover(true); rightMenuBar.setFloatable(false); JButton button; leftMenuBar = new JToolBar(); leftMenuBar.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); leftMenuBar.setRollover(true); leftMenuBar.setFloatable(false); BrowserManageAction a; int type = model.getBrowserType(); switch (type) { case Browser.PROJECTS_EXPLORER: case Browser.SCREENS_EXPLORER: a = (BrowserManageAction) controller.getAction(BrowserControl.NEW_CONTAINER); button = new JButton(a); button.setName("new container button"); button.setBorderPainted(false); button.addMouseListener(a); rightMenuBar.add(button); break; case Browser.ADMIN_EXPLORER: a = (BrowserManageAction) controller.getAction(BrowserControl.NEW_ADMIN); button = new JButton(a); button.setName("new group or user button"); button.setBorderPainted(false); button.addMouseListener(a); rightMenuBar.add(button); break; case Browser.TAGS_EXPLORER: a = (BrowserManageAction) controller.getAction(BrowserControl.NEW_TAG); button = new JButton(a); button.setName("new tag button"); button.setBorderPainted(false); button.addMouseListener(a); rightMenuBar.add(button); } button = new JButton(controller.getAction(BrowserControl.CUT)); button.setName("cut button"); button.setBorderPainted(false); rightMenuBar.add(button); button = new JButton(controller.getAction(BrowserControl.COPY)); button.setName("copy button"); button.setBorderPainted(false); rightMenuBar.add(button); button = new JButton(controller.getAction(BrowserControl.PASTE)); button.setName("paste button"); button.setBorderPainted(false); rightMenuBar.add(button); button = new JButton(controller.getAction(BrowserControl.DELETE)); button.setName("delete button"); button.setBorderPainted(false); rightMenuBar.add(button); button = new JButton(controller.getAction(BrowserControl.REFRESH)); button.setName("refresh button"); button.setBorderPainted(false); rightMenuBar.add(button); if (type == Browser.ADMIN_EXPLORER) { button = new JButton(controller.getAction( BrowserControl.RESET_PASSWORD)); button.setBorderPainted(false); rightMenuBar.add(button); } else { button = new JButton(controller.getAction(BrowserControl.IMPORT)); button.setBorderPainted(false); //rightMenuBar.add(button); } rightMenuBar.add(Box.createHorizontalStrut(6)); rightMenuBar.add(new JSeparator()); rightMenuBar.add(Box.createHorizontalStrut(6)); ButtonGroup group = new ButtonGroup(); JToggleButton b = new JToggleButton(); group.add(b); b.setBorderPainted(true); b.setSelected(true); b.setAction(controller.getAction(BrowserControl.SORT)); rightMenuBar.add(b); b = new JToggleButton(controller.getAction(BrowserControl.SORT_DATE)); b.setBorderPainted(true); group.add(b); rightMenuBar.add(b); partialButton = new JToggleButton( controller.getAction(BrowserControl.PARTIAL_NAME)); partialButton.setBorderPainted(true); rightMenuBar.add(partialButton); rightMenuBar.add(new JSeparator(JSeparator.VERTICAL)); button = new JButton(controller.getAction(BrowserControl.COLLAPSE)); button.setBorderPainted(false); rightMenuBar.add(button); } /** * Reacts to node expansion event. * * @param node The node to handle. * @param expanded Pass <code>true</code> is the node is expanded, * <code>false</code> otherwise. */ private void onNodeNavigation(TreeImageDisplay node, boolean expanded) { node.setExpanded(expanded); controller.onNodeNavigation(node, expanded); treeDisplay.clearSelection(); try { treeDisplay.setSelectionPath(new TreePath(node.getPath())); } catch (Exception e) {} treeDisplay.repaint(); } /** * Reacts to mouse pressed and mouse release event. * * @param me The event to handle. * @param released Pass <code>true</code> if the method is invoked when * the mouse is released, <code>false</code> otherwise. */ private void onClick(MouseEvent me, boolean released) { Point p = me.getPoint(); int row = treeDisplay.getRowForLocation(p.x, p.y); if (row != -1) { if (me.getClickCount() == 1) { model.setClickPoint(p); if (mousePressedTime > eventHandledTime) /* have not yet seen the tree selection event */ delayedHandlingTreeSelection = true; else handleTreeSelection(); //if (released) { if ((me.isPopupTrigger() && !released) || (me.isPopupTrigger() && released && !UIUtilities.isMacOS()) || (UIUtilities.isMacOS() && SwingUtilities.isLeftMouseButton(me) && me.isControlDown())) { if (rightClickButton && !model.isMultiSelection()) { //(!(me.isShiftDown() || ctrl)) TreePath path = treeDisplay.getPathForLocation(p.x, p.y); //treeDisplay.removeTreeSelectionListener( // selectionListener); if (path != null) treeDisplay.setSelectionPath(path); //treeDisplay.addTreeSelectionListener(selectionListener); if (path != null && path.getLastPathComponent() instanceof TreeImageDisplay) controller.onRightClick((TreeImageDisplay) path.getLastPathComponent()); } if (model.getBrowserType() == Browser.ADMIN_EXPLORER) controller.showPopupMenu(TreeViewer.ADMIN_MENU); else controller.showPopupMenu(TreeViewer.FULL_POP_UP_MENU); } } else if (me.getClickCount() == 2 && !(me.isMetaDown() || me.isControlDown() || me.isShiftDown())) { //controller.cancel(); //model.viewDataObject(); TreeImageDisplay d = model.getLastSelectedDisplay(); if (d == null) return; Object o = d.getUserObject(); if (o instanceof ImageData) { model.browse(d); } else if (o instanceof PlateData) { if (!d.hasChildrenDisplay() || d.getChildrenDisplay().size() == 1) model.browse(d); } else if (o instanceof PlateAcquisitionData) { model.browse(d); } } } } /** * Handles the mouse moved event. Displays the properties of the * the nodes the mouse is over. * * @param e The mouse event to handle. */ private void rollOver(MouseEvent e) { if (!model.getParentModel().isRollOver()) return; JTree tree = treeDisplay; TreePath path = treeDisplay.getClosestPathForLocation( e.getX(), e.getY()); Rectangle bounds = tree.getPathBounds(path); if (!bounds.contains(e.getPoint())) return; TreeImageDisplay node = (TreeImageDisplay) path.getLastPathComponent(); Object uo = node.getUserObject(); if (!(uo instanceof DataObject)) return; } /** * Creates an experimenter node hosting the passed experimenter. * * @param exp The experimenter to add. * @param groupNode The node to attach the experimenter to. * @return See above. */ private TreeImageSet createExperimenterNode(ExperimenterData exp, TreeImageDisplay groupNode) { DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); TreeImageSet node = new TreeImageSet(exp); switch (model.getBrowserType()) { case Browser.IMAGES_EXPLORER: createTimeElements(node); break; case Browser.FILES_EXPLORER: createFileElements(node); break; case Browser.TAGS_EXPLORER: createTagsElements(node); break; default: buildEmptyNode(node); } //TreeImageDisplay root = getTreeRoot(); //root.addChildDisplay(node); //tm.insertNodeInto(node, root, root.getChildCount()); groupNode.addChildDisplay(node); tm.insertNodeInto(node, groupNode, groupNode.getChildCount()); return node; } /** * Creates the elements to attach the specified group node. * * @param node The node to attach the elements to. * @return See above. */ private TreeImageSet createGroupNode(TreeImageSet node) { switch (model.getBrowserType()) { case Browser.IMAGES_EXPLORER: createTimeElements(node); break; case Browser.FILES_EXPLORER: createFileElements(node); break; default: buildEmptyNode(node); } return node; } /** * Creates the smart folders added to the passed node. * * @param parent The parent of the smart folder. */ private void createTimeElements(TreeImageSet parent) { createTimeNode(TreeImageTimeSet.TODAY, parent, true); createTimeNode(TreeImageTimeSet.WEEK, parent, true); createTimeNode(TreeImageTimeSet.TWO_WEEK, parent, true); TreeImageSet n = createTimeNode(TreeImageTimeSet.YEAR, parent, false); int month = TreeImageTimeSet.getCurrentMonth()+1; for (int i = 0; i < month; i++) createTimeNode(TreeImageTimeSet.YEAR, i, n); n.setNumberItems(-1); n = createTimeNode(TreeImageTimeSet.YEAR_BEFORE, parent, false); for (int i = 0; i < 12; i++) createTimeNode(TreeImageTimeSet.YEAR_BEFORE, i, n); n.setNumberItems(-1); createTimeNode(TreeImageTimeSet.OTHER, parent, true); //parent.setChildrenLoaded(true); } /** * Creates folders hosting <code>FileAnnotation</code>s. * * @param parent The parent of the elements. */ private void createFileElements(TreeImageSet parent) { DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); TreeFileSet node; for (int i = 0; i < VALUES.length; i++) { node = new TreeFileSet(VALUES[i]); buildEmptyNode(node); node.setNumberItems(-1); parent.addChildDisplay(node); tm.insertNodeInto(node, parent, parent.getChildCount()); } } /** * Creates the smart folders added to the passed node. * * @param parent The parent of the smart folder. */ private void createTagsElements(TreeImageDisplay parent) { if (model.getDisplayMode() == TreeViewer.GROUP_DISPLAY) return; DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); TreeFileSet node = new TreeFileSet(TreeFileSet.TAG); buildEmptyNode(node); node.setNumberItems(-1); parent.addChildDisplay(node); tm.insertNodeInto(node, parent, parent.getChildCount()); } /** * Creates and returns a {@link TreeImageTimeSet}. * * @param index One of the following constants: * {@link TreeImageTimeSet#YEAR} or * {@link TreeImageTimeSet#WEEK} * @param parent The parent of the new node. * @param empty Pass <code>true</code> to add an empty node, * <code>false</code> otherwise. * @return See above. */ private TreeImageTimeSet createTimeNode(int index, TreeImageSet parent, boolean empty) { DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); TreeImageTimeSet date = new TreeImageTimeSet(index); if (empty) buildEmptyNode(date); parent.addChildDisplay(date); tm.insertNodeInto(date, parent, parent.getChildCount()); return date; } /** * Creates and returns a {@link TreeImageTimeSet}. * * @param index One of the following constants: * {@link TreeImageTimeSet#YEAR} or * {@link TreeImageTimeSet#YEAR_BEFORE}. * @param month The index of the month. * @param parent The parent of the new node. * @return See above. */ private TreeImageTimeSet createTimeNode(int index, int month, TreeImageSet parent) { DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); TreeImageTimeSet date = new TreeImageTimeSet(index, month); buildEmptyNode(date); parent.addChildDisplay(date); tm.insertNodeInto(date, parent, parent.getChildCount()); parent.setChildrenLoaded(true); return date; } /** * Transforms the file contained in the passed directory. * Returns the collection of <code>TreeImageNode</code> to check. * * @param dirSet The directory to transform. * @return See above. */ private List<TreeImageNode> transformDirectory(TreeImageSet dirSet) { List<TreeImageNode> leaves = new ArrayList<TreeImageNode>(); FileData dir = (FileData) dirSet.getUserObject(); TreeImageDisplay expNode = BrowserFactory.getDataOwner(dirSet); if (expNode == null) return leaves; Object ho = expNode.getUserObject(); if (!(ho instanceof ExperimenterData)) return leaves; long expID = ((ExperimenterData) ho).getId(); DataObject[] files = model.getFilesData(expID, dir); if (files != null && files.length > 0) { DataObject object; FileData file; TreeImageDisplay display; DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); for (int i = 0; i < files.length; i++) { object = files[i]; display = null; if (object instanceof FileData) { file = (FileData) object; if (file.isDirectory()) { if (!file.isHidden()) { display = new TreeImageSet(file); buildEmptyNode(display); } } else { if (!file.isHidden()) display = new TreeImageNode(file); } } else if (object instanceof ImageData) { display = TreeViewerTranslator.transformImage( (ImageData) object); } if (display != null) dirSet.addChildDisplay(display); } } return leaves; } /** * Creates the file system view. * * @param id The id of the user the directory is for. * @return See above. */ private Set<TreeImageDisplay> createFileSystemExplorer( TreeImageDisplay expNode) { Set<TreeImageDisplay> results = new HashSet<TreeImageDisplay>(); ExperimenterData exp = (ExperimenterData) expNode.getUserObject(); FSFileSystemView fs = model.getRepositories(exp.getId()); if (fs == null) return results; FileData file; TreeImageSet display; FileData[] files = fs.getRoots(); for (int j = 0; j < files.length; j++) { file = files[j]; if (file.isDirectory() && !file.isHidden()) { display = new TreeImageSet(file); //display.setChildrenLoaded(true); expNode.addChildDisplay(display); buildEmptyNode(display); //transformDirectory(display); results.add(display); } } return results; } /** Handles the selection of the nodes in the tree.*/ private void handleTreeSelection() { delayedHandlingTreeSelection = false; TreeImageDisplay[] nodes = model.getSelectedDisplays(); if (((rightClickButton && !ctrl) || rightClickPad) && model.isMultiSelection()) { setFoundNode(nodes); return; } if (ctrl && leftMouseButton) { TreePath[] paths = treeDisplay.getSelectionPaths(); List<TreePath> added = new ArrayList<TreePath>(); TreePath[] all = null; if (paths != null) { all = new TreePath[paths.length]; for (int i = 0; i < paths.length; i++) { all[i] = new TreePath(paths[i].getPath()); } } //treeDisplay.removeTreeSelectionListener(selectionListener); if (all != null) treeDisplay.setSelectionPaths(all); //treeDisplay.addTreeSelectionListener(selectionListener); if (all != null) { for (int i = 0; i < all.length; i++) added.add(all[i]); } controller.onClick(added); return; } if (event == null) return; TreePath[] paths = event.getPaths(); List<TreePath> added = new ArrayList<TreePath>(); for (int i = 0; i < paths.length; i++) { if (rightClickPad) { if (!event.isAddedPath(paths[i])) { added.add(paths[i]); } } else { if (event.isAddedPath(paths[i])) { added.add(paths[i]); } } } //if (!ctrl) controller.onClick(added); } /** * Handles multi-selection using <code>Ctrl-A</code> or <code>Meta-A</code>. */ private void handleMultiSelection() { TreeImageDisplay[] nodes = model.getSelectedDisplays(); if (nodes == null || nodes.length == 0) return; TreeImageDisplay n = nodes[0]; Object o = n.getUserObject(); TreeImageDisplay p; if (o instanceof ImageData) { p = n.getParentDisplay(); controller.selectNodes(p.getChildrenDisplay(), ImageData.class); } else if (o instanceof DatasetData) { if (n.isExpanded()) { controller.selectNodes(n.getChildrenDisplay(), ImageData.class); } else { p = n.getParentDisplay(); controller.selectNodes(p.getChildrenDisplay(), DatasetData.class); } } else if (o instanceof ProjectData) { if (n.isExpanded()) { controller.selectNodes(n.getChildrenDisplay(), DatasetData.class); } else { p = n.getParentDisplay(); controller.selectNodes(p.getChildrenDisplay(), ProjectData.class); } } else if (o instanceof ScreenData) { if (n.isExpanded()) { controller.selectNodes( n.getChildrenDisplay(), PlateData.class); } else { p = n.getParentDisplay(); controller.selectNodes(p.getChildrenDisplay(), ScreenData.class); } } else if (o instanceof PlateData) { if (n.isExpanded()) { controller.selectNodes( n.getChildrenDisplay(), PlateAcquisitionData.class); } else { p = n.getParentDisplay(); controller.selectNodes(p.getChildrenDisplay(), PlateData.class); } } else if (o instanceof TagAnnotationData) { TagAnnotationData tag = (TagAnnotationData) o; String ns = tag.getNameSpace(); if (TagAnnotationData.INSIGHT_TAGSET_NS.equals(ns)) { //dealing with a tag set. if (n.isExpanded()) { controller.selectNodes(n.getChildrenDisplay(), TagAnnotationData.class); } else { p = n.getParentDisplay(); List l = p.getChildrenDisplay(); Iterator i = l.iterator(); List values = new ArrayList(); TreeImageDisplay node; Object data; while (i.hasNext()) { node = (TreeImageDisplay) i.next(); data = node.getUserObject(); if (data instanceof TagAnnotationData) { tag = (TagAnnotationData) data; ns = tag.getNameSpace(); if (TagAnnotationData.INSIGHT_TAGSET_NS.equals(ns)) values.add(node); } } controller.selectNodes(values, TagAnnotationData.class); } } else { //tag if (n.isExpanded()) { controller.selectNodes(n.getChildrenDisplay(), ImageData.class); } else { p = n.getParentDisplay(); List l = p.getChildrenDisplay(); Iterator i = l.iterator(); List values = new ArrayList(); TreeImageDisplay node; Object data; while (i.hasNext()) { node = (TreeImageDisplay) i.next(); data = node.getUserObject(); if (data instanceof TagAnnotationData) { tag = (TagAnnotationData) data; ns = tag.getNameSpace(); if (!TagAnnotationData.INSIGHT_TAGSET_NS.equals(ns)) values.add(node); } } controller.selectNodes(values, TagAnnotationData.class); } } } else if (o instanceof FileAnnotationData) { p = n.getParentDisplay(); controller.selectNodes(p.getChildrenDisplay(), FileAnnotationData.class); } else if (n instanceof TreeImageTimeSet) { TreeImageTimeSet time = (TreeImageTimeSet) n; if (time.containsImages()) { controller.selectNodes(n.getChildrenDisplay(), ImageData.class); } } else if (n instanceof TreeFileSet) { controller.selectNodes(n.getChildrenDisplay(), FileAnnotationData.class); } } /** * Builds the top nodes of the tree. * * @param exp The experimenter to attach. * @return See above. */ private TreeImageDisplay buildTreeNodes(ExperimenterData exp) { TreeImageDisplay root = getTreeRoot(); TreeImageDisplay node = null; switch (model.getDisplayMode()) { case TreeViewer.GROUP_DISPLAY: if (TreeViewerAgent.isMultiGroups()) { GroupData group = model.getSelectedGroup(); List<TreeImageSet> nodes = createGroups(group); Iterator<TreeImageSet> i = nodes.iterator(); TreeImageSet n; GroupData g; while (i.hasNext()) { n = i.next(); g = (GroupData) n.getUserObject(); n = createGroupNode((TreeImageSet) n); if (g.getId() == group.getId()) node = n; } } else { node = createGroup(model.getSelectedGroup()); node = createGroupNode((TreeImageSet) node); } break; case TreeViewer.EXPERIMENTER_DISPLAY: default: if (model.isSingleGroup()) { node = root; node = createExperimenterNode(exp, node); } else { if (TreeViewerAgent.isMultiGroups()) { GroupData group = model.getSelectedGroup(); List<TreeImageSet> nodes = createGroups(group); Iterator<TreeImageSet> i = nodes.iterator(); TreeImageSet n; GroupData g; while (i.hasNext()) { n = i.next(); g = (GroupData) n.getUserObject(); n = createExperimenterNode(exp, n); if (g.getId() == group.getId()) node = n; } } else { node = createGroup(model.getSelectedGroup()); node = createExperimenterNode(exp, node); } } break; } return node; } /** * Creates the group nodes. * * @param defaultGroup The default group * @return See above. */ private List<TreeImageSet> createGroups(GroupData defaultGroup) { TreeImageDisplay root = getTreeRoot(); DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); //root.addChildDisplay(node); //tm.insertNodeInto(node, root, root.getChildCount()); List<TreeImageSet> l = new ArrayList<TreeImageSet>(); List groups = sorter.sort(TreeViewerAgent.getAvailableUserGroups()); //sort the group first. Iterator i = groups.iterator(); GroupData group; TreeImageSet n = new TreeImageSet(defaultGroup); TreeViewerTranslator.formatToolTipFor(n); l.add(n); root.addChildDisplay(n); tm.insertNodeInto(n, root, root.getChildCount()); while (i.hasNext()) { group = (GroupData) i.next(); if (group.getId() != defaultGroup.getId()) { n = new TreeImageSet(group); TreeViewerTranslator.formatToolTipFor(n); l.add(n); root.addChildDisplay(n); tm.insertNodeInto(n, root, root.getChildCount()); } } return l; } /** * Creates the group node. * * @param group The group to add. * @return See above. */ private TreeImageSet createGroup(GroupData group) { TreeImageDisplay root = getTreeRoot(); DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); //root.addChildDisplay(node); //tm.insertNodeInto(node, root, root.getChildCount()); TreeImageSet n = new TreeImageSet(group); TreeViewerTranslator.formatToolTipFor(n); root.addChildDisplay(n); tm.insertNodeInto(n, root, root.getChildCount()); return n; } /** * Helper method to create the trees hosting the display. * * @param exp The logged in experimenter. */ private void createTrees(ExperimenterData exp) { treeDisplay = new DnDTree(model.getUserID(), TreeViewerAgent.isAdministrator()); treeDisplay.addPropertyChangeListener(this); String key = "meta pressed A"; if (UIUtilities.isWindowsOS()) key = "ctrl pressed A"; KeyStroke ks = KeyStroke.getKeyStroke(key); treeDisplay.getInputMap().put(ks, "none"); treeDisplay.setVisible(true); treeDisplay.setRootVisible(false); ToolTipManager.sharedInstance().registerComponent(treeDisplay); treeDisplay.setCellRenderer(new TreeCellRenderer(model.getUserID())); treeDisplay.setShowsRootHandles(true); TreeImageSet root = new TreeImageSet(""); treeDisplay.setModel(new DefaultTreeModel(root)); if (model.getBrowserType() != Browser.ADMIN_EXPLORER) { TreeImageDisplay node = buildTreeNodes(exp); if (node != null) treeDisplay.collapsePath(new TreePath(node.getPath())); } //Add Listeners //treeDisplay.requestFocus(); treeDisplay.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { mousePressedTime = e.getWhen(); rightClickPad = UIUtilities.isMacOS() && SwingUtilities.isLeftMouseButton(e) && e.isControlDown(); rightClickButton = SwingUtilities.isRightMouseButton(e); ctrl = e.isControlDown(); if (UIUtilities.isMacOS()) ctrl = e.isMetaDown(); leftMouseButton = SwingUtilities.isLeftMouseButton(e); if (UIUtilities.isMacOS() || UIUtilities.isLinuxOS()) onClick(e, false); } public void mouseReleased(MouseEvent e) { leftMouseButton = SwingUtilities.isLeftMouseButton(e); if (UIUtilities.isWindowsOS()) onClick(e, true); } // public void mouseMoved(MouseEvent e) { rollOver(e); } }); treeDisplay.addMouseMotionListener(new MouseMotionAdapter() { public void mouseMoved(MouseEvent e) { rollOver(e); } }); treeDisplay.addTreeExpansionListener(listener); selectionListener = new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { event = e; eventHandledTime = System.currentTimeMillis(); if (delayedHandlingTreeSelection) /* mouse click delayed handling until this event occurred */ handleTreeSelection(); switch (keyEvent) { case KeyEvent.VK_DOWN: case KeyEvent.VK_UP: TreePath[] paths = treeDisplay.getSelectionPaths(); if (paths != null) controller.onClick(Arrays.asList(paths)); else controller.onClick(new ArrayList<TreePath>()); break; } } }; treeDisplay.addTreeSelectionListener(selectionListener); //remove standard behaviour treeDisplay.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { ctrl = false; switch (e.getKeyCode()) { case KeyEvent.VK_ENTER: ViewCmd cmd = new ViewCmd(model.getParentModel(), true); cmd.execute(); break; case KeyEvent.VK_DELETE: switch (model.getState()) { case Browser.LOADING_DATA: case Browser.LOADING_LEAVES: //case Browser.COUNTING_ITEMS: break; default: model.delete(); } break; case KeyEvent.VK_CONTROL: if (!UIUtilities.isMacOS()) ctrl = true; break; case KeyEvent.VK_META: if (UIUtilities.isMacOS()) ctrl = true; break; case KeyEvent.VK_A: if (UIUtilities.isWindowsOS() && e.isControlDown() || !UIUtilities.isWindowsOS() && e.isMetaDown()) { handleMultiSelection(); } break; case KeyEvent.VK_DOWN: case KeyEvent.VK_UP: case KeyEvent.VK_RIGHT: keyEvent = e.getKeyCode(); break; case KeyEvent.VK_LEFT: TreePath[] paths = treeDisplay.getSelectionPaths(); TreeImageDisplay node; Object o; for (int i = 0; i < paths.length; i++) { o = paths[i].getLastPathComponent(); if (o instanceof TreeImageDisplay) { node = (TreeImageDisplay) o; if (node.isExpanded()) node.setExpanded(false); } } } } public void keyReleased(KeyEvent e) { ctrl = false; keyEvent = -1; } }); } /** * Adds the nodes to the specified parent. * * @param parent The parent node. * @param nodes The list of nodes to add. * @param tm The tree model. */ private void buildTreeNode(TreeImageDisplay parent, Collection nodes, DefaultTreeModel tm) { if (nodes.size() == 0) { tm.insertNodeInto(new DefaultMutableTreeNode(Browser.EMPTY_MSG), parent, parent.getChildCount()); return; } Iterator i = nodes.iterator(); TreeImageDisplay display; List children; parent.removeAllChildren(); int browserType = model.getBrowserType(); Object uo; while (i.hasNext()) { display = (TreeImageDisplay) i.next(); tm.insertNodeInto(display, parent, parent.getChildCount()); if (display instanceof TreeImageSet) { children = display.getChildrenDisplay(); if (children.size() > 0) { if (display.containsImages()) { display.setExpanded(true); setExpandedParent(display, false); nodesToReset.add(display); buildTreeNode(display, prepareSortedList(sorter.sort(children)), tm); expandNode(display); tm.reload(display); } else { if (browserType == Browser.TAGS_EXPLORER) { if (display.isExpanded()) { setExpandedParent(display, false); nodesToReset.add(display); } buildTreeNode(display, prepareSortedList(sorter.sort(children)), tm); if (display.isExpanded()) { expandNode(display); tm.reload(display); } } else { if (display.isExpanded()) { setExpandedParent(display, true); nodesToReset.add(display); } buildTreeNode(display, prepareSortedList(sorter.sort(children)), tm); } } } else { uo = display.getUserObject(); if (uo instanceof DatasetData) { tm.insertNodeInto(new DefaultMutableTreeNode(Browser.EMPTY_MSG), display, display.getChildCount()); } else if (uo instanceof TagAnnotationData) { TagAnnotationData tag = (TagAnnotationData) uo; if (!(TagAnnotationData.INSIGHT_TAGSET_NS.equals( tag.getNameSpace()))) { tm.insertNodeInto( new DefaultMutableTreeNode(Browser.EMPTY_MSG), display, display.getChildCount()); } } else if (uo instanceof GroupData) { tm.insertNodeInto(new DefaultMutableTreeNode(Browser.EMPTY_MSG), display, display.getChildCount()); } else if (uo instanceof FileAnnotationData) { if (browserType == Browser.SCREENS_EXPLORER) { TreeImageSet n = new TreeImageSet(uo); tm.insertNodeInto( new DefaultMutableTreeNode(Browser.EMPTY_MSG), n, n.getChildCount()); } } } } } if (parent.isExpanded()) { expandNode(parent); tm.reload(parent); } } /** * Sets the value of the <code>expanded</code> flag for the parent of * the specified node. * * @param n The node to handle. * @param b The value to set. */ private void setExpandedParent(TreeImageDisplay n, boolean b) { TreeImageDisplay p = n.getParentDisplay(); if (p != null) { p.setExpanded(b); setExpandedParent(p, b); } } /** * Adds a dummy node to the specified node. * * @param node The parent node. */ private void buildEmptyNode(DefaultMutableTreeNode node) { DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); tm.insertNodeInto(new DefaultMutableTreeNode(Browser.EMPTY_MSG), node, node.getChildCount()); } /** * Creates the smart folders added to the passed node. * * @param parent The parent of the smart folder. */ private void buildOrphanImagesNode(TreeImageDisplay parent) { Registry reg = TreeViewerAgent.getRegistry(); //First check if we had the orphaned images node Boolean value = Boolean.parseBoolean((String) reg.lookup( LookupNames.ORPHANED_IMAGE_ENABLED)); if (value != null && !value.booleanValue()) { boolean enabled = false; if (TreeViewerAgent.isAdministrator()) { enabled = true; } else { //check group owner. long expID = TreeViewerAgent.getUserDetails().getId(); if (expID == parent.getUserObjectId()) { enabled = true; } else { TreeImageDisplay node = parent.getParentDisplay(); long id = -1; if (node != null) { id = node.getUserObjectId(); if (id == -1) { //only in one group id = TreeViewerAgent.getUserDetails().getGroupId(); } } Set leaders = TreeViewerAgent.getGroupsLeaderOf(); if (CollectionUtils.isNotEmpty(leaders)) { Iterator i = leaders.iterator(); while (i.hasNext()) { GroupData type = (GroupData) i.next(); if (id == type.getId()) { enabled = true; } } } } } if (!enabled) { return; } } DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); TreeFileSet node = new TreeFileSet(TreeFileSet.ORPHANED_IMAGES); String v = (String) reg.lookup(LookupNames.ORPHANED_IMAGE_NAME); if (CommonsLangUtils.isNotBlank(v)) { node.setUserObject(v); } v = (String) reg.lookup(LookupNames.ORPHANED_IMAGE_DESCRIPTION); if (CommonsLangUtils.isNotBlank(v)) { node.setToolTip(v); } buildEmptyNode(node); node.setNumberItems(-1); parent.addChildDisplay(node); tm.insertNodeInto(node, parent, parent.getChildCount()); } /** * Sorts the children of the passed node. * * @param node The node to handle. */ private void sortNode(TreeImageSet node) { DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); List children = node.getChildrenDisplay(); Iterator j; if (node.containsImages()) { node.removeAllChildren(); dtm.reload(node); if (children.size() != 0) { buildTreeNode(node, sorter.sort(children), dtm); } else buildEmptyNode(node); j = nodesToReset.iterator(); while (j.hasNext()) { setExpandedParent((TreeImageDisplay) j.next(), true); } } else { if (children.size() != 0) { j = children.iterator(); while (j.hasNext()) sortNode((TreeImageSet) j.next()); } else buildEmptyNode(node); } } /** * Refreshes the passed folder node. * * @param node The node to refresh. * @param elements The elements to add. */ private void refreshFolderNode(TreeImageSet node, Set elements) { node.removeAllChildren(); node.removeAllChildrenDisplay(); Iterator k = elements.iterator(); TreeImageDisplay child; while (k.hasNext()) { child = (TreeImageDisplay) k.next(); node.addChildDisplay(child); } buildTreeNode(node, sorter.sort(elements), (DefaultTreeModel) treeDisplay.getModel()); node.setExpanded(true); expandNode(node); } /** * Organizes the sorted list so that the Project/Screen/Tag Set * are displayed first. * * @param sorted The collection to organize. * @return See above. */ private List prepareSortedList(List sorted) { List<TreeImageDisplay> top = new ArrayList<TreeImageDisplay>(); List<TreeImageDisplay> bottom = new ArrayList<TreeImageDisplay>(); List<TreeImageDisplay> top2 = new ArrayList<TreeImageDisplay>(); List<TreeImageDisplay> bottom2 = new ArrayList<TreeImageDisplay>(); Iterator j = sorted.iterator(); TreeImageDisplay object; Object uo; while (j.hasNext()) { object = (TreeImageDisplay) j.next(); uo = object.getUserObject(); if (uo instanceof ProjectData) top.add(object); else if (uo instanceof GroupData) top.add(object); else if (uo instanceof ScreenData) top2.add(object); else if (uo instanceof DatasetData) bottom.add(object); else if (uo instanceof PlateData) bottom2.add(object); else if (uo instanceof PlateAcquisitionData) bottom2.add(object); else if (uo instanceof TagAnnotationData) { if (TagAnnotationData.INSIGHT_TAGSET_NS.equals( ((TagAnnotationData) uo).getNameSpace())) top.add(object); else bottom.add(object); } else if (uo instanceof File) { File f = (File) uo; if (f.isDirectory()) { if (((TreeImageSet) object).isSystem()) top.add(object); else bottom.add(object); } else top.add(object); } else if (uo instanceof FileData) { FileData f = (FileData) uo; if (f.isDirectory()) top.add(object); else bottom.add(object); } else if (uo instanceof ImageData) bottom.add(object); else if (uo instanceof ExperimenterData) bottom.add(object); else if (object instanceof SmartFolder || object instanceof TreeFileSet) bottom2.add(object); } List<TreeImageDisplay> all = new ArrayList<TreeImageDisplay>(); if (top.size() > 0) all.addAll(top); if (bottom.size() > 0) all.addAll(bottom); if (top2.size() > 0) all.addAll(top2); if (bottom2.size() > 0) all.addAll(bottom2); return all; } /** * Refreshes the folder hosting the file. * * @param expNode The experimenter node to refresh. * @param r The data to display. */ private void refreshFileFolder(TreeImageDisplay expNode, Map<Integer, Set> r) { DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); expNode.setChildrenLoaded(Boolean.valueOf(true)); expNode.setExpanded(Boolean.valueOf(true)); if (r == null || r.size() == 0) return; Iterator i = r.keySet().iterator(); int index; int n = expNode.getChildCount(); TreeFileSet node; dtm.reload(); while (i.hasNext()) { index = (Integer) i.next(); for (int j = 0; j < n; j++) { node = (TreeFileSet) expNode.getChildAt(j); if (node.getType() == index) refreshFolderNode(node, r.get(index)); } } setExpandedParent(expNode, true); } /** * Refreshes the folder hosting the time. * * @param expNode The experimenter node to refresh. * @param r The data to display. */ private void refreshTimeFolder(TreeImageDisplay expNode, Map<Integer, Set> r) { DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); expNode.setChildrenLoaded(Boolean.TRUE); expNode.setExpanded(true); if (r == null || r.size() == 0) return; Iterator i = r.keySet().iterator(); int index; int n = expNode.getChildCount(); TreeImageTimeSet node, child; dtm.reload(); int nodeType; List children; Iterator s; while (i.hasNext()) { index = (Integer) i.next(); for (int j = 0; j < n; j++) { node = (TreeImageTimeSet) expNode.getChildAt(j); nodeType = node.getType(); switch (nodeType) { case TreeImageTimeSet.YEAR: case TreeImageTimeSet.YEAR_BEFORE: children = node.getChildrenDisplay(); s = children.iterator(); while (s.hasNext()) { child = (TreeImageTimeSet) s.next(); if (child.getIndex() == index) refreshFolderNode(child, r.get(index)); } default: if (node.getIndex() == index) refreshFolderNode(node, r.get(index)); break; } } } setExpandedParent(expNode, true); } /** * Creates a new instance. * The {@link #initialize(BrowserControl, BrowserModel) initialize} method * should be called straight after to link this View to the Controller. */ BrowserUI() { sorter = new ViewerSorter(); nodesToReset = new HashSet<TreeImageDisplay>(); listener = new TreeExpansionListener() { public void treeCollapsed(TreeExpansionEvent e) { onNodeNavigation((TreeImageDisplay) e.getPath().getLastPathComponent(), false); } public void treeExpanded(TreeExpansionEvent e) { onNodeNavigation((TreeImageDisplay) e.getPath().getLastPathComponent(), true); } }; } /** * Links this View to its Controller and its Model. * * @param controller The Controller. * @param model The Model. * @param exp The experimenter the tree view is for. */ void initialize(BrowserControl controller, BrowserModel model, ExperimenterData exp) { if (controller == null) throw new IllegalArgumentException("Controller cannot be null"); if (model == null) throw new IllegalArgumentException("Model cannot be null"); this.controller = controller; this.model = model; createMenuBars(); createTrees(exp); buildGUI(); } /** * Creates a dummy loading node whose parent is the specified node. * * @param parent The parent node. */ void loadAction(TreeImageDisplay parent) { DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); parent.removeAllChildren(); tm.insertNodeInto(new DefaultMutableTreeNode(Browser.LOADING_MSG), parent, parent.getChildCount()); tm.reload(parent); } /** * Returns <code>true</code> if the first child of the passed node * is one of the added element. * * @param parent The node to handle. * @return See above. */ boolean isFirstChildMessage(TreeImageDisplay parent) { int n = parent.getChildCount(); if (n == 0) return true; DefaultMutableTreeNode node = (DefaultMutableTreeNode) parent.getChildAt(0); Object uo = node.getUserObject(); if (Browser.LOADING_MSG.equals(uo) || Browser.EMPTY_MSG.equals(uo)) return true; return false; } /** * Returns the tree hosting the display. * * @return See above. */ JTree getTreeDisplay() { return treeDisplay; } /** * Returns the root node of the tree. * * @return See above. */ TreeImageDisplay getTreeRoot() { if (treeDisplay == null) return null; DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); if (dtm == null) return null; return (TreeImageDisplay) dtm.getRoot(); } /** * Returns the title of the Browser according to the type. * * @return See above. */ String getBrowserTitle() { switch (model.getBrowserType()) { case Browser.PROJECTS_EXPLORER: return Browser.HIERARCHY_TITLE; case Browser.IMAGES_EXPLORER: return Browser.IMAGES_TITLE; case Browser.TAGS_EXPLORER: return Browser.TAGS_TITLE; case Browser.SCREENS_EXPLORER: return Browser.SCREENS_TITLE; case Browser.FILES_EXPLORER: return Browser.FILES_TITLE; case Browser.FILE_SYSTEM_EXPLORER: return Browser.FILE_SYSTEM_TITLE; case Browser.ADMIN_EXPLORER: return Browser.ADMIN_TITLE; } return ""; } /** * Selects the specified node. * * @param node The node to select. */ void selectFoundNode(TreeImageDisplay node) { TreePath path = new TreePath(node.getPath()); treeDisplay.setSelectionPath(path); TreeCellRenderer renderer = (TreeCellRenderer) treeDisplay.getCellRenderer(); //treeDisplay.requestFocus(); renderer.getTreeCellRendererComponent(treeDisplay, node, treeDisplay.isPathSelected(path), false, true, 0, false); } /** * Collapses the specified node. To avoid loop, we first need to * remove the <code>TreeExpansionListener</code>. * * @param node The node to collapse. */ void collapsePath(DefaultMutableTreeNode node) { //First remove listener otherwise an event is fired. treeDisplay.removeTreeExpansionListener(listener); treeDisplay.collapsePath(new TreePath(node.getPath())); treeDisplay.addTreeExpansionListener(listener); } /** * Collapses the node when an on-going data loading is cancelled. * * @param node The node to collapse. */ void cancel(DefaultMutableTreeNode node) { if (node == null) return; if (node.getChildCount() <= 1) { if (node.getUserObject() instanceof String) { node.removeAllChildren(); buildEmptyNode(node); } } //in this order otherwise the node is not collapsed. ((DefaultTreeModel) treeDisplay.getModel()).reload(node); collapsePath(node); } /** * Update the specified set of nodes. * * @param nodes The collection of nodes to update. * @param object The <code>DataObject</code> to update. */ void updateNodes(List nodes, DataObject object) { DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); Iterator i = nodes.iterator(); TreeImageDisplay node; while (i.hasNext()) { node = (TreeImageDisplay) i.next(); node.setUserObject(object); dtm.nodeChanged(node); } } /** * Removes the specified set of nodes from the tree. * * @param nodes The collection of nodes to remove. * @param parentDisplay The selected parent. */ void removeNodes(List nodes, TreeImageDisplay parentDisplay) { if (parentDisplay == null) parentDisplay = getTreeRoot(); Iterator i = nodes.iterator(); TreeImageDisplay node; TreeImageDisplay parent; DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); while (i.hasNext()) { node = (TreeImageDisplay) i.next(); parent = node.getParentDisplay(); if (parent.isChildrenLoaded()) { parent.removeChildDisplay(node); parent.remove(node); dtm.reload(parent); if (parent.equals(parentDisplay)) treeDisplay.setSelectionPath( new TreePath(parent.getPath())); } } } /** * Adds the newly created node to the tree. * * @param nodes The collection of the parent nodes. * @param newNode The node to add to the parent. * @param parentDisplay The selected parent. */ void createNodes(List nodes, TreeImageDisplay newNode, TreeImageDisplay parentDisplay) { if (parentDisplay == null) parentDisplay = getTreeRoot(); Iterator i = nodes.iterator(); TreeImageDisplay parent; List list; Iterator j; DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); //buildEmptyNode(newNode); boolean toLoad = false; TreeImageDisplay n; while (i.hasNext()) { parent = (TreeImageDisplay) i.next(); //problem will come when we have images if (parent.isChildrenLoaded()) { parent.addChildDisplay(newNode); list = prepareSortedList( sorter.sort(parent.getChildrenDisplay())); parent.removeAllChildren(); j = list.iterator(); while (j.hasNext()) { n = (TreeImageDisplay) j.next(); if (!n.isChildrenLoaded()) { n.removeAllChildren(); buildEmptyNode(n); } dtm.insertNodeInto(n, parent, parent.getChildCount()); } dtm.reload(parent); expandNode(parent); if (parent.equals(parentDisplay)) treeDisplay.setSelectionPath( new TreePath(newNode.getPath())); } else { //Only the currently selected one will be loaded. if (parent.equals(parentDisplay)) toLoad = true; } } //should be leaves. Need to review that code. if (toLoad) { //TO BE MODIFIED //if (parentDisplay.getParentDisplay() == null) //root // controller.loadData(); //else controller.loadLeaves(); } } /** * Sorts the nodes in the tree view according to the specified index. * * @param type One out of the following constants: * {@link Browser#SORT_NODES_BY_DATE} or * {@link Browser#SORT_NODES_BY_NAME}. */ void sortNodes(int type) { boolean b = type == Browser.SORT_NODES_BY_DATE; sorter.setByDate(b); sorter.setAscending(!b); DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); TreeImageDisplay root = (TreeImageDisplay) dtm.getRoot(); int n = root.getChildCount(); TreeImageDisplay node; List children; Iterator j; List all; switch (model.getBrowserType()) { case Browser.IMAGES_EXPLORER: case Browser.FILES_EXPLORER: for (int i = 0; i < n; i++) { node = (TreeImageDisplay) root.getChildAt(i); children = node.getChildrenDisplay(); j = children.iterator(); TreeImageDisplay child; while (j.hasNext()) { child = (TreeImageDisplay) j.next(); if (child instanceof TreeImageTimeSet || child instanceof TreeFileSet) sortNode((TreeImageSet) child); } } break; case Browser.ADMIN_EXPLORER: for (int i = 0; i < n; i++) { node = (TreeImageDisplay) root.getChildAt(i); children = node.getChildrenDisplay(); node.removeAllChildren(); dtm.reload(node); if (!children.isEmpty()) { if (node.getUserObject() instanceof GroupData) { all = prepareSortedList(sorter.sort(children)); buildTreeNode(node, all, dtm); } else { buildTreeNode(node, sorter.sort(children), dtm); } } else buildEmptyNode(node); j = nodesToReset.iterator(); while (j.hasNext()) { setExpandedParent((TreeImageDisplay) j.next(), true); } } break; default: for (int i = 0; i < n; i++) { node = (TreeImageDisplay) root.getChildAt(i); children = node.getChildrenDisplay(); node.removeAllChildren(); dtm.reload(node); if (!children.isEmpty()) { if (node.getUserObject() instanceof ExperimenterData) { List<Object> sets = new ArrayList<Object>(); List<Object> toSort = new ArrayList<Object>(); Iterator k = children.iterator(); while (k.hasNext()) { Object object = (Object) k.next(); if (object instanceof TreeFileSet) { sets.add((TreeFileSet) object); } else toSort.add(object); } sets.addAll(sorter.sort(toSort)); Collections.reverse(sets); all = prepareSortedList(sets); buildTreeNode(node, all, dtm); } else { buildTreeNode(node, sorter.sort(children), dtm); } } else buildEmptyNode(node); j = nodesToReset.iterator(); while (j.hasNext()) { setExpandedParent((TreeImageDisplay) j.next(), true); } } } } /** * Reacts to state change. * * @param b Pass <code>true</code> to enable the trees, <code>false</code> * otherwise. */ void onStateChanged(boolean b) { model.getParentModel().onComponentStateChange(b); } /** * Enables the components composing the display depending on the specified * parameter. * * @param b Pass <code>true</code> to enable the component, * <code>false</code> otherwise. */ void onComponentStateChange(boolean b) { treeDisplay.setEnabled(b); } /** Resets the UI so that we have no node selected in trees. */ void setNullSelectedNode() { if (getTreeRoot() != null) { treeDisplay.setSelectionRow(-1); } } /** * Returns <code>true</code> if the partial name is displayed, * <code>false</code> otherwise. * * @return See above. */ boolean isPartialName() { return !partialButton.isSelected(); } /** * Removes the collection of <code>TreePath</code>s from the main tree. * We first need to remove the <code>TreeSelectionListener</code> to avoid * loop. * * @param paths Collection of paths to be removed. */ void removeTreePaths(List paths) { //treeDisplay.removeTreeSelectionListener(selectionListener); Iterator j = paths.iterator(); while (j.hasNext()) treeDisplay.removeSelectionPath((TreePath) j.next()); //treeDisplay.addTreeSelectionListener(selectionListener); } /** * Adds the experimenter's data to the passed node. * * @param nodes The data to add. * @param expNode The selected experimenter node. */ void setExperimenterData(Set nodes, TreeImageDisplay expNode) { DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); expNode.removeAllChildren(); expNode.removeAllChildrenDisplay(); expNode.setChildrenLoaded(Boolean.valueOf(true)); dtm.reload(expNode); Iterator i; if (nodes.size() > 0) { boolean createFolder = true; i = nodes.iterator(); TreeImageDisplay node; TreeFileSet n = null; Set<TreeImageDisplay> toKeep = new HashSet<TreeImageDisplay>(); int type; while (i.hasNext()) { node = (TreeImageDisplay) i.next(); if (node instanceof TreeFileSet) { type = ((TreeFileSet) node).getType(); if (type == TreeFileSet.TAG || type == TreeFileSet.ORPHANED_IMAGES) { List l = node.getChildrenDisplay(); if (l.size() > 0) { createFolder = false; n = (TreeFileSet) node.copy(); n.setChildrenLoaded(Boolean.valueOf(true)); expNode.addChildDisplay(n); expandNode(n); } } else { toKeep.add(node); expNode.addChildDisplay(node); } } else { toKeep.add(node); expNode.addChildDisplay(node); } } List sorted = prepareSortedList(sorter.sort(toKeep)); if (n != null) sorted.add(n); buildTreeNode(expNode, sorted, (DefaultTreeModel) treeDisplay.getModel()); if (createFolder) { switch (model.getBrowserType()) { case Browser.TAGS_EXPLORER: if (n == null) createTagsElements(expNode); break; case Browser.PROJECTS_EXPLORER: buildOrphanImagesNode(expNode); } } } else { switch (model.getBrowserType()) { case Browser.TAGS_EXPLORER: createTagsElements(expNode); break; case Browser.PROJECTS_EXPLORER: buildOrphanImagesNode(expNode); break; default: expNode.setExpanded(false); buildEmptyNode(expNode); } } // i = nodesToReset.iterator(); while (i.hasNext()) setExpandedParent((TreeImageDisplay) i.next(), true); TreeImageDisplay root = getTreeRoot(); TreeImageDisplay element, child; Object ho; List children; for (int j = 0; j < root.getChildCount(); j++) { element = (TreeImageDisplay) root.getChildAt(j); ho = element.getUserObject(); if (ho instanceof GroupData && element.isExpanded()) { expandNode(element); children = element.getChildrenDisplay(); if (children != null) { i = children.iterator(); while (i.hasNext()) { child = (TreeImageDisplay) i.next(); if (child.isExpanded()) expandNode(child); } } } } } /** * Sets the nodes hosting the groups to manage. * * @param nodes The nodes to display * @param expanded The list of nodes previously expanded. */ void setGroups(Set nodes, List expanded) { DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); TreeImageDisplay root = getTreeRoot(); root.removeAllChildren(); root.removeAllChildrenDisplay(); root.setChildrenLoaded(Boolean.valueOf(true)); root.setExpanded(true); dtm.reload(); if (nodes.size() != 0) { Iterator i = nodes.iterator(); while (i.hasNext()) { root.addChildDisplay((TreeImageDisplay) i.next()); } buildTreeNode(root, prepareSortedList(sorter.sort(nodes)), dtm); i = nodes.iterator(); while (i.hasNext()) { ((TreeImageDisplay) i.next()).setExpanded(false); } if (expanded != null && expanded.size() > 0) { i = nodes.iterator(); TreeImageDisplay display; GroupData group; Object ho; Set l; GroupData g; while (i.hasNext()) { display = (TreeImageDisplay) i.next(); ho = display.getUserObject(); if (ho instanceof GroupData) { g = (GroupData) ho; if (expanded.contains(g.getId())) { expandNode(display); } } } } } if (TreeViewerAgent.isAdministrator()) { SmartFolder folder = new SmartFolder(GroupData.class, "Experimenters w/o groups"); buildEmptyNode(folder); //root.addChildDisplay(folder); //dtm.insertNodeInto(folder, root, root.getChildCount()); } } /** * Sets the number of items imported during a period of time. * * @param expNode The node hosting the experimenter. * @param index The index of the time node. * @param value The value to set. */ void setCountValues(TreeImageDisplay expNode, int index, Object value) { DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); if (model.getBrowserType() != Browser.TAGS_EXPLORER) expNode.setChildrenLoaded(Boolean.valueOf(true)); int n = expNode.getChildCount(); TreeImageSet node; List l; Iterator i, k; TreeImageTimeSet child; //Test List<TreeImageSet> toRemove = new ArrayList<TreeImageSet>(); List<TreeImageSet> toKeep = new ArrayList<TreeImageSet>(); int number; int total; DefaultMutableTreeNode childNode; List<DefaultMutableTreeNode> remove = new ArrayList<DefaultMutableTreeNode>(); for (int j = 0; j < n; j++) { childNode = (DefaultMutableTreeNode) expNode.getChildAt(j); Object o = childNode.getUserObject(); if (o instanceof String) { String s = (String) o; if (Browser.EMPTY_MSG.equals(s)) { remove.add(childNode); } } if (childNode instanceof TreeImageTimeSet) { node = (TreeImageTimeSet) childNode; if (((TreeImageTimeSet) node).getType() == index) { if (value instanceof Integer) { //Check if the number is 0 int vv = ((Integer) value).intValue(); long v = node.getNumberItems(); node.setNumberItems(vv); if (v == 0 && vv > 0) { buildEmptyNode(node); node.setChildrenLoaded(Boolean.valueOf(false)); } } else if (value instanceof List) { l = (List) value; total = 0; i = node.getChildrenDisplay().iterator(); while (i.hasNext()) { child = (TreeImageTimeSet) i.next(); number = child.countTime(l); total += number; if (number > 0) { child.setNumberItems(number); toKeep.add(child); } else { toRemove.add(child); } } node.removeAllChildren(); node.removeChildrenDisplay(toRemove); node.setNumberItems(total); k = toKeep.iterator(); while (k.hasNext()) { dtm.insertNodeInto((TreeImageTimeSet) k.next(), node, node.getChildCount()); } } dtm.reload(node); } } else if (childNode instanceof TreeFileSet) { node = (TreeFileSet) childNode; if (((TreeFileSet) node).getType() == index) { if (value instanceof Long) node.setNumberItems((Long) value); } } } if (remove.size() > 0) { Iterator<DefaultMutableTreeNode> j = remove.iterator(); while (j.hasNext()) { expNode.remove(j.next()); dtm.reload(expNode); } } } /** * Refreshes the folder hosting the files and the times. * * @param expNode The experimenter node to refresh. * @param r The data to display. */ void refreshFolder(TreeImageDisplay expNode, Map<Integer, Set> r) { int browseType = model.getBrowserType(); if (browseType == Browser.IMAGES_EXPLORER) refreshTimeFolder(expNode, r); else if (browseType == Browser.FILES_EXPLORER) refreshFileFolder(expNode, r); } /** * Adds the specifies nodes to the currently selected * {@link TreeImageDisplay}. * * @param nodes The collection of nodes to add. * @param parent The parent of the nodes. */ void setLeavesViews(Collection nodes, TreeImageSet parent) { DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); parent.removeAllChildren(); parent.removeAllChildrenDisplay(); parent.setChildrenLoaded(Boolean.TRUE); if (nodes.size() != 0) { Iterator i = nodes.iterator(); while (i.hasNext()) parent.addChildDisplay((TreeImageDisplay) i.next()) ; buildTreeNode(parent, sorter.sort(nodes), dtm); } else buildEmptyNode(parent); dtm.reload(parent); if (!isPartialName()) { model.component.accept(new PartialNameVisitor(isPartialName()), TreeImageDisplayVisitor.TREEIMAGE_NODE_ONLY); } } /** * Adds a new experimenter to the tree. * * @param experimenter The experimenter to add. * @param groupNode The node to add the experimenter to. */ void addExperimenter(ExperimenterData experimenter, TreeImageDisplay groupNode) { TreeImageSet node = createExperimenterNode(experimenter, groupNode); } /** * Removes the specified experimenter from the tree. * * @param exp The experimenter data to remove. * @param refNode The node to remove the experimenter from. */ void removeExperimenter(ExperimenterData exp, TreeImageDisplay refNode) { if (model.getBrowserType() == Browser.ADMIN_EXPLORER) return; if (refNode == null) refNode = getTreeRoot(); List<TreeImageDisplay> nodesToKeep; List l = refNode.getChildrenDisplay(); if (l == null || l.size() == 0) return; Iterator j = l.iterator(); TreeImageDisplay element, node; Object ho; ExperimenterData expElement; DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); Iterator k; node = null; nodesToKeep = new ArrayList<TreeImageDisplay>(); List<TreeImageDisplay> toExpand = new ArrayList<TreeImageDisplay>(); while (j.hasNext()) { element = (TreeImageDisplay) j.next(); ho = element.getUserObject(); if (ho instanceof ExperimenterData) { expElement = (ExperimenterData) ho; if (expElement.getId() == exp.getId()) node = element; else { nodesToKeep.add(element); if (element.isExpanded()) toExpand.add(element); } } } if (node != null) refNode.removeChildDisplay(node); k = nodesToKeep.iterator(); refNode.removeAllChildren(); while (k.hasNext()) { tm.insertNodeInto((TreeImageSet) k.next(), refNode, refNode.getChildCount()); } tm.reload(refNode); k = toExpand.iterator(); while (k.hasNext()) { expandNode((TreeImageSet) k.next(), false); } } /** * Removes the specified group from the tree. * * @param group The data to remove. */ void removeGroup(GroupData group) { if (model.getBrowserType() == Browser.ADMIN_EXPLORER) return; TreeImageDisplay root = getTreeRoot(); List<TreeImageDisplay> nodesToKeep; List l = root.getChildrenDisplay(); if (l == null || l.size() == 0) return; Iterator j = l.iterator(); TreeImageDisplay element, n, node; Object ho; ExperimenterData expElement; DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); Iterator k; GroupData g; nodesToKeep = new ArrayList<TreeImageDisplay>(); node = null; while (j.hasNext()) { element = (TreeImageDisplay) j.next(); if (element.getUserObject() instanceof GroupData) { g = (GroupData) element.getUserObject(); if (g.getId() == group.getId()) node = element; else nodesToKeep.add(element); } } if (node != null) root.removeChildDisplay(node); k = nodesToKeep.iterator(); root.removeAllChildren(); while (k.hasNext()) { tm.insertNodeInto((TreeImageSet) k.next(), root, root.getChildCount()); } tm.reload(); } /** Reactivates the tree. */ void reActivate() { clear(); treeDisplay.reset(model.getUserID(), TreeViewerAgent.isAdministrator()); TreeCellRenderer renderer = (TreeCellRenderer) treeDisplay.getCellRenderer(); renderer.reset(model.getUserID()); if (model.getBrowserType() != Browser.ADMIN_EXPLORER) { ExperimenterData exp = model.getUserDetails(); TreeImageDisplay node = buildTreeNodes(exp); if (model.isSelected() && node != null) { expandNode(node, true); } } DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); tm.reload(); } /** Clear the tree.*/ void clear() { TreeImageDisplay root = getTreeRoot(); root.removeAllChildren(); root.removeAllChildrenDisplay(); DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); tm.reload(); } /** * Sets the nodes selecting via other views. * * @param newSelection The collection of nodes to select. */ void setFoundNode(TreeImageDisplay[] newSelection) { if (newSelection == null) { model.setSelectedDisplay(null, true); treeDisplay.clearSelection(); controller.getAction(BrowserControl.DELETE).setEnabled(false); } else { TreePath[] paths = new TreePath[newSelection.length]; for (int i = 0; i < newSelection.length; i++) { paths[i] = new TreePath(newSelection[i].getPath()); } treeDisplay.setSelectionPaths(paths); } treeDisplay.repaint(); } /** * Loads the files contained in the passed folder. * * @param display The directory. */ void loadFile(TreeImageDisplay display) { //if ((uo instanceof File) && (display instanceof TreeImageSet)) { display.removeAllChildren(); display.removeAllChildrenDisplay(); display.setChildrenLoaded(Boolean.TRUE); display.setExpanded(true); transformDirectory((TreeImageSet) display); buildTreeNode(display, prepareSortedList(sorter.sort( display.getChildrenDisplay())), (DefaultTreeModel) treeDisplay.getModel()); DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); tm.reload(display); //model.fireFilesCheck(leaves); } /** * Loads the file system. * * @param refresh Pass <code>true</code> to rebuild the file system view. * <code>false</code> otherwise. */ void loadFileSystem(TreeImageDisplay expNode) { if (model.getBrowserType() != Browser.FILE_SYSTEM_EXPLORER) return; setExperimenterData(createFileSystemExplorer(expNode), expNode); } /** * Reloads the specified node. * * @param node The node to reload. */ void reloadContainer(TreeImageDisplay node) { if (node == null) return; node.removeAllChildren(); node.removeAllChildrenDisplay(); node.setChildrenLoaded(Boolean.FALSE); buildEmptyNode(node); treeDisplay.collapsePath(new TreePath(node.getPath())); treeDisplay.expandPath(new TreePath(node.getPath())); DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); tm.reload(node); } /** * Reloads the specified node. * * @param node The node to reload. */ void reloadNode(TreeImageDisplay node) { DefaultTreeModel tm = (DefaultTreeModel) treeDisplay.getModel(); if (node == null) tm.reload(); else tm.reload(node); } /** * Expands the specified node. To avoid loop, we first need to * remove the <code>TreeExpansionListener</code>. * * @param node The node to expand. */ void expandNode(TreeImageDisplay node, boolean withListener) { //First remove listener otherwise an event is fired. if (node == null) return; node.setExpanded(true); if (withListener) { treeDisplay.expandPath(new TreePath(node.getPath())); } else { treeDisplay.removeTreeExpansionListener(listener); treeDisplay.expandPath(new TreePath(node.getPath())); treeDisplay.addTreeExpansionListener(listener); } } /** * Expands the specified node. To avoid loop, we first need to * remove the <code>TreeExpansionListener</code>. * * @param node The node to expand. */ void expandNode(TreeImageDisplay node) { expandNode(node, false); } /** * Adds the component under the tree. * * @param component The component to add. */ void addComponent(JComponent component) { if (bottomComponent != null) remove(bottomComponent); bottomComponent = component; if (component != null) add(bottomComponent, BorderLayout.SOUTH); revalidate(); repaint(); } /** * Adds the specified groups to the tree. * * @param group The group to add. */ void setUserGroup(List<GroupData> groups) { if (groups == null || groups.size() == 0) return; ExperimenterData exp = model.getUserDetails(); TreeImageSet node; Iterator<GroupData> i = groups.iterator(); while (i.hasNext()) { node = createGroup(i.next()); node = createExperimenterNode(exp, node); } DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel(); dtm.reload(); } /** * Reacts to the D&D properties. * @see PropertyChangeListener#propertyChange(PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (DnDTree.DRAGGED_PROPERTY.equals(name)) { ObjectToTransfer transfer = (ObjectToTransfer) evt.getNewValue(); int action = TreeViewer.CUT_AND_PASTE; if (transfer.getDropAction() == DnDConstants.ACTION_COPY) action = TreeViewer.COPY_AND_PASTE; //check the node model.transfer(transfer.getTarget(), transfer.getNodes(), action); } } }