/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2012 University of Dundee & Open Microscopy Environment.
* 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.util;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.ToolTipManager;
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.jdesktop.swingx.JXBusyLabel;
import org.openmicroscopy.shoola.agents.treeviewer.IconManager;
import org.openmicroscopy.shoola.agents.util.ViewerSorter;
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 omero.gateway.SecurityContext;
import org.openmicroscopy.shoola.util.ui.TitlePanel;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.filechooser.CreateFolderDialog;
import omero.gateway.model.DataObject;
import omero.gateway.model.DatasetData;
import omero.gateway.model.ImageData;
import omero.gateway.model.PlateData;
import omero.gateway.model.ProjectData;
import omero.gateway.model.ScreenData;
/**
* Selects the targets of the move group action.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @since Beta4.4
*/
public class MoveGroupSelectionDialog
extends JDialog
implements ActionListener, PropertyChangeListener, TreeSelectionListener
{
/** Bound property indicating to transfer the data.*/
public static final String TRANSFER_PROPERTY = "transfer";
/** Action id to close and dispose.*/
public static final int CANCEL = 1;
/** Action id to move the data.*/
private static final int MOVE = 2;
/**
* Action id to create a new container e.g. Dataset. The supported types
* are Project, Dataset or Screen, depending on the objects to move.
*/
private static final int CREATE = 3;
/** Text displayed in the header.*/
private static final String TEXT = "Select where to move the data.";
/** The default size of the busy image.*/
private static final Dimension SIZE = new Dimension(32, 32);
/** The object to handle.*/
private ChgrpObject object;
/** The button to close and dispose.*/
private JButton cancelButton;
/** The button to move the data.*/
private JButton moveButton;
/** The button to create a new container.*/
private JButton createButton;
/** The component displayed in center of dialog.*/
private JComponent body;
/** The id of the user.*/
private long userID;
/** Sort the data.*/
private ViewerSorter sorter;
/** Indicate the status of the dialog.*/
private int status;
/** Handle the nodes to display.*/
private JTree treeDisplay;
/** The type of container to create.*/
private Class<?> containerType;
/** The data object to create.*/
private DataObject toCreate;
/** Flag indicating that the message indicating that no node displayed.*/
private boolean noDisplay;
/**
* Creates a new container corresponding to {@link #containerType}.
*
* @param name The name of the container.
*/
private void create(String name)
{
if (containerType == null) return;
if (ProjectData.class.equals(containerType)) {
toCreate = new ProjectData();
((ProjectData) toCreate).setName(name);
} else if (ScreenData.class.equals(containerType)) {
toCreate = new ScreenData();
((ScreenData) toCreate).setName(name);
} else if (DatasetData.class.equals(containerType)) {
toCreate = new DatasetData();
((DatasetData) toCreate).setName(name);
}
DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel();
TreeImageDisplay root = (TreeImageDisplay) dtm.getRoot();
TreeImageSet node = new TreeImageSet(toCreate);
node.setDisplayItems(false);
dtm.insertNodeInto(node, root, root.getChildCount());
node = (TreeImageSet) root.getChildAt(root.getChildCount()-1);
treeDisplay.setSelectionPath(new TreePath(node.getPath()));
if (noDisplay) {
noDisplay = false;
Container c = getContentPane();
c.remove(body);
c.remove(1);
c.add(new JScrollPane(treeDisplay), BorderLayout.CENTER);
c.add(buildToolBar(), BorderLayout.SOUTH);
validate();
repaint();
}
}
/** Brings up a dialog to create a new container.*/
private void showNewFolder()
{
if (containerType == null) return;
String title = "";
String defaultName = "";
if (ProjectData.class.equals(containerType)) {
title = "New Project";
defaultName = "untitled project";
} else if (ScreenData.class.equals(containerType)) {
title = "New Screen";
defaultName = "untitled screen";
} else if (DatasetData.class.equals(containerType)) {
title = "New Dataset";
defaultName = "untitled dataset";
}
CreateFolderDialog d = new CreateFolderDialog(this, title);
d.setDefaultName(defaultName);
d.addPropertyChangeListener(CreateFolderDialog.CREATE_FOLDER_PROPERTY,
this);
d.pack();
UIUtilities.centerAndShow(this, d);
}
/**
* 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<TreeImageDisplay> nodes, DefaultTreeModel tm)
{
Iterator<TreeImageDisplay> i = nodes.iterator();
TreeImageDisplay display;
List<TreeImageDisplay> children;
while (i.hasNext()) {
display = (TreeImageDisplay) i.next();
display.setDisplayItems(false);
tm.insertNodeInto(display, parent, parent.getChildCount());
if (display instanceof TreeImageSet) {
children = display.getChildrenDisplay();
if (children.size() > 0) {
buildTreeNode(display,
prepareSortedList(sorter.sort(children)), tm);
}
}
}
}
/** Closes and disposes.*/
private void cancel()
{
status = CANCEL;
setVisible(false);
dispose();
}
/** Moves the data.*/
private void move()
{
DataObject target = null;
if (toCreate != null) {
target = toCreate;
} else {
TreePath path = treeDisplay.getSelectionPath();
if (path != null) {
Object object = path.getLastPathComponent();
if (object != null && object instanceof TreeImageDisplay) {
target = (DataObject)
((TreeImageDisplay) object).getUserObject();
}
}
}
ChgrpObject newObject = new ChgrpObject(object.getGroupData(),
target, object.getTransferable());
firePropertyChange(TRANSFER_PROPERTY, null, newObject);
cancel();
}
/** Initializes the components.
*
* @param same Pass <code>true</code> if the user moving the data and the
* owner of the data are the same person, <code>false</code>
*/
private void initComponents(boolean same)
{
containerType = null;
sorter = new ViewerSorter();
createButton = new JButton("New...");
createButton.addActionListener(this);
createButton.setActionCommand(""+CREATE);
cancelButton = new JButton("Cancel");
cancelButton.addActionListener(this);
cancelButton.setActionCommand(""+CANCEL);
moveButton = new JButton("Move");
moveButton.setEnabled(true);
moveButton.addActionListener(this);
moveButton.setActionCommand(""+MOVE);
Entry<SecurityContext, List<DataObject>> entry;
Iterator<Entry<SecurityContext, List<DataObject>>>
i = object.getTransferable().entrySet().iterator();
List<DataObject> list;
DataObject data;
while (i.hasNext()) {
entry = i.next();
list = entry.getValue();
if (list != null && list.size() > 0) {
data = list.get(0);
if (data instanceof PlateData) {
containerType = ScreenData.class;
} else if (data instanceof DatasetData) {
containerType = ProjectData.class;
} else if (data instanceof ImageData) {
containerType = DatasetData.class;
}
}
}
createButton.setEnabled(false);
if (same) {
createButton.setEnabled(containerType != null);
createButton.setVisible(containerType != null);
}
}
/**
* Builds and lays out the buttons.
*
* @return See above.
*/
private JPanel buildToolBar()
{
JPanel bar = new JPanel();
bar.add(moveButton);
bar.add(Box.createRigidArea(UIUtilities.H_SPACER_SIZE));
bar.add(cancelButton);
bar.add(Box.createRigidArea(UIUtilities.H_SPACER_SIZE));
JPanel barLeft = new JPanel();
barLeft.add(createButton);
JPanel row = new JPanel();
row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
row.add(UIUtilities.buildComponentPanel(barLeft));
row.add(UIUtilities.buildComponentPanelRight(bar));
return row;
}
/** Builds and lays out the UI.*/
private void buildGUI()
{
IconManager icons = IconManager.getInstance();
TitlePanel tp = new TitlePanel(getTitle(), TEXT,
icons.getIcon(IconManager.MOVE_48));
Container c = getContentPane();
c.add(tp, BorderLayout.NORTH);
JXBusyLabel label = new JXBusyLabel(SIZE);
label.setBusy(true);
body = buildContent(label);
c.add(body, BorderLayout.CENTER);
c.add(buildToolBar(), BorderLayout.SOUTH);
setSize(400, 500);
}
/**
* Builds the main component of this dialog.
*
* @param group The selected group if any.
* @param comp The component to add.
* @return See above.
*/
private JComponent buildContent(JComponent comp)
{
double[][] tl = {{TableLayout.FILL}, //columns
{TableLayout.FILL}}; //rows
JPanel content = new JPanel();
content.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
content.setLayout(new TableLayout(tl));
content.setBackground(UIUtilities.BACKGROUND);
content.add(comp, "0, 0, CENTER, CENTER");
return content;
}
/**
* 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<TreeImageDisplay>
prepareSortedList(List<TreeImageDisplay> 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<TreeImageDisplay> j = sorted.iterator();
TreeImageDisplay object;
Object uo;
while (j.hasNext()) {
object = (TreeImageDisplay) j.next();
uo = object.getUserObject();
if (ProjectData.class.equals(containerType)) {
if (uo instanceof ProjectData) {
top.add(object);
object.removeAllChildrenDisplay();
}
} else if (ScreenData.class.equals(containerType)) {
if (uo instanceof ScreenData) {
top.add(object);
object.removeAllChildrenDisplay();
}
} else if (DatasetData.class.equals(containerType)) {
if (uo instanceof ProjectData) top.add(object);
else if (uo instanceof DatasetData) bottom.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;
}
/** Builds the components displayed when no node to display.*/
private void buildNoContentPane()
{
noDisplay = true;
Container c = getContentPane();
StringBuffer s = new StringBuffer();
StringBuffer s1 = new StringBuffer();
if (ProjectData.class.equals(containerType)) {
s.append("There is no Project to move the Dataset(s) into.");
s1.append("Please create a new Project if you wish.");
} else if (DatasetData.class.equals(containerType)) {
s.append("There is no Dataset to move the Image(s) into.");
s1.append("Please create a new Dataset if you wish.");
} else if (ScreenData.class.equals(containerType)) {
s.append("There is no Screen to move the Plate into.");
s1.append("Please create a new Screen if you wish.");
}
JPanel p = new JPanel();
p.setBackground(UIUtilities.BACKGROUND);
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
p.add(new JLabel(s.toString()));
p.add(new JLabel(s1.toString()));
body = buildContent(p);
c.add(body, BorderLayout.CENTER);
c.add(buildToolBar(), BorderLayout.SOUTH);
validate();
repaint();
}
/**
* Creates a new instance.
*
* @param owner The owner of the dialog.
* @param userID The identifier of the user.
* @param object The object to move.
* @param same Pass <code>true</code> if the user moving the data and the
* owner of the data are the same person, <code>false</code>
*/
public MoveGroupSelectionDialog(JFrame owner, long userID,
ChgrpObject object, boolean same)
{
super(owner);
if (object == null)
throw new IllegalArgumentException("No object to move.");
StringBuffer buf = new StringBuffer();
buf.append("Move to ");
buf.append(object.getGroupData().getName());
setTitle(buf.toString());
this.object = object;
this.userID = userID;
initComponents(same);
buildGUI();
}
/**
* Returns the status of the dialog.
*
* @return See above.
*/
public int getStatus() { return status; }
/**
* Sets the values where to import the data.
*
* @param targets The values to display.
*/
public void setTargets(Collection<DataObject> targets)
{
Container c = getContentPane();
c.remove(body);
c.remove(1);
treeDisplay = new JTree();
treeDisplay.setVisible(true);
treeDisplay.setRootVisible(false);
ToolTipManager.sharedInstance().registerComponent(treeDisplay);
treeDisplay.setCellRenderer(new TreeCellRenderer(userID));
treeDisplay.setShowsRootHandles(true);
TreeImageSet root = new TreeImageSet("");
treeDisplay.setModel(new DefaultTreeModel(root));
treeDisplay.addTreeSelectionListener(this);
if (CollectionUtils.isEmpty(targets)) {
buildNoContentPane();
return;
}
Set<TreeImageDisplay>
nodes = TreeViewerTranslator.transformHierarchy(
new ArrayList<Object>(targets));
List<TreeImageDisplay> transformedNodes =
prepareSortedList(sorter.sort(nodes));
if (transformedNodes.size() == 0) {
buildNoContentPane();
return;
}
DefaultTreeModel dtm = (DefaultTreeModel) treeDisplay.getModel();
buildTreeNode(root, transformedNodes, dtm);
dtm.reload();
c.add(new JScrollPane(treeDisplay), BorderLayout.CENTER);
c.add(buildToolBar(), BorderLayout.SOUTH);
validate();
repaint();
}
/**
* Closes or moves the data.
* @see ActionListener#actionPerformed(ActionEvent)
*/
public void actionPerformed(ActionEvent e)
{
int index = Integer.parseInt(e.getActionCommand());
switch (index) {
case CANCEL:
cancel();
break;
case MOVE:
move();
break;
case CREATE:
showNewFolder();
}
}
/**
* Listens to property to create a new container.
*/
public void propertyChange(PropertyChangeEvent evt)
{
String name = evt.getPropertyName();
if (CreateFolderDialog.CREATE_FOLDER_PROPERTY.equals(name)) {
String folderName = (String) evt.getNewValue();
if (folderName != null && folderName.trim().length() > 0)
create(folderName);
}
}
/**
* Listens to node selection in the tree.
* @see TreeSelectionListener#valueChanged(TreeSelectionEvent)
*/
public void valueChanged(TreeSelectionEvent e)
{
TreePath[] paths = treeDisplay.getSelectionPaths();
if (paths == null || paths.length == 0) {
moveButton.setEnabled(false);
return;
}
Object p = paths[0].getLastPathComponent();
if (p instanceof TreeImageDisplay) {
Object ho = ((TreeImageDisplay) p).getUserObject();
moveButton.setEnabled(ho.getClass().equals(containerType));
}
}
}