/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2014 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.dataBrowser.view;
import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.tree.TreePath;
import org.jdesktop.swingx.JXTreeTable;
import org.openmicroscopy.shoola.agents.dataBrowser.Colors;
import org.openmicroscopy.shoola.agents.dataBrowser.browser.ImageDisplay;
import org.openmicroscopy.shoola.agents.dataBrowser.browser.ImageNode;
import org.openmicroscopy.shoola.agents.dataBrowser.browser.RollOverNode;
import org.openmicroscopy.shoola.agents.dataBrowser.util.ImageTableIconRenderer;
import org.openmicroscopy.shoola.agents.dataBrowser.util.ImageTableRenderer;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.treetable.OMETreeTable;
import org.openmicroscopy.shoola.util.ui.treetable.model.OMETreeNode;
import org.openmicroscopy.shoola.util.ui.treetable.model.OMETreeTableModel;
import org.openmicroscopy.shoola.util.ui.treetable.renderers.NumberCellRenderer;
import omero.gateway.model.DataObject;
import omero.gateway.model.ImageData;
/**
* Tree table displaying the hierarchy.
*
* @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 OME3.0
*/
class ImageTable
extends OMETreeTable
{
/** Identified the column displaying the file's path. */
static final int NAME_COL = 0;
/** Identified the column displaying the added date. */
//static final int DATE_COL = 1;
/** Identified the column displaying the added date. */
static final int THUMBNAIL_COL = 1;
/** The text of the {@link #NAME_COL}. */
static final String NAME = "Name";
/** The text of the {@link #SIZE_COL}. */
static final String SIZE = "Size";
/** The text of the {@link #THUMBNAIL_COL}. */
static final String VIEW = "Image";
/** The text of the {@link #LOGIN_NAME_COL}. */
static final String LOGIN_NAME = "User Name";
/** The text of the {@link #DATE_COL}. */
static final String DATE = "Acquired";
/** The text of the {@link #ANNOTATED_COL}. */
static final String ANNOTATED = "Annotated";
/** The text of the {@link #INSTITUTION_COL}. */
static final String INSTITUTION = "Institution";
/** The columns of the table. */
static Vector<String> COLUMNS;
/** Map indicating how to render each column. */
private static Map<Integer, Class> RENDERERS;
/** The columns of the table. */
static Vector<String> COLUMNS_GROUPS;
/** Map indicating how to render each column. */
private static Map<Integer, Class> RENDERERS_GROUPS;
static {
COLUMNS = new Vector<String>(2);
COLUMNS.add(NAME);
//COLUMNS.add(DATE);
COLUMNS.add(VIEW);
RENDERERS = new HashMap<Integer, Class>();
RENDERERS.put(NAME_COL, ImageTableNode.class);
//RENDERERS.put(DATE_COL, String.class);
RENDERERS.put(THUMBNAIL_COL, ImageTableNode.class);
COLUMNS_GROUPS = new Vector<String>(3);
COLUMNS_GROUPS.add(LOGIN_NAME);
COLUMNS_GROUPS.add(NAME);
COLUMNS_GROUPS.add(INSTITUTION);
RENDERERS_GROUPS = new HashMap<Integer, Class>();
}
/** The root node of the table. */
private ImageTableNode tableRoot;
/** Listen to the node selection. */
private TreeSelectionListener selectionListener;
/** The component hosting the table. */
private ImageTableView view;
/** Collection used when visiting the tree structure. */
private List<ImageTableNode> nodes;
/** Reference to the model. */
private DataBrowserModel model;
/** The default height of row.*/
private int rowHeight;
/** Flag indicating if the mouse has entered the table.*/
private boolean entered;
/**
* Displays the node if roll over flag is <code>one</code> when the mouse
* enters the table or is over the table.
*
* @param p The location of the mouse.
*/
private void handleRollOver(Point p)
{
int column = columnAtPoint(p);
if (column != THUMBNAIL_COL) {
view.rollOverNode(null);
return;
}
OMETreeNode n = getNodeAtRow(rowAtPoint(p));
if (n instanceof ImageTableNode) {
ImageTableNode node = (ImageTableNode) n;
Object ho = node.getHierarchyObject();
if (ho instanceof ImageData) {
ImageNode image = (ImageNode) node.getUserObject();
SwingUtilities.convertPointToScreen(p, this);
view.rollOverNode(new RollOverNode(image, p));
}
}
}
/**
* Builds the node.
*
* @param parent The parent node.
* @param comp The nodes to add to the parent.
*/
private void buildTreeNode(ImageTableNode parent, List comp)
{
if (parent == null) return;
if (comp == null || comp.size() == 0) return;
ImageDisplay display;
ImageTableNode n;
Object c;
Iterator i = comp.iterator();
while (i.hasNext()) {
c = i.next();
if (c instanceof ImageDisplay) {
display = (ImageDisplay) c;
n = new ImageTableNode(display);
parent.insert(n, parent.getChildCount());
buildTreeNode(n, view.getSorter().sort(
display.getInternalDesktop().getComponents()));
}
}
}
/** Reacts to nodes selection in the tree. */
private void onNodeSelection()
{
TreePath[] paths = getTreeSelectionModel().getSelectionPaths();
if (paths == null) return;
int n = paths.length;
if (n == 0) return;
List<ImageDisplay> nodes = new ArrayList<ImageDisplay>();
Object node;
for (int i = 0; i < n; i++) {
node = paths[i].getLastPathComponent();
if (node instanceof ImageTableNode)
nodes.add((ImageDisplay)
((ImageTableNode) node).getUserObject());
}
view.selectNodes(nodes);
}
/** Formats the table.*/
private void formatTable()
{
OMETreeTableModel om;
if (model.getType() == DataBrowserModel.GROUP)
om = new OMETreeTableModel(tableRoot, COLUMNS_GROUPS,
RENDERERS_GROUPS);
else
om = new OMETreeTableModel(tableRoot, COLUMNS, RENDERERS);
double f = view.getMagnification();
if (f <= ImageTableNode.MIN_FACTOR)
setRowHeight(ImageTableNode.MIN_HEIGHT);
else if (f > ImageTableNode.MEDIUM_FACTOR)
setRowHeight(ImageTableNode.MAX_HEIGHT);
else
setRowHeight(ImageTableNode.MEDIUM_HEIGHT);
setTableModel(om);
TableColumnModel tcm = getColumnModel();
TableColumn tc = tcm.getColumn(THUMBNAIL_COL);
tc.setCellRenderer(new ImageTableIconRenderer());
setDefaultRenderer(String.class, new NumberCellRenderer());
}
/** Initializes the component. */
private void initialize()
{
setBackground(UIUtilities.BACKGROUND_COLOR);
nodes = new ArrayList<ImageTableNode>();
formatTable();
setTreeCellRenderer(new ImageTableRenderer());
setAutoResizeMode(JXTreeTable.AUTO_RESIZE_ALL_COLUMNS);
setDefaultRenderer(String.class, new NumberCellRenderer());
//setDefaultRenderers();
setRootVisible(false);
setColumnSelectionAllowed(true);
setRowSelectionAllowed(true);
setHorizontalScrollEnabled(true);
setColumnControlVisible(true);
selectionListener = new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e)
{
onNodeSelection();
}
};
addTreeSelectionListener(selectionListener);
addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e) {
handleRollOver(e.getPoint());
}
public void mouseDragged(MouseEvent e) {}
});
}
/**
* Visits the tree.
*
* @param node The node to visit.
* @param objects The collection of <code>DataObject</code> to search for.
*/
private void visitAllNodes(ImageTableNode node, List<DataObject> objects)
{
// node is visited exactly once
//process(node);
if (objects == null) return;
Object ho = node.getHierarchyObject();
if (ho instanceof DataObject) {
Iterator<DataObject> i = objects.iterator();
DataObject object = (DataObject) ho;
long objectId = object.getId();
DataObject data;
while (i.hasNext()) {
data = i.next();
if (data.getId() == objectId) {
nodes.add(node);
break;
}
}
}
if (node.getChildCount() >= 0) {
for (Enumeration e = node.children(); e.hasMoreElements();) {
visitAllNodes((ImageTableNode) e.nextElement(), objects);
}
}
}
/**
* Visits the table-tree and recurrently checks each element for the
* fileset id. Applies different highlighting if needed. In the future,
* this might become a stand-alone visitor.
*
* @param node The starting tree root node.
* @param selectedNodes The nodes selected in the UI by the user.
*/
private void visitAllNodesToHighlight(ImageTableNode node,
List<ImageDisplay> selectedNodes) {
if (selectedNodes == null) {
return;
}
Color borderColor = Colors.getInstance().getColor(
Colors.TITLE_BAR_HIGHLIGHT_SECONDARY);
Object ho = node.getHierarchyObject();
if (ho instanceof DataObject) {
DataObject nodeDataObject = (DataObject) ho;
long nodeId = nodeDataObject.getId();
ImageData selected, sibling;
Object refNode;
for (ImageDisplay display : selectedNodes) {
refNode = display.getHierarchyObject();
node.setSibingColor(null);
if (refNode instanceof ImageData) {
selected = (ImageData) refNode;
if (nodeDataObject instanceof ImageData) {
sibling = (ImageData) nodeDataObject;
if (selected.getId() != nodeId &&
selected.getFilesetId() == sibling.getFilesetId()) {
node.setSibingColor(borderColor);
break;
}
}
}
}
}
if (node.getChildCount() >= 0) {
for (Enumeration e = node.children(); e.hasMoreElements();) {
visitAllNodesToHighlight((ImageTableNode) e.nextElement(),
selectedNodes);
}
}
}
/**
* Visits the tree.
*
* @param node The node to visit.
* @param type The type of node to handle.
* @param ids The collection of <code>DataObject</code>'s ids.
*/
private void visitAllNodes(ImageTableNode node, Class type,
Collection<Long> ids)
{
if (node == null | ids == null) return;
Object ho = node.getHierarchyObject();
if (ho == null) return;
if (ho.getClass().equals(type) && ho instanceof DataObject) {
Iterator<Long> i = ids.iterator();
DataObject object = (DataObject) ho;
long objectId = object.getId();
Long id;
while (i.hasNext()) {
id = i.next();
if (id == objectId) {
nodes.add(node);
break;
}
}
}
if (node.getChildCount() >= 0) {
for (Enumeration e = node.children(); e.hasMoreElements();) {
visitAllNodes((ImageTableNode) e.nextElement(), type, ids);
}
}
}
/**
* Creates a new instance.
*
* @param root The root of the tree.
* @param view The component hosting that table.
* @param model Reference to the model.
*/
ImageTable(ImageDisplay root, ImageTableView view, DataBrowserModel model)
{
super();
if (view == null)
throw new IllegalArgumentException("No view");
if (model == null)
throw new IllegalArgumentException("No Model");
this.view = view;
this.model = model;
tableRoot = new ImageTableNode(root);
Component[] comp = root.getInternalDesktop().getComponents();
buildTreeNode(tableRoot, view.getSorter().sort(comp));
initialize();
}
/** Refreshes the table. */
void refreshTable()
{
ImageDisplay root = ((ImageDisplay) tableRoot.getUserObject());
tableRoot = new ImageTableNode(root);
Component[] comp = root.getInternalDesktop().getComponents();
buildTreeNode(tableRoot, view.getSorter().sort(comp));
formatTable();
invalidate();
repaint();
}
/**
* Sets the selected nodes, the nodes have been selected via other views.
*
* @param objects The selected objects.
*/
void setSelectedNodes(List<DataObject> objects)
{
removeTreeSelectionListener(selectionListener);
visitAllNodes(tableRoot, objects);
Iterator<ImageTableNode> i = nodes.iterator();
ImageTableNode node;
int row = 0;
selectionModel.clearSelection();
while (i.hasNext()) {
node = i.next();
row = getRowForPath(node.getPath());
selectionModel.addSelectionInterval(row, row);
}
nodes.clear();
repaint();
addTreeSelectionListener(selectionListener);
}
/**
* Sets the highlighting for nodes with the same fileset id as the supplied
* nodes.
*
* @param nodes The list of user-selected nodes.
*/
void setHighlightedNodes(List<ImageDisplay> objects)
{
visitAllNodesToHighlight(tableRoot, objects);
repaint();
}
/**
* Marks the nodes on which a given operation could not be performed
* e.g. paste rendering settings.
*
* @param type The type of data objects.
* @param ids Collection of object's ids.
*/
void markUnmodifiedNodes(Class type, Collection<Long> ids)
{
removeTreeSelectionListener(selectionListener);
visitAllNodes(tableRoot, type, ids);
Iterator<ImageTableNode> i = nodes.iterator();
ImageTableNode node;
Object ho;
int row = 0;
long id;
selectionModel.clearSelection();
while (i.hasNext()) {
node = i.next();
ho = node.getHierarchyObject();
if (ho.getClass().equals(type) && ho instanceof DataObject) {
id = ((DataObject) ho).getId();
if (ids.contains(id)) {
row = getRowForPath(node.getPath());
selectionModel.addSelectionInterval(row, row);
}
}
}
nodes.clear();
repaint();
addTreeSelectionListener(selectionListener);
}
/**
* Overridden to pop up a menu when the user right-clicks on a selected
* item.
* @see OMETreeTable#onMousePressed(MouseEvent)
*/
protected void onMousePressed(MouseEvent e)
{
if (e.isPopupTrigger()) view.showMenu(e.getPoint());
}
/**
* Overridden to pop up a menu when the user right-clicks on a selected
* item.
* @see OMETreeTable#onMouseReleased(MouseEvent)
*/
protected void onMouseReleased(MouseEvent e)
{
int count = e.getClickCount();
if (count == 2) {
if (model.getType() != DataBrowserModel.GROUP)
view.viewSelectedNode();
} else {
if (e.isPopupTrigger()) view.showMenu(e.getPoint());
}
}
/**
* Overridden to display a larger thumbnail when mousing over.
* @see OMETreeTable#onMouseEnter(MouseEvent)
*/
protected void onMouseEnter(MouseEvent e)
{
handleRollOver(e.getPoint());
}
/**
* Overridden to hide the thumbnail.
* @see OMETreeTable#onMouseExited(MouseEvent)
*/
protected void onMouseExited(MouseEvent e)
{
view.rollOverNode(null);
}
}