/********************************************************************************
* *
* (c) Copyright 2010 Verizon Communications USA and The Open University UK *
* *
* This software is freely distributed in accordance with *
* the GNU Lesser General Public (LGPL) license, version 3 or later *
* as published by the Free Software Foundation. *
* For details see LGPL: http://www.fsf.org/licensing/licenses/lgpl.html *
* and GPL: http://www.fsf.org/licensing/licenses/gpl-3.0.html *
* *
* This software is provided by the copyright holders and contributors "as is" *
* and any express or implied warranties, including, but not limited to, the *
* implied warranties of merchantability and fitness for a particular purpose *
* are disclaimed. In no event shall the copyright owner or contributors be *
* liable for any direct, indirect, incidental, special, exemplary, or *
* consequential damages (including, but not limited to, procurement of *
* substitute goods or services; loss of use, data, or profits; or business *
* interruption) however caused and on any theory of liability, whether in *
* contract, strict liability, or tort (including negligence or otherwise) *
* arising in any way out of the use of this software, even if advised of the *
* possibility of such damage. *
* *
********************************************************************************/
package com.compendium.ui.tags;
import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.util.Date;
import java.util.Vector;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.compendium.ProjectCompendium;
import com.compendium.core.datamodel.Code;
import com.compendium.core.datamodel.IModel;
import com.compendium.core.datamodel.PCSession;
import com.compendium.ui.IUIConstants;
/**
* This class create a stencil icon which can be dragged and creat a node.
*
* @author Michelle Bachler
*/
public class UIDraggableTreeCellRenderer extends DefaultTreeCellRenderer
implements DragSourceListener, DragGestureListener, DropTargetListener,
Transferable, MouseListener {
/**
* class's own logger
*/
static final Logger log = LoggerFactory.getLogger(UIDraggableTreeCellRenderer.class);
private Icon leafIcon = null;
private Icon openIcon = null;
private Icon closedIcon = null;
/** The DragSource object associated with this draggable item.*/
private DragSource dragSource;
/** The drop target object associated with this draggable item.*/
private DropTarget dropTarget = null;
/** The tooltip text.*/
private String sTip = ""; //$NON-NLS-1$
private Code oCode = null;
private IModel model = null;
private PCSession session = null;
private JTree tree = null;
private DefaultMutableTreeNode oTreeNode = null;
private Vector vtGroupItem = null;
private boolean isGroup = false;
/** The data flavors supported by this class.*/
public static final DataFlavor[] supportedFlavors = { null };
static {
try {
supportedFlavors[0] = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType);
}
catch (Exception ex) {
log.error("Error...", ex);
}
}
/**
* The Constructor.
*/
public UIDraggableTreeCellRenderer() {
model = ProjectCompendium.APP.getModel();
session = model.getSession();
dragSource = new DragSource();
dragSource.createDefaultDragGestureRecognizer((Component)this, DnDConstants.ACTION_COPY, this);
dropTarget = new DropTarget((JComponent)this, this);
// GET ICONS
DefaultTreeCellRenderer check = new DefaultTreeCellRenderer();
leafIcon = check.getLeafIcon();
openIcon = check.getOpenIcon();
closedIcon = check.getClosedIcon();
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
oTreeNode = (DefaultMutableTreeNode) value;
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
String text = ""; //$NON-NLS-1$
this.tree = tree;
isGroup = false;
if (oTreeNode.getUserObject() instanceof Vector) {
Vector item = (Vector)oTreeNode.getUserObject();
String sCodeGroupID = (String)item.elementAt(0);
isGroup = true;
// DISTIGUISH THE ACTIVE GROUP
if ( (ProjectCompendium.APP.getActiveCodeGroup()).equals(sCodeGroupID) )
setForeground(IUIConstants.DEFAULT_COLOR);
setText((String)item.elementAt(1));
} else {
oCode = (Code)oTreeNode.getUserObject();
text = oCode.getName();
try {
int count = (model.getCodeService()).getNodeCount(model.getSession(), oCode.getId());
setText(text+" ("+count+")"); //$NON-NLS-1$ //$NON-NLS-2$
}
catch(Exception ex) {
log.error("Error...", ex);
//ProjectCompendium.APP.displayError("Exception: (UICodeMaintPanel.getListCellRendererComponent) \nUnable to calculate usage for "+code.getName() +"\n"+ex.getMessage());
}
}
// YOU CAN'T SELECT A GROUP HEADING!
if (!leaf) {
selected = false;
tree.removeSelectionRow(row);
}
Icon icon = null;
if (isGroup) {
if (expanded)
icon = openIcon;
else
icon = closedIcon;
} else if (leaf) {
icon = leafIcon;
}
setIcon(icon);
//repaint();
return this;
}
/**
* Return the code associated with this draggable item.
* @return Code the code associated with this draggable item.
*/
public Code getCode() {
return oCode;
}
// CODE METHODS
/**
* Check that a code with the given text does not already exist.
* Return true if a match found, else false.
* @param codeID the id of the code to ignore.
* @param text the text to check.
* @return boolean if the code with the given name already exists.
*/
private boolean checkCode(String codeID, String text) {
return false;
}
// CODE GROUP METHODS
/**
* Add the given code to the current code group
*/
private void onAddToGroup(Code code) {
if (vtGroupItem == null) {
return;
}
String sCodeGroupID = ""; //$NON-NLS-1$
Vector group = vtGroupItem;
sCodeGroupID = (String)group.elementAt(0);
try {
// ONLY ADD IF IT WAS NOT AN EXISITING CODE
// check model
//if ( !htExistingCodes.containsKey(code.getId()) ) {
String sAuthor = model.getUserProfile().getUserName();
Date date = new Date();
// UPDATE DATABASE
model.getGroupCodeService().createGroupCode(session, code.getId(), sCodeGroupID, sAuthor, date, date);
// UPDATE MODEL
model.addCodeGroupCode(sCodeGroupID, code.getId(), code);
//}
} catch (Exception ex) {
log.error("Error...", ex);
}
}
private void onRemoveFromGroup(Code code) {
if (vtGroupItem == null) {
return;
}
String sCodeGroupID = ""; //$NON-NLS-1$
Vector group = vtGroupItem;
sCodeGroupID = (String)group.elementAt(0);
try {
// ONLY REMOVE, IF IT WAS AN EXISTING CODE
// CHECK MODEL
//if ( htExistingCodes.containsKey(oldcode.getId()) ) {
// UPDATE DATABASE
model.getGroupCodeService().delete(session, code.getId(), sCodeGroupID );
// UPDATE MODEL
model.removeCodeGroupCode(sCodeGroupID, code.getId());
//}
}
catch(Exception ex) {
log.error("Error...", ex);
}
// YOU YOU ARE ADDING OR REMOVING FROM THE ACTIVEGROUP,
// REFRESH THE CHOICE BOX ON THE MAIN TOOLBAR
if (sCodeGroupID.equals(ProjectCompendium.APP.getActiveCodeGroup())) {
ProjectCompendium.APP.updateCodeChoiceBoxData();
}
//oParentDialog.updateTreeData();
}
/**
* Set the current code group as the active group.
*/
private void onActiveGroup() {
String newActive = (String)vtGroupItem.elementAt(0);
String oldActive = ProjectCompendium.APP.getActiveCodeGroup();
if (oldActive.equals(newActive)) {
if (ProjectCompendium.APP.setActiveCodeGroup("")) { //$NON-NLS-1$
//oParentDialog.updateTreeData();
}
}
else if (ProjectCompendium.APP.setActiveCodeGroup(newActive)) {
//oParentDialog.updateTreeData();
}
}
// MOUSE LISTENER METHODS
/**
* Handles the single and double click events.
* @param evt the associated MouseEvent.
*/
public void mouseClicked(MouseEvent evt) {
boolean isRightMouse = SwingUtilities.isRightMouseButton(evt);
boolean isLeftMouse = SwingUtilities.isLeftMouseButton(evt);
if (ProjectCompendium.isMac &&
(evt.getButton() == 3 && evt.isShiftDown())) {
isRightMouse = true;
isLeftMouse = false;
}
}
/**
* Open right-click menu for this item.
*/
private void showPopupMenu(int x, int y) {
}
/**
* Handles mouse pressed events
* @param evt, the associated MouseEvent.
*/
public void mousePressed(MouseEvent evt) {}
/**
* Handles mouse released events
* @param evt the associated MouseEvent.
*/
public void mouseReleased(MouseEvent evt) {}
/**
* Visualizes when a mouse enters the node.
* Also, display any detail text for the node in the status bar.
* @param evt, the associated MouseEvent.
*/
public void mouseEntered(MouseEvent evt) {}
/**
* Visualizes when a mouse exits the node.
* @param evt the associated MouseEvent.
*/
public void mouseExited(MouseEvent evt) {}
// DRAG AND DROP METHODS
// TRANSFERABLE
/**
* Returns an array of DataFlavor objects indicating the flavors the data
* can be provided in.
* @return an array of data flavors in which this data can be transferred
*/
public DataFlavor[] getTransferDataFlavors() {
return supportedFlavors;
}
/**
* Returns whether or not the specified data flavor is supported for
* this object.
* @param flavor the requested flavor for the data
* @return boolean indicating whether or not the data flavor is supported
*/
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.isMimeTypeEqual(DataFlavor.javaJVMLocalObjectMimeType);
}
/**
* Returns an object which represents the data to be transferred. The class
* of the object returned is defined by the representation class of the flavor.
*
* @param flavor the requested flavor for the data
* @see DataFlavor#getRepresentationClass
* @exception IOException if the data is no longer available in the requested flavor.
* @exception UnsupportedFlavorException if the requested data flavor is not supported.
*/
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (flavor.isMimeTypeEqual(DataFlavor.javaJVMLocalObjectMimeType))
return this;
else return null;
}
// SOURCE
/**
* A <code>DragGestureRecognizer</code> has detected
* a platform-dependent drag initiating gesture and
* is notifying this listener
* in order for it to initiate the action for the user.
* <P>
* @param e the <code>DragGestureEvent</code> describing
* the gesture that has just occurred
*/
public void dragGestureRecognized(DragGestureEvent e) {
InputEvent in = e.getTriggerEvent();
if (in instanceof MouseEvent) {
MouseEvent evt = (MouseEvent)in;
boolean isLeftMouse = SwingUtilities.isLeftMouseButton(evt);
if (isLeftMouse && !evt.isAltDown()) {
dragSource.startDrag(e, DragSource.DefaultCopyDrop, this, this);
}
}
}
/**
* This method is invoked to signify that the Drag and Drop
* operation is complete. The getDropSuccess() method of
* the <code>DragSourceDropEvent</code> can be used to
* determine the termination state. The getDropAction() method
* returns the operation that the drop site selected
* to apply to the Drop operation. Once this method is complete, the
* current <code>DragSourceContext</code> and
* associated resources become invalid.
* HERE THE METHOD DOES NOTHING.
*
* @param e the <code>DragSourceDropEvent</code>
*/
public void dragDropEnd(DragSourceDropEvent e) {}
/**
* Called as the cursor's hotspot enters a platform-dependent drop site.
* This method is invoked when all the following conditions are true:
* <UL>
* <LI>The cursor's hotspot enters the operable part of a platform-
* dependent drop site.
* <LI>The drop site is active.
* <LI>The drop site accepts the drag.
* </UL>
* HERE THE METHOD DOES NOTHING.
*
* @param e the <code>DragSourceDragEvent</code>
*/
public void dragEnter(DragSourceDragEvent e) {}
/**
* Called as the cursor's hotspot exits a platform-dependent drop site.
* This method is invoked when any of the following conditions are true:
* <UL>
* <LI>The cursor's hotspot no longer intersects the operable part
* of the drop site associated with the previous dragEnter() invocation.
* </UL>
* OR
* <UL>
* <LI>The drop site associated with the previous dragEnter() invocation
* is no longer active.
* </UL>
* OR
* <UL>
* <LI> The current drop site has rejected the drag.
* </UL>
* HERE THE METHOD DOES NOTHING.
*
* @param e the <code>DragSourceEvent</code>
*/
public void dragExit(DragSourceEvent e) {}
/**
* Called as the cursor's hotspot moves over a platform-dependent drop site.
* This method is invoked when all the following conditions are true:
* <UL>
* <LI>The cursor's hotspot has moved, but still intersects the
* operable part of the drop site associated with the previous
* dragEnter() invocation.
* <LI>The drop site is still active.
* <LI>The drop site accepts the drag.
* </UL>
* HERE THE METHOD DOES NOTHING.
*
* @param e the <code>DragSourceDragEvent</code>
*/
public void dragOver(DragSourceDragEvent e) {}
/**
* Called when the user has modified the drop gesture.
* This method is invoked when the state of the input
* device(s) that the user is interacting with changes.
* Such devices are typically the mouse buttons or keyboard
* modifiers that the user is interacting with.
* HERE THE METHOD DOES NOTHING.
*
* @param e the <code>DragSourceDragEvent</code>
*/
public void dropActionChanged(DragSourceDragEvent e) {}
//TARGET
/**
* Called if the user has modified
* the current drop gesture.
* <P>HERE DOES NOTHING</P>
* @param e the <code>DropTargetDragEvent</code>
*/
public void dropActionChanged(DropTargetDragEvent e) {
//log.info("IN dropActionChanged of Target");
}
/**
* Called when a drag operation is ongoing, while the mouse pointer is still
* over the operable part of the drop site for the <code>DropTarget</code>
* registered with this listener.
* <P>HERE DOES NOTHING</P>
* @param e the <code>DropTargetDragEvent</code>
*/
public void dragOver(DropTargetDragEvent e) {
//log.info("dragtargetdrag event at "+e.getLocation());
}
/**
* Called while a drag operation is ongoing, when the mouse pointer has
* exited the operable part of the drop site for the
* <code>DropTarget</code> registered with this listener.
* <P>HERE DOES NOTHING</P>
* @param e the <code>DropTargetEvent</code>
*/
public void dragExit(DropTargetEvent e) {
//log.info("In drag exit of Target");
}
/**
* Called while a drag operation is ongoing, when the mouse pointer enters
* the operable part of the drop site for the <code>DropTarget</code>
* registered with this listener.
* <P>HERE DOES NOTHING</P>
* @param e the <code>DropTargetDragEvent</code>
*/
public void dragEnter(DropTargetDragEvent e) {
log.info("dragEnter - about to accept DnDConstants.ACTION_LINK");
//e.acceptDrag(DnDConstants.ACTION_LINK);
//e.acceptDrag(DnDConstants.ACTION_MOVE);
}
/**
* Called when the drag operation has terminated with a drop on
* the operable part of the drop site for the <code>DropTarget</code>
* registered with this listener.
* <p>
* Process a drop for createing links - CURRENTLY ONLY ON THE MAC (WINDOW/LINUX use mouse events).
* <P>
* @param e the <code>DropTargetDropEvent</code>
*/
public void drop(DropTargetDropEvent e) {
if (!isGroup) {
return;
} else {
DropTarget drop = (DropTarget)e.getSource();
if (drop.getComponent() instanceof UIDraggableTreeCellRenderer) {
UIDraggableTreeCellRenderer item = (UIDraggableTreeCellRenderer)drop.getComponent();
}
}
}
}