/* *------------------------------------------------------------------------------ * 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.util; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.ToolTipManager; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import info.clearthought.layout.TableLayout; import org.apache.commons.collections.CollectionUtils; import org.openmicroscopy.shoola.util.CommonsLangUtils; import org.openmicroscopy.shoola.agents.util.browser.TreeImageDisplay; import org.openmicroscopy.shoola.agents.util.browser.TreeImageSet; import org.openmicroscopy.shoola.agents.util.browser.TreeViewerTranslator; import org.openmicroscopy.shoola.util.ui.IconManager; import org.openmicroscopy.shoola.util.ui.MessageBox; import org.openmicroscopy.shoola.util.ui.NotificationDialog; import org.openmicroscopy.shoola.util.ui.UIUtilities; import omero.gateway.model.AnnotationData; import omero.gateway.model.DataObject; import omero.gateway.model.DatasetData; import omero.gateway.model.ExperimenterData; import omero.gateway.model.FileAnnotationData; import omero.gateway.model.GroupData; import omero.gateway.model.TagAnnotationData; /** * Provided UI to select between two lists of objects. * * @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 * @since 3.0-Beta4 */ public class SelectionWizardUI extends JPanel implements ActionListener, DocumentListener { /** Bound property indicating that the selection has changed. */ public static final String SELECTION_CHANGE = "selectionChange"; /** * Bound property indicating that a new item is selected in the * available items tree. */ static final String AVAILABLE_SELECTION_CHANGE = "availableSelectionChange"; /** All tags regardless of who is the owner will be displayed.*/ static Integer ALL = 0; /** Tags owned by logged in user will be displayed.*/ static int CURRENT = 1; /** Tags not owned by logged in user will be displayed.*/ static int OTHERS = 2; /** The default text for the filter dialog.*/ private static final String DEFAULT_FILTER_TEXT = "Filter"; /** Action command ID to add a field to the result table. */ private static final int ADD = 0; /** Action command ID to remove a field from the result table. */ private static final int REMOVE = 1; /** Action command ID to add all fields to the result table. */ private static final int ADD_ALL = 2; /** Action command ID to remove all fields from the result table. */ private static final int REMOVE_ALL = 3; /** The original items before the user selects items. */ private List<TreeImageDisplay> originalItems; /** The original selected items before the user selects items. */ private List<TreeImageDisplay> originalSelectedItems; /** Collection of available items. */ private List<TreeImageDisplay> availableItems; /** Collection of all the selected items. */ private List<TreeImageDisplay> selectedItems; /** The list box showing the available items. */ private JTree availableItemsListbox; /** The list box showing the selected items. */ private JTree selectedItemsListbox; /** The button to move an item from the remaining items to current items. */ private JButton addButton; /** The button to move an item from the current items to remaining items. */ private JButton removeButton; /** The button to move all items to the current items. */ private JButton addAllButton; /** The button to move all items to the remaining items. */ private JButton removeAllButton; /** Sorts the object. */ private ViewerSorter sorter; /** The type to handle. */ private Class<?> type; /** The collection of immutable nodes. */ private Collection immutable; /** The group available.*/ private Collection<GroupData> groups; /** Filter the data.*/ private JTextField filterArea; /** Flag indicating to filter with letter anywhere in the text.*/ private boolean filterAnywhere; /** The original color of a text field.*/ private Color originalColor; /** The parent dialog.*/ private JDialog view; /** The user currently logged in.*/ private ExperimenterData user; private int ownerFilterIndex; /** * Returns <code>true</code> if the item is already selected or is * an item to create, <code>false</code> otherwise. * * @param elt The element to handle. * @return See above. */ private boolean isSelected(Object elt) { if (elt instanceof TreeImageDisplay) { DataObject n = (DataObject) ((TreeImageDisplay) elt).getUserObject(); for (TreeImageDisplay item : selectedItems) { DataObject data = (DataObject) item.getUserObject(); if (n.getId() == data.getId()) { return true; } } } return false; } /** * Filters the data according to ownership. * * @param value The value to check * @param txt The text of reference. * @param data The data object of reference. * @return */ private boolean filterItem(String value, String txt, DataObject data) { if (!(data instanceof ExperimenterData || data instanceof GroupData)) { ExperimenterData exp = data.getOwner(); if (ownerFilterIndex == CURRENT) { if (exp.getId() != user.getId()) return false; } else if (ownerFilterIndex == OTHERS) { if (exp.getId() == user.getId()) return false; } } if (filterAnywhere) { return value.contains(txt); } return value.startsWith(txt); } /** * Filters the list of displayed items. * * @param insert Pass <code>true</code> when inserting new character * <code>false</code> when removing. */ private void filter(boolean insert) { String txt = filterArea.getText(); if (DEFAULT_FILTER_TEXT.equals(txt)) { return; } filterArea.setForeground(originalColor); List<TreeImageDisplay> ref; Iterator<TreeImageDisplay> i; TreeImageDisplay node, child; Object ho; String value; if (insert) { ref = availableItems; } else { ref = new ArrayList<TreeImageDisplay>(); for (TreeImageDisplay item : originalItems) { if (!isSelected(item)) { ref.add(item); } } for (TreeImageDisplay item : originalSelectedItems) { if (!isSelected(item) && !isChild(item)) { ref.add(item); } } } i = ref.iterator(); txt = txt.toLowerCase(); List<TreeImageDisplay> toKeep = new ArrayList<TreeImageDisplay>(); while (i.hasNext()) { node = i.next(); ho = node.getUserObject(); value = null; if (ho instanceof TagAnnotationData) { TagAnnotationData tag = (TagAnnotationData) ho; if (!TagAnnotationData.INSIGHT_TAGSET_NS.equals( tag.getNameSpace())) { value = tag.getTagValue(); } else { List l = node.getChildrenDisplay(); Iterator j = l.iterator(); while (j.hasNext()) { child = (TreeImageDisplay) j.next(); if (!isSelected(child)) { ho = child.getUserObject(); if (ho instanceof TagAnnotationData) { tag = (TagAnnotationData) ho; value = tag.getTagValue(); value = value.toLowerCase(); if (filterItem(value, txt, (DataObject) ho)) { toKeep.add(node); break; } } } } value = null; } } else if (ho instanceof FileAnnotationData) { value = ((FileAnnotationData) ho).getFileName(); } else if (ho instanceof DataObject) { value = node.getNodeName(); } if (value != null) { value = value.toLowerCase(); if (filterItem(value, txt, (DataObject) ho)) { toKeep.add(node); } } } availableItems.clear(); availableItems.addAll(toKeep); availableItems = sorter.sort(availableItems); populateTreeItems(availableItemsListbox, availableItems); //Select the first not //Get the first node. if (CollectionUtils.isNotEmpty(availableItems) && CommonsLangUtils.isNotBlank(txt)) { node = availableItems.get(0); TreePath path = null; if (node.hasChildrenDisplay() && node.getChildCount() > 0) { child = (TreeImageDisplay) node.getFirstChild(); path = new TreePath(child.getPath()); } else { path = new TreePath(node.getPath()); } if (path != null) { availableItemsListbox.setSelectionPath(path); availableItemsListbox.scrollPathToVisible(path); availableItemsListbox.repaint(); } } } /** * Returns the node found if any. * * @param object The object to handle. * @param selected From the currently selected tag. * @return See above. */ private TreeImageDisplay doesObjectExist(DataObject object, boolean selected) { if (object == null) return null; if (object instanceof TagAnnotationData) { Iterator<TreeImageDisplay> i; TagAnnotationData ob; String value = ((TagAnnotationData) object).getTagValue(); if (value == null) return null; value = value.toLowerCase(); String v; TreeImageDisplay node; List children; Iterator j; if (!selected) { i = originalItems.iterator(); while (i.hasNext()) { node = i.next(); ob = (TagAnnotationData) node.getUserObject(); if (ob != null) { if (TagAnnotationData.INSIGHT_TAGSET_NS.equals( ob.getNameSpace())) { children = node.getChildrenDisplay(); j = children.iterator(); while (j.hasNext()) { node = (TreeImageDisplay) j.next(); if (!isSelected(node)) { ob = (TagAnnotationData) node.getUserObject(); v = ob.getTagValue().toLowerCase(); if (value.equals(v)) { return node; } } } } else { if (!isSelected(node)) { v = ob.getTagValue().toLowerCase(); if (value.equals(v)) { return node; } } } } } //not in the original selection. Might have been moved. i = availableItems.iterator(); while (i.hasNext()) { node = i.next(); ob = (TagAnnotationData) node.getUserObject(); if (ob != null) { if (TagAnnotationData.INSIGHT_TAGSET_NS.equals( ob.getNameSpace())) { children = node.getChildrenDisplay(); j = children.iterator(); while (j.hasNext()) { node = (TreeImageDisplay) j.next(); if (!isSelected(node)) { ob = (TagAnnotationData) node.getUserObject(); v = ob.getTagValue().toLowerCase(); if (value.equals(v)) { return node; } } } } else { if (!isSelected(node)) { v = ob.getTagValue().toLowerCase(); if (value.equals(v)) { return node; } } } } } } else { i = selectedItems.iterator(); while (i.hasNext()) { node = i.next(); ob = (TagAnnotationData) node.getUserObject(); if (ob != null) { v = ob.getTagValue().toLowerCase(); if (value.equals(v)) return node; } } } } return null; } /** * Sets the default text for the specified field. * * @param text The text to display */ private void setTextFieldDefault(String text) { filterArea.getDocument().removeDocumentListener(this); if (text == null) { filterArea.setText(""); filterArea.setForeground(originalColor); } else { filterArea.setText(text); filterArea.setForeground(Color.LIGHT_GRAY); } filterArea.getDocument().addDocumentListener(this); filterArea.setSelectionStart(0); filterArea.setSelectionEnd(filterArea.getText().length()); } /** * Initializes the specified tree * * @param tree The tree to handle. * @param user The user currently logged in. */ private void initializeTree(JTree tree, ExperimenterData user) { tree.setVisible(true); tree.setRootVisible(false); ToolTipManager.sharedInstance().registerComponent(tree); tree.setCellRenderer(new TreeCellRenderer(false)); tree.setShowsRootHandles(true); TreeImageSet root = new TreeImageSet(""); tree.setModel(new DefaultTreeModel(root)); } /** Initializes the components composing the display.*/ private void initComponents() { filterAnywhere = true; filterArea = new JTextField(); originalColor = filterArea.getForeground(); setTextFieldDefault(DEFAULT_FILTER_TEXT); StringBuilder builder = new StringBuilder(); builder.append("Filter"); if (TagAnnotationData.class.equals(type)) { builder.append(" Tags."); } else if (FileAnnotationData.class.equals(type)) { builder.append(" Attachments."); } else if (DatasetData.class.equals(type)) { builder.append(" Datasets."); } else builder.append("."); filterArea.setToolTipText(builder.toString()); filterArea.getDocument().addDocumentListener(this); filterArea.addFocusListener(new FocusListener() { @Override public void focusLost(FocusEvent evt) { String value = filterArea.getText(); if (CommonsLangUtils.isBlank(value)) { setTextFieldDefault(DEFAULT_FILTER_TEXT); } } @Override public void focusGained(FocusEvent evt) {} }); filterArea.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent evt) { String value = filterArea.getText(); if (DEFAULT_FILTER_TEXT.equals(value)) { setTextFieldDefault(null); } } }); filterArea.addKeyListener(new KeyAdapter() { /** * Adds the items to the selected list. * @see KeyListener#keyPressed(KeyEvent) */ public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER && filterArea.isFocusOwner()) { addItem(); //reset filter setTextFieldDefault(DEFAULT_FILTER_TEXT); availableItemsListbox.requestFocus(); } } }); sorter = new ViewerSorter(); availableItemsListbox = new JTree(); initializeTree(availableItemsListbox, user); availableItemsListbox.addKeyListener(new KeyAdapter() { /** * Adds the items to the selected list. * @see KeyListener#keyPressed(KeyEvent) */ public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER && availableItemsListbox.isFocusOwner()) { addItem(); } } }); availableItemsListbox.addMouseListener(new MouseAdapter() { /** * Adds the items to the selected list. * @see MouseListener#mouseReleased(MouseEvent) */ public void mouseReleased(MouseEvent e) { if (e.getClickCount() == 2) { if (availableItemsListbox.isFocusOwner()) addItem(); } } }); availableItemsListbox.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent evt) { firePropertyChange(AVAILABLE_SELECTION_CHANGE, Boolean.TRUE, Boolean.FALSE); } }); selectedItemsListbox = new JTree(); initializeTree(selectedItemsListbox, user); selectedItemsListbox.addKeyListener(new KeyAdapter() { /** * Removes the selected elements from the selected list. * @see KeyListener#keyPressed(KeyEvent) */ public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER && selectedItemsListbox.isFocusOwner()) { removeItem(); } } }); selectedItemsListbox.addMouseListener(new MouseAdapter() { /** * Removes the selected elements from the selected list. * @see MouseListener#mouseReleased(MouseEvent) */ public void mouseReleased(MouseEvent e) { if (e.getClickCount() == 2) { if (selectedItemsListbox.isFocusOwner()) removeItem(); } } }); IconManager icons = IconManager.getInstance(); addButton = new JButton(icons.getIcon(IconManager.RIGHT_ARROW)); removeButton = new JButton(icons.getIcon(IconManager.LEFT_ARROW)); addAllButton = new JButton( icons.getIcon(IconManager.DOUBLE_RIGHT_ARROW)); removeAllButton = new JButton( icons.getIcon(IconManager.DOUBLE_LEFT_ARROW)); addButton.setActionCommand(""+ADD); addButton.addActionListener(this); addAllButton.setActionCommand(""+ADD_ALL); addAllButton.addActionListener(this); removeButton.setActionCommand(""+REMOVE); removeButton.addActionListener(this); removeAllButton.setActionCommand(""+REMOVE_ALL); removeAllButton.addActionListener(this); setImmutableElements(null); } /** * Formats the tooltip of a tag. * * @param data The annotation to handle. * @param parents The tags. * @param exp The experimenter associated to the tag. * @return See */ public String formatTooltip(AnnotationData data, List<TagAnnotationData> parents, ExperimenterData exp) { if (data == null) return ""; StringBuilder buf = new StringBuilder(); buf.append("<html><body>"); String txt = ""; if (exp != null) { txt = EditorUtil.formatExperimenter(exp); } else { txt = EditorUtil.formatExperimenter(data.getOwner()); } if (CommonsLangUtils.isNotBlank(txt)) { buf.append("<b>"); buf.append("Owner: "); buf.append("</b>"); buf.append(txt); buf.append("<br>"); } if (data instanceof TagAnnotationData) { txt = ((TagAnnotationData) data).getTagDescription(); if (CommonsLangUtils.isNotBlank(txt)) { buf.append("<b>"); buf.append("Description: "); buf.append("</b>"); buf.append(txt); buf.append("<br>"); } } if (CollectionUtils.isNotEmpty(parents)) { buf.append("<b>"); buf.append("Tag Set: "); buf.append("</b>"); for (TagAnnotationData item : parents) { buf.append(item.getTagValue()); buf.append(" "); } buf.append("<br>"); } buf.append("</body></html>"); return buf.toString(); } /** * Returns the list of tag sets the tag is linked to. * * @param tag The tag to handle. * @return See above */ private List<TagAnnotationData> getParents(TagAnnotationData tag) { List<TagAnnotationData> parents = new ArrayList<TagAnnotationData>(); for (TreeImageDisplay item : originalItems) { TagAnnotationData t = (TagAnnotationData) item.getUserObject(); if (TagAnnotationData.INSIGHT_TAGSET_NS.equals(t.getNameSpace())) { Set<TagAnnotationData> tags = t.getTags(); if (CollectionUtils.isEmpty(tags)) continue; for (TagAnnotationData n : tags) { if (n.getId() == tag.getId()) { parents.add(t); } } } } return parents; } /** Creates a copy of the original selections. */ private void createOriginalSelections() { originalItems = new ArrayList<TreeImageDisplay>(); TagAnnotationData tag; Object ho; if (availableItems != null) { for (TreeImageDisplay item : availableItems) { originalItems.add(item); ho = item.getUserObject(); if (ho instanceof TagAnnotationData) { tag = (TagAnnotationData) ho; item.setToolTip(formatTooltip(tag, null, null)); List<TreeImageDisplay> l = item.getChildrenDisplay(); List<TagAnnotationData> p = Arrays.asList(tag); for (TreeImageDisplay j : l) { j.setToolTip(formatTooltip(tag, p, null)); } } else if (ho instanceof AnnotationData) { item.setToolTip(formatTooltip((AnnotationData) ho, null, null)); } } } originalSelectedItems = new ArrayList<TreeImageDisplay>(); if (selectedItems != null) { for (TreeImageDisplay item : selectedItems) { //format tooltip of annotation. originalSelectedItems.add(item); ho = item.getUserObject(); if (ho instanceof TagAnnotationData) { tag = (TagAnnotationData) item.getUserObject(); item.setToolTip(formatTooltip(tag, getParents(tag), null)); } else if (ho instanceof AnnotationData) { item.setToolTip(formatTooltip((AnnotationData) ho, null, null)); } } } } /** Adds all the items to the selection. */ private void addAllItems() { TreeImageDisplay child; List<TreeImageDisplay> toKeep = new ArrayList<TreeImageDisplay>(); for (TreeImageDisplay node: availableItems) { if (node.hasChildrenDisplay()) { //tagset with tag. toKeep.add(node); List l = node.getChildrenDisplay(); Iterator j = l.iterator(); while (j.hasNext()) { child = (TreeImageDisplay) j.next(); if (!isSelected(child)) { selectedItems.add(child); } } } else { Object ho = node.getUserObject(); if (ho instanceof TagAnnotationData) { TagAnnotationData tag = (TagAnnotationData) ho; if (!TagAnnotationData.INSIGHT_TAGSET_NS.equals(tag.getNameSpace())) { if (!isSelected(node)) { selectedItems.add(node); } } else toKeep.add(node); } else { if (!isSelected(node)) { selectedItems.add(node); } } } } availableItems.retainAll(toKeep); sortLists(); populateTreeItems(availableItemsListbox, availableItems); populateTreeItems(selectedItemsListbox, selectedItems); onSelectionChange(); } /** * Returns <code>true</code> if the node cannot remove, * <code>false</code> otherwise. * * @param data The element to handle. * @return See above. */ private boolean isImmutable(DataObject data) { Iterator i = immutable.iterator(); DataObject o; while (i.hasNext()) { o = (DataObject) i.next(); if (data.getId() == o.getId()) return true; } return false; } /** * Returns <code>true</code> if the node is the child of an available node, * <code>false</code> otherwise. * * @param node The node to handle. * @return See above. */ private boolean isChild(TreeImageDisplay node) { if (node == null) return false; Object uo = node.getUserObject(); if (!(uo instanceof DataObject)) return false; DataObject ref = (DataObject) uo; Iterator<TreeImageDisplay> i = originalItems.iterator(); TreeImageDisplay n; Object data; while (i.hasNext()) { n = i.next(); data = n.getUserObject(); if (data instanceof TagAnnotationData) { TagAnnotationData tag = (TagAnnotationData) data; if (TagAnnotationData.INSIGHT_TAGSET_NS.equals( tag.getNameSpace())) { Set<TagAnnotationData> children = tag.getTags(); if (CollectionUtils.isEmpty(children)) continue; Iterator<TagAnnotationData> j = children.iterator(); DataObject o; while (j.hasNext()) { o = j.next(); if (o.getId() == ref.getId()) return true; } } } } return false; } /** Removes an item from the selection. */ private void removeItem() { TreePath[] paths = selectedItemsListbox.getSelectionPaths(); if (paths == null || paths.length == 0) return; Object c; TreeImageDisplay node; Object ho; DataObject data; List<TreeImageDisplay> toRemove = new ArrayList<TreeImageDisplay>(); for (int i = 0; i < paths.length; i++) { c = paths[i].getLastPathComponent(); if (c instanceof TreeImageDisplay) { node = (TreeImageDisplay) c; ho = node.getUserObject(); if (ho instanceof DataObject) { data = (DataObject) ho; if (!isImmutable(data)) { if (data.getId() >= 0 && !isChild(node)) { availableItems.add(node); } toRemove.add(node); } } else { toRemove.add(node); } } } selectedItems.removeAll(toRemove); sortLists(); populateTreeItems(availableItemsListbox, availableItems); populateTreeItems(selectedItemsListbox, selectedItems); onSelectionChange(); } /** Removes all items from the selection. */ private void removeAllItems() { List<TreeImageDisplay> toKeep = new ArrayList<TreeImageDisplay>(); Object ho; DataObject data; for (TreeImageDisplay node: selectedItems) { ho = node.getUserObject(); if (ho instanceof DataObject) { data = (DataObject) ho; if (isImmutable(data)) { toKeep.add(node); } else { if (data.getId() >= 0 && !isChild(node)) { availableItems.add(node); } } } } selectedItems.retainAll(toKeep); sortLists(); populateTreeItems(availableItemsListbox, availableItems); populateTreeItems(selectedItemsListbox, selectedItems); onSelectionChange(); } /** Adds an item to the list and then sorts the list to maintain order.*/ private void addItem() { TreePath[] paths = availableItemsListbox.getSelectionPaths(); if (paths == null || paths.length == 0) return; Object c; TreeImageDisplay node, child; //List List<TreeImageDisplay> toRemove = new ArrayList<TreeImageDisplay>(); for (int i = 0; i < paths.length; i++) { c = paths[i].getLastPathComponent(); if (c instanceof TreeImageDisplay) { node = (TreeImageDisplay) c; if (node.hasChildrenDisplay()) { //tagset List l = node.getChildrenDisplay(); Iterator j = l.iterator(); while (j.hasNext()) { child = (TreeImageDisplay) j.next(); if (!isSelected(child)) { selectedItems.add(child); } } } else { if (!isSelected(node)) { toRemove.add(node); selectedItems.add(node); } } } } availableItems.removeAll(toRemove); sortLists(); populateTreeItems(availableItemsListbox, availableItems); populateTreeItems(selectedItemsListbox, selectedItems); onSelectionChange(); } /** Notifies that the selection has changed. */ private void onSelectionChange() { boolean b = false; if (originalSelectedItems.size() != selectedItems.size()) { b = true; } else { int n = 0; Iterator<TreeImageDisplay> i = selectedItems.iterator(); DataObject ref, original; Iterator<TreeImageDisplay> j; while (i.hasNext()) { ref = (DataObject) i.next().getUserObject(); j = originalSelectedItems.iterator(); while (j.hasNext()) { original = (DataObject) j.next().getUserObject(); if (original.getId() == ref.getId()) { n++; break; } } } b = (n != originalSelectedItems.size()); } firePropertyChange(SELECTION_CHANGE, Boolean.valueOf(!b), Boolean.valueOf(b)); } /** * Returns <true> if the node should be filtered, <code>false</code> * otherwise. * * @param child The node to handle. * @return See above. */ private boolean isFiltered(TreeImageDisplay child) { if (child == null) return false; String txt = filterArea.getText(); if (CommonsLangUtils.isBlank(txt) || DEFAULT_FILTER_TEXT.equals(txt)) return false; txt = txt.toLowerCase(); Object ho = child.getUserObject(); String value; if (ho instanceof TagAnnotationData) { TagAnnotationData tag = (TagAnnotationData) ho; value = tag.getTagValue(); value = value.toLowerCase(); if (filterAnywhere) { return !value.contains(txt); } return !value.startsWith(txt); } return true; } /** * Updates the specified tree. * * @param tree The tree to update. * @param nodes The collection of nodes to handle. */ private void populateTreeItems(JTree tree, List<TreeImageDisplay> nodes) { DefaultTreeModel dtm = (DefaultTreeModel) tree.getModel(); TreeImageDisplay parent = (TreeImageDisplay) dtm.getRoot(); parent.removeAllChildrenDisplay(); parent.removeAllChildren(); Iterator<TreeImageDisplay> i = nodes.iterator(); TreeImageDisplay node, child; Iterator<TreeImageDisplay> j; Set<TreeImageDisplay> toExpand = new HashSet<TreeImageDisplay>(); while (i.hasNext()) { node = i.next(); node.setDisplayItems(false); Object ho = node.getUserObject(); if (ho instanceof TagAnnotationData) { TagAnnotationData tag = (TagAnnotationData) ho; if (!TagAnnotationData.INSIGHT_TAGSET_NS.equals( tag.getNameSpace()) || node.hasChildrenDisplay()) { dtm.insertNodeInto(node, parent, parent.getChildCount()); } } else { dtm.insertNodeInto(node, parent, parent.getChildCount()); } if (node.hasChildrenDisplay()) { node.removeAllChildren(); tree.expandPath(new TreePath(node.getPath())); Collection<TreeImageDisplay> l = node.getChildrenDisplay(); l = sorter.sort(l); j = l.iterator(); while (j.hasNext()) { child = j.next(); child.setDisplayItems(false); if (!isSelected(child) && !isFiltered(child)) { dtm.insertNodeInto(child, node, node.getChildCount()); toExpand.add(node); tree.expandPath(new TreePath(node.getPath())); } } } } dtm.reload(); i = toExpand.iterator(); while (i.hasNext()) { tree.expandPath(new TreePath(i.next().getPath())); } } /** Sorts the lists. */ private void sortLists() { if (availableItems != null) availableItems = sorter.sort(availableItems); if (selectedItems != null) selectedItems = sorter.sort(selectedItems); } /** Builds and lays out the UI. */ private void buildGUI() { setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); double[][] size = {{TableLayout.FILL, 40, TableLayout.FILL}, {TableLayout.FILL}}; setLayout(new TableLayout(size)); add(createAvailableItemsPane(), "0, 0"); add(createSelectionPane(), "1, 0, CENTER, CENTER"); add(createSelectedItemsPane(), "2, 0"); } /** * Builds and lays out the available tags. * * @return See above. */ private JPanel createAvailableItemsPane() { JPanel p = new JPanel(); p.setLayout(new BorderLayout()); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.add(UIUtilities.setTextFont(createText("Available"))); panel.add(filterArea); panel.add(Box.createVerticalStrut(2)); p.add(panel, BorderLayout.NORTH); p.add(new JScrollPane(availableItemsListbox), BorderLayout.CENTER); populateTreeItems(availableItemsListbox, availableItems); return p; } /** * Builds and lays out the buttons used to select tags. * * @return See above. */ private JPanel createSelectionPane() { JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS)); buttonPanel.add(Box.createVerticalStrut(30)); buttonPanel.add(addButton); buttonPanel.add(Box.createVerticalStrut(10)); buttonPanel.add(removeButton); buttonPanel.add(Box.createVerticalStrut(10)); buttonPanel.add(addAllButton); buttonPanel.add(Box.createVerticalStrut(10)); buttonPanel.add(removeAllButton); buttonPanel.add(Box.createVerticalStrut(10)); return buttonPanel; } /** * Builds and lays out the list of selected tags. * * @return See above. */ private JPanel createSelectedItemsPane() { JPanel p = new JPanel(); p.setLayout(new BorderLayout()); p.add(UIUtilities.setTextFont(createText("Selected")), BorderLayout.NORTH); p.add(new JScrollPane(selectedItemsListbox), BorderLayout.CENTER); populateTreeItems(selectedItemsListbox, selectedItems); return p; } /** * Creates the text displayed above the selections. * * @param txt The text to display. * @return See above. */ private String createText(String txt) { StringBuilder b = new StringBuilder(); b.append(txt); if (TagAnnotationData.class.equals(type)) { b.append(" tags"); } else if (FileAnnotationData.class.equals(type)) { b.append(" attachments"); } b.append(":"); return b.toString(); } /** * Creates a new instance. * * @param view The parent. * @param available The collection of available items. * @param type The type of object to handle. * @param user The current user. */ public SelectionWizardUI(JDialog view, Collection<Object> available, Class<?> type, ExperimenterData user) { this(view, available, null, type, user); } /** * Creates a new instance. * * @param available The collection of available items. * @param selected The collection of selected items. * @param type The type of object to handle. * @param user The current user. */ public SelectionWizardUI(JDialog view, Collection<Object> available, Collection<Object> selected, Class<?> type, ExperimenterData user) { if (selected == null) selected = new ArrayList<Object>(); if (available == null) available = new ArrayList<Object>(); this.view = view; this.user = user; this.availableItems = new ArrayList<TreeImageDisplay>( TreeViewerTranslator.transformHierarchy(available)); this.selectedItems = new ArrayList<TreeImageDisplay>( TreeViewerTranslator.transformHierarchy(selected)); this.type = type; createOriginalSelections(); initComponents(); sortLists(); buildGUI(); } /** Resets the selection. */ void reset() { availableItems.clear(); selectedItems.clear(); for (TreeImageDisplay item : originalItems) availableItems.add(item); for (TreeImageDisplay item : originalSelectedItems) selectedItems.add(item); sortLists(); populateTreeItems(availableItemsListbox, availableItems); populateTreeItems(selectedItemsListbox, selectedItems); onSelectionChange(); } /** * Adds the passed objects. Returns <code>true</code> to clear the text * and resets the default, <code>false</code> otherwise. * * @param toAdd The objects to add. * @return See above. */ boolean addObjects(List<DataObject> toAdd) { if (CollectionUtils.isEmpty(toAdd)) return true; Iterator<DataObject> i = toAdd.iterator(); DataObject data; TreeImageDisplay node = null; String desc = ""; String s = "", value; while (i.hasNext()) { data = i.next(); node = doesObjectExist(data, true); if (node != null) { if (data instanceof TagAnnotationData) { desc = ((TagAnnotationData) data).getTagDescription(); } break; } } //now check if the node is already selected. if (node != null) { //check the description for the txt data = (DataObject) node.getUserObject(); if (data instanceof TagAnnotationData) { value = ((TagAnnotationData) data).getTagDescription(); if (!desc.equals(value)) { s = " a different"; } } NotificationDialog msg = new NotificationDialog(view, "Add new tag", String.format( "A tag with the same name and%s description " + "already exists\nand is selected.", s), null); UIUtilities.centerAndShow(msg); return false; } if (node == null) { //warning i = toAdd.iterator(); while (i.hasNext()) { data = i.next(); node = doesObjectExist(data, false); if (node != null) { if (data instanceof TagAnnotationData) { desc = ((TagAnnotationData) data).getTagDescription(); } break; } } if (node != null) { //check the description for the txt data = (DataObject) node.getUserObject(); if (data instanceof TagAnnotationData) { value = ((TagAnnotationData) data).getTagDescription(); if (!desc.equals(value)) { s = " a different"; } } MessageBox msg = new MessageBox(view, "Add new tag", String.format("A tag with the same name and" + "%s description already exists.\n" + "Would you like to select the existing tag?", s)); int option = msg.centerMsgBox(); if (option == MessageBox.YES_OPTION) { availableItemsListbox.setSelectionPath( new TreePath(node.getPath())); addItem(); availableItemsListbox.requestFocus(); return true; } return false; } } //We create a new tag. i = toAdd.iterator(); DataObject ho; while (i.hasNext()) { ho = i.next(); node = TreeViewerTranslator.transformDataObject(ho); if (ho instanceof TagAnnotationData) { TagAnnotationData tag = (TagAnnotationData) ho; Set<DataObject> set = tag.getDataObjects(); List<TagAnnotationData> p = new ArrayList<TagAnnotationData>(); if (CollectionUtils.isNotEmpty(set)) { Iterator<DataObject> j = set.iterator(); DataObject d; while (j.hasNext()) { d = j.next(); if (d instanceof TagAnnotationData) { p.add((TagAnnotationData) d); } } } String txt = formatTooltip(tag, p, user); node.setToolTip(txt); } selectedItems.add(node); } sortLists(); populateTreeItems(selectedItemsListbox, selectedItems); onSelectionChange(); return true; } /** * Sets the collection of nodes that cannot be removed. * * @param immutable The collection to set. */ void setImmutableElements(Collection immutable) { if (immutable == null) immutable = new ArrayList(); this.immutable = immutable; } /** * Returns the collection of immutable objects. * * @return See above. */ Collection getImmutableElements() { return immutable; } /** * Returns <code>true</code> if the node has been added, * <code>false</code> otherwise. * * @param value The value to handle. * @return See above. */ boolean isAddedNode(Object value) { return !originalSelectedItems.contains(value); } /** * Returns the name of the group corresponding to identifier. * * @param ctx The context to handle. * @return See above */ String getGroupName(long groupId) { if (groups == null) return null; Iterator<GroupData> i = groups.iterator(); GroupData g; while (i.hasNext()) { g = i.next(); if (g.getId() == groupId) return g.getName(); } return null; } /** * Sets the groups. * * @param groups The groups to set. */ void setGroups(Collection<GroupData> groups) { this.groups = groups; } /** * Returns <code>true</code> to filter by term anywhere in the word, * <code>false</code> otherwise. * @return */ boolean isFilterAnywhere() { return filterAnywhere; } /** * Sets to <code>true</code> to filter by term anywhere in the word, * <code>false</code> otherwise. * * @param filterAnywhere The value to set. */ void setFilterAnywhere(boolean filterAnywhere) { this.filterAnywhere = filterAnywhere; String text = filterArea.getText(); if (!DEFAULT_FILTER_TEXT.equals(text)) { filter(false); } } /** * Sets the owner's filtering index. * * @param index The value to set. */ void setOwnerIndex(int index) { ownerFilterIndex = index; String text = filterArea.getText(); boolean reset = false; if (DEFAULT_FILTER_TEXT.equals(text)) { setTextFieldDefault(null); reset = true; } filter(false); if (reset) { setTextFieldDefault(DEFAULT_FILTER_TEXT); } } /** * Returns the collection of nodes selected in the "Available" pane. * * @return See above. */ Set<DataObject> getAvailableSelectedNodes() { TreePath[] paths = availableItemsListbox.getSelectionPaths(); if (paths == null || paths.length == 0) return null; Object c; Set<DataObject> nodes = new HashSet<DataObject>(); TreeImageDisplay node; for (int i = 0; i < paths.length; i++) { c = paths[i].getLastPathComponent(); if (c instanceof TreeImageDisplay) { node = (TreeImageDisplay) c; if (node.hasChildrenDisplay()) { //tagset nodes.add((DataObject) node.getUserObject()); } else { if (isChild(node)) { nodes.add((DataObject) node.getParentDisplay().getUserObject()); } } } } return nodes; } /** * Returns the selected items, excluding the immutable node. * * @return See above. */ public Collection<Object> getSelection() { Iterator<TreeImageDisplay> i = selectedItems.iterator(); List<Object> results = new ArrayList<Object>(); TreeImageDisplay object; Object uo; while (i.hasNext()) { object = i.next(); uo = object.getUserObject(); if (isAddedNode(object)) results.add(uo); else { //was there but is immutable if (uo instanceof DataObject) { if (!isImmutable((DataObject) uo)) results.add(uo); } else results.add(uo); } } return results; } /** * Reacts to event fired by the various controls. * @see ActionListener#actionPerformed(ActionEvent) */ public void actionPerformed(ActionEvent evt) { int id = Integer.parseInt(evt.getActionCommand()); switch (id) { case ADD: addItem(); break; case ADD_ALL: addAllItems(); break; case REMOVE: removeItem(); break; case REMOVE_ALL: removeAllItems(); } } @Override public void removeUpdate(DocumentEvent e) { filter(false); } @Override public void insertUpdate(DocumentEvent e) { filter(true); } @Override public void changedUpdate(DocumentEvent e) {} }