/* * Copyright 2003-2010 Tufts University Licensed under the * Educational Community License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may * obtain a copy of the License at * * http://www.osedu.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing * permissions and limitations under the License. */ package tufts.vue; import tufts.vue.gui.*; import tufts.Util; import java.util.*; import java.lang.*; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; /** * Base class for a VUE tool, that may have sub-tools. * * Sub-classes AbstractAction for addActionListener, although * that usage is probably on it's way out when we get around * to cleaning up the VueTool code & it's supporting GUI classes. * * @version $Revision: 1.87 $ / $Date: 2010-02-03 19:17:41 $ / $Author: mike $ */ public abstract class VueTool extends AbstractAction implements PickContext.Acceptor { private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(VueTool.class); /** the tool's unique id **/ protected String mID = null; /** the tool name **/ protected String mToolName = null; /** the tool tip text, if any **/ protected String mToolTipText = null; /** the default icon to draw in the up or idle states **/ protected Icon mUpIcon; /** the icon to use for mouse down or press states **/ protected Icon mDownIcon; /** the icon to use for selected state **/ protected Icon mSelectedIcon; /** the icon to use for disabled state **/ protected Icon mDisabledIcon; /** the rollover icon **/ protected Icon mRolloverIcon; /** the menu item state (if any) **/ protected Icon mMenuItemIcon; /** the menu item selected state **/ protected Icon mMenuItemSelectedIcon; /** the icon to overlay if there are any sub items **/ protected Icon mOverlayUpIcon; /** the icon to overlay if there are any sub items **/ protected Icon mOverlayDownIcon; /** the raw icon used as a base for the generated icons */ protected Icon mRawIcon; /** Short-cut key to active this tool */ protected char mShortcutKey = 0; /** Short-cut key-code to temporarily active this tool while this key is held down */ protected int mActiveWhileDownKeyCode = 0; /** A cursor to use with this tool */ protected java.awt.Cursor mCursor; protected HashMap mResourceMap = new HashMap(); protected AbstractButton mLinkedButton; private boolean isTemporary; private JPanel mToolPanel; /** tool display order by keyname **/ protected Vector mSubToolIDs = new Vector(); /** the sub tool map with toolname as key **/ protected Map<String,VueTool> mSubToolMap = new HashMap(); /** the parent tool -- if this is a subtool **/ protected VueTool mParentTool = null; /** the currently active subtool name **/ protected VueTool mSelectedSubTool = null; private static final Map<Class<? extends VueTool>, VueTool> InstanceMap = new HashMap(); /** for storing all the tools by name, including sub-tools */ private static final java.util.List<VueTool> mAllTools = new java.util.ArrayList(); //private LWComponent mStyleCache; public VueTool() { super(); VueTool old = InstanceMap.put(getClass(), this); if (DEBUG.TOOL) { if (old != null) { System.out.println("already instanced: " + getClass() + "; was=" + old); // need to permanently clear out this item for the multiple instance cases //tufts.Util.printStackTrace("already instanced: " + getClass() + "; was=" + old); } else System.out.println("instanced " + this); } //mStyleCache = createStyleCache(); mAllTools.add(this); } /** @return list of all tool instances */ public static java.util.List<VueTool> getTools() { return mAllTools; } public boolean isActive() { return VueToolbarController.getActiveTool() == this; } public static VueTool getInstance(Class<? extends VueTool> clazz) { final VueTool tool = InstanceMap.get(clazz); if (tool == null) tufts.Util.printStackTrace("failed to find tool instance of type: " + clazz); return tool; } /** * getID * Gets the unique tool id. * @return String the tool's id. **/ public String getID() { return mID; } /** * setID * Sets the tool's unique id. * @param pID the id String **/ public void setID(String pID ) { mID = pID; //System.out.println("Initializing tool " + this); ResourceBundle rb = VueResources.getBundle(); Enumeration e = rb.getKeys(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); if (key.startsWith(pID)) { String localKey = key.substring(mID.length()+1, key.length()); //System.out.println("\tkey: " + localKey + "=" + rb.getString(key)); mResourceMap.put(localKey, rb.getString(key));// todo: use rb.getObject? } } //tool.setToolName( VueResources.getString(( pName+".name") ) ); } public String getAttribute(String key) { return (String) mResourceMap.get(key); } /** Initialize tool from resource properties. Must be called after setID. */ void initFromResources() { setToolName(getAttribute("name")); setToolTipText(getAttribute("tooltip")); Icon rawIcon = VueResources.getImageIcon(mID+".raw"); if (rawIcon != null) { setGeneratedIcons(rawIcon); } else { Icon i; if ((i = VueResources.getImageIcon(mID+".up")) != null) setIcon(i); if ((i = VueResources.getImageIcon(mID+".down")) != null) setDownIcon(i); if ((i = VueResources.getImageIcon(mID+".selected")) != null) setSelectedIcon(i); if ((i = VueResources.getImageIcon(mID+".disabled")) != null) setDisabledIcon(i); if ((i = VueResources.getImageIcon(mID+".rollover")) != null) setRolloverIcon(i); if ((i = VueResources.getImageIcon(mID+".menu")) != null) setMenuItemIcon(i); if ((i = VueResources.getImageIcon(mID+".menuselected")) != null) setMenuItemSelectedIcon(i); } setShortcutKey(VueResources.getChar(mID+".shortcutKey")); //setActiveWhileDownKey(VueResources.getChar(mID+".activeWhileDownKey")); // need to parse key code's... int cursorID = VueResources.getInt(mID+".cursorID", -1); if (cursorID >= 0) { //System.out.println(tool + " found cursor ID: " + cursorID); setCursorByID(cursorID); } else { Cursor cursor = VueResources.getCursor(mID+".cursor"); if (cursor != null) setCursor(cursor); /* ImageIcon icon = VueResources.getImageIcon( mID+".cursor"); if (icon != null) { //System.out.println(tool + " found cursor icon: " + icon); //System.out.println(tool + " cursor icon image: " + icon.getImage()); Toolkit toolkit = Toolkit.getDefaultToolkit(); //System.out.println("Creating cursor for " + icon); tool.setCursor(toolkit.createCustomCursor(icon.getImage(), new Point(0,0), mID+":"+icon.toString())); } */ } } /** @return true if the tool requests a button the toolbar */ public boolean hasToolbarButton() { return mUpIcon != null; } /** * getSelectionID * This method returns the full id of the selected tool * For example, a selected parent and subtool would return * partenID.subToolID. This is obttained by getting * the subtools id, ir there is one selected, or the partent if not. * * @return String t-the selection ID string **/ public String getSelectionID() { String id = getID(); if( getSelectedSubTool() != null ) { id = getSelectedSubTool().getID(); } return id; } /** * getToolName * Gets a display name for the tool * @return String the display name of the tool **/ public String getToolName() { return mToolName; } /** * setToolName * Sets the tool's display name * @param pName - the tool's name **/ public void setToolName( String pName ) { mToolName = pName; } /** * getToolTipText * Gets the tool tip text string for the tool * @return String the tool tip text **/ public String getToolTipText() { if (mToolTipText == null) return null; else if (getShortcutKey() == 0) return mToolTipText; else return mToolTipText + " (" + getShortcutKey() + ")"; } /** * setToolTipText * Sets the tool tip text string * @param pText the tip text **/ public void setToolTipText( String pText ) { mToolTipText = pText; } /** * setCursorByID * Gets the cursor for the tool based on the cursor ID * @param id a cursor constant from java.awt.Cursor in the range 0-13 (as of java 1.4.1) **/ public void setCursorByID(int id) { if (id < 0 || id > 13) throw new IllegalArgumentException("cursor id outside range 0-13: " + id); setCursor(Cursor.getPredefinedCursor(id)); } /** * setCursor * Gets the cursor for the tool based on the cursor ID * @param pCursor a java.awt.Cursor object **/ public void setCursor(Cursor pCursor) { mCursor = pCursor; } /** * getCursor * Gets the cursor for the tool * @return an instance of java.awt.Cursor **/ public Cursor getCursor() { Cursor cursor = null; if (hasSubTools()) cursor = getSelectedSubTool().getCursor(); if (cursor == null) cursor = mCursor; return cursor; } public void setShortcutKey(char pChar) { mShortcutKey = pChar; } public char getShortcutKey() { return mShortcutKey; } public char getBackwardCompatShortcutKey() { return 0; } public void setActiveWhileDownKeyCode(int keyCode) { mActiveWhileDownKeyCode = keyCode; } public int getActiveWhileDownKeyCode() { return mActiveWhileDownKeyCode; } /** if this returns non-null, only objects of the given type will be selected * by the dragged selector */ public Object getSelectionType() { return null; } /** * override this for subclasses to provide limits on what can be selected * (interface LWSelection.Acceptor) */ public boolean accept(PickContext pc, LWComponent c) { if (pc.isRegionPick()) { if (getSelectionType() == null) return true; else return getSelectionType() == c.getTypeToken(); } else { // always accept point-picks return true; } // if (getSelectionType() != null) // return getSelectionType().isInstance(c); // else // return true; } public void setLinkedButton(AbstractButton b) { mLinkedButton = b; } /** supports the click-selection of objects */ public boolean supportsSelection() { return true; } /** @return true of this tool supports any map editing actions (e.g., add new node) */ public boolean supportsEditActions() { return supportsSelection(); } /** @return true of this tool supports double-click for on-map label edit */ public boolean supportsEditLabel() { return supportsEditActions(); } /** @return true by deafult if tool supports drag operations -- override to change */ public boolean supportsDrag(InputEvent e) { return supportsEditActions(); } /** does tool make use of a dragged box for selecting objects or regions of the map, for any purpose * (e.g., may not just be for selection) */ public boolean supportsDraggedSelector(MapMouseEvent e) { return true; } /** @return true if the tool supports resize controls */ public boolean supportsResizeControls() { return supportsSelection(); } /** does tool make use of right click -- meaning the * viewer shouldn't pop a context menu on right-clicks */ public boolean usesRightClick() { return false; } /** does the tool draw it's own decorations, reqiring a repaint when it's selected/deselected? */ public boolean hasDecorations() { return false; } public final boolean supportsXORSelectorDrawing() { return false; } /** @return true -- sub-impl's return false if this tool is currently preventing changes to any other active tool */ public boolean permitsToolChange() { return true; } /** what to do, if anything, when the tool is selected */ public void handleToolSelection(boolean selected, VueTool otherTool) { if (DEBUG.TOOL) Log.debug("handleToolSelection: " + this + ": " + selected + "; from=" + otherTool); } public DrawContext getDrawContext(DrawContext dc) { return dc; } public void handlePreDraw(DrawContext dc, MapViewer viewer) {} public void handlePostDraw(DrawContext dc, MapViewer viewer) {} /** * called upon entering/exiting full screen * @param entering -- true if entering full screen, false if exiting * @param nativeMode -- true if entering or exiting native (non-working) full screen mode */ public void handleFullScreen(boolean entering, boolean nativeMode) {} /** mark this tool as temporarily activated */ public void setTemporary(boolean t) { isTemporary = t; } public boolean isTemporary() { return isTemporary; } // // possible: default true, but zoom / pres tool / hand tool return false. // // Tools that aren't editors should not allow temporary // // activation to other tools that are editors // // e.g., zoom tool can use ctrl/alt for zoom modifiers w/out activating link tool on accidental drag // public boolean isEditor() { // return true; // } public void drawSelector(DrawContext dc, java.awt.Rectangle r) { //g.fill(r);//if mac? dc.g.draw(r); } public boolean handleKeyPressed(java.awt.event.KeyEvent e) { return false; } public boolean handleKeyReleased(java.awt.event.KeyEvent e) { return false; } /** @return false by default: returning true will have the effect of disabling the tool-tip style * node rollovers on the map. */ public boolean handleMouseMoved(MapMouseEvent e) { if (DEBUG.MOUSE && DEBUG.META) System.out.println(this + " handleMouseMoved " + e); return false; } public boolean handleMousePressed(MapMouseEvent e) { if (DEBUG.TOOL) System.out.println(this + " handleMousePressed " + e); return false; } public boolean handleComponentPressed(MapMouseEvent e) { return false; } public boolean handleMouseDragged(MapMouseEvent e) { //System.out.println(this + " handleMouseDragged " + e); return false; } public boolean handleMouseReleased(MapMouseEvent e) { if (DEBUG.TOOL) System.out.println(this + " handleMouseReleased " + e); return false; } public boolean handleMouseClicked(MapMouseEvent e) { if (DEBUG.TOOL) System.out.println(this + " handleMouseClicked " + e); return false; } public void handleDragAbort() {} /** @return false by default: impl's can return true if they don't * want the viewer to change the display on focal load (e.g., do a zoom fit) */ public boolean handleFocalSwitch(MapViewer viewer, LWComponent oldFocal, LWComponent newFocal) { return false; } public boolean handleSelectorRelease(MapMouseEvent e) { if (DEBUG.TOOL) System.out.println(this + " handleSelectorRelease " + e); return false; } public PickContext initPick(PickContext pc, float x, float y) { return pc; } public PickContext initPick(PickContext pc, java.awt.geom.Rectangle2D.Float rect) { return pc; } /* private static class SlideProxyMap extends LWMap { private LWMap srcMap; SlideProxyMap(LWMap srcMap) { super("SlideViewer"); this.srcMap = srcMap; } public LWPathwayList getPathwayList() { return srcMap.getPathwayList(); } } private static MapViewer SlideViewer = null; private static LWMap SlideMap = null; private static LWComponent CurSlide = null; */ public void handleSelectionChange(LWSelection s) { /* if (s.size() == 1 && !(s.getSource() instanceof tufts.vue.ui.SlideViewer) && !(s.first().getParent() instanceof LWSlide) ) { // don't display in slide viewer if is a child of an existing // slide: either we actually just selected in the slide viewer // itself, or we selected a child of a LWSlide, which for now // is just going to show that slide. tufts.vue.ui.SlideViewer.getInstance().loadFocal(s.first()); return; } */ /* if (s.size() == 1 && VUE.isActiveViewerOnLeft()) tufts.vue.ui.SlideViewer.setFocused(s.first()); if (s.size() == 1 && s.first().getSlide() != null && VUE.multipleMapsVisible() && VUE.isActiveViewerOnLeft()) { */ /* // Experimental creation of a slide in the right viewer if (SlideViewer == null) { //SlideMap = new LWMap("SlideViewer"); SlideMap = new SlideProxyMap(s.first().getMap()); SlideViewer = new MapViewer(SlideMap); VUE.getRightTabbedPane().addViewer(SlideViewer); } else { SlideMap.removeChild(CurSlide); } CurSlide = s.first().getSlide(); SlideMap.addChild(CurSlide); ZoomTool.setZoomFitRegion(SlideViewer, CurSlide.getBounds(), 16, false); */ /* OLD if (rightViewer.getMap() == VUE.getActiveMap()) { tufts.Util.printStackTrace("CREATING NEW SLIDE VIEWER"); slideMap = new LWMap("SlideView"); slideMap.addChild(slide); slideViewer = new MapViewer(slideMap); VUE.getRightTabbedPane().addViewer(slideViewer); } else if (rightViewer.getMap().getLabel().equals("SlideView")) { tufts.Util.printStackTrace("USING EXISTING SLIDE VIEWER"); slideViewer = rightViewer; slideMap = slideViewer.getMap(); slideMap.removeChildren(slideMap.getChildIterator()); slideMap.addChild(slide); } if (slideMap != null) ZoomTool.setZoomFitRegion(slideViewer, slide.getBounds(), 16); */ //} //----------------------------------------------------------------------------- // The right viewer tracks & zooms to whatever is selected // in the left viewer if it's showing the same map as the left viewer. // TEST ONLY FOR NOW. //----------------------------------------------------------------------------- if (false && !s.isEmpty() && VUE.multipleMapsVisible() && VUE.isActiveViewerOnLeft() && VUE.getRightTabbedPane().getSelectedViewer().getMap() == VUE.getActiveMap() ) { MapViewer viewer = VUE.getRightTabbedPane().getSelectedViewer(); ZoomTool.setZoomFitRegion(viewer, s.getBounds(), 16, false); } } // public LWComponent pickNodeAt(PickContext pc, float mapX, float mapY) { // return LWTraversal.PointPick.pick(pc, mapX, mapY); // } // public LWComponent pickNodeAt(PickContext pc) { // return LWTraversal.PointPick.pick(pc); // } /** * setParentTool * Sets the parent tool if this is a subtool * @param pTool - the VueTool of the parent **/ public void setParentTool( VueTool pTool) { mParentTool = pTool; } /** * getParentTool * Gets the parent tool if the tool is a subtool * @return VueTool the parent tool **/ public VueTool getParentTool() { return mParentTool; } /** * setSelectedSubTool *( This sets teh active subtool. * @param pTool - the selected subtool **/ public void setSelectedSubTool( VueTool pTool) { if (DEBUG.TOOL) out("setSelectedSubTool: " + pTool); if (DEBUG.TOOL && DEBUG.META) tufts.Util.printStackTrace("setSelectedSubTool: " + pTool); mSelectedSubTool = pTool; if (VUE.getActiveViewer() !=null) VUE.getActiveViewer().setCursor(mSelectedSubTool.getCursor()); } /** * getCurrentSubTool * Gets the active selected subtool * @return VueTool the active subtool **/ public VueTool getSelectedSubTool() { return mSelectedSubTool; } /** * hasSubTools * Determines if this tool has sub tools. * @return boolean - true if has subtools; false if no sub tools **/ public boolean hasSubTools() { return !( mSubToolMap.isEmpty()); } /** * getSubToolIIDs * Returns an ordered Vector of subtool ids for this tool * @return Vector - the display order set of sub tool ids **/ public Vector getSubToolIDs() { return mSubToolIDs; } /** * getSubTool * Gets the subtool with the given ID. * @param pID the unique string ID of the subtool * @return VueTool the subtool with the passed ID **/ public VueTool getSubTool( String pID) { Object tool = null; tool = mSubToolMap.get( pID); return (VueTool) tool; } /** * addSubTool * Adds the passed VueTool as a subtool of this tool * @param VueTool - the new subtool **/ public void addSubTool( VueTool pTool) { mSubToolMap.put( pTool.getID(), pTool); mSubToolIDs.add( pTool.getID() ); } /** * removeSubTool * This removes teh current tool from the set of subtools * @param pID - the subtool 's IDto remove **/ public void removeSubTool( String pID) { mSubToolMap.remove( pID); mSubToolIDs.remove( pID); } ///////////////////////////// // // The follow are a slew of icon properties used for tool GUI // //////////////////////////////////////// public void setIcon( Icon pIcon) { mUpIcon = pIcon; } public Icon getIcon() { return mUpIcon; } public void setDownIcon( Icon pIcon) { mDownIcon = pIcon; } public Icon getDownIcon() { return mDownIcon; } public void setSelectedIcon( Icon pIcon) { mSelectedIcon = pIcon; } public Icon getSelectedIcon() { return mSelectedIcon; } public void setDisabledIcon( Icon pIcon) { mDisabledIcon = pIcon; } public Icon getDisabledIcon( ) { return mDisabledIcon; } public void setMenuItemSelectedIcon( Icon pIcon) { mMenuItemSelectedIcon = pIcon; } public Icon getMenuItemSelectedIcon() { return mMenuItemSelectedIcon; } public void setMenuItemIcon( Icon pIcon) { mMenuItemIcon = pIcon; } public Icon getMenuItemIcon() { return mMenuItemIcon; } public void setRolloverIcon( Icon pIcon) { mRolloverIcon = pIcon; } public Icon getRolloverIcon() { return mRolloverIcon; } public void setOverlayUpIcon( Icon pIcon) { mOverlayUpIcon = pIcon; } public Icon getOverlayUpIcon() { return mOverlayUpIcon; } public void setOverlayDownIcon( Icon pIcon) { mOverlayDownIcon = pIcon; } public Icon getOverlayDownIcon() { return mOverlayDownIcon; } public void setGeneratedIcons(Icon pIcon) { //System.out.println(this + " generating icons from " + pIcon); mRawIcon = pIcon; setIcon(new ToolIcon(mRawIcon, ToolIcon.UP)); setDownIcon(new ToolIcon(mRawIcon, ToolIcon.PRESSED)); setSelectedIcon(new ToolIcon(mRawIcon, ToolIcon.SELECTED)); setDisabledIcon(new ToolIcon(mRawIcon, ToolIcon.DISABLED)); setRolloverIcon(new ToolIcon(mRawIcon, ToolIcon.ROLLOVER)); setMenuItemIcon(new ToolIcon(mRawIcon, ToolIcon.MENU)); setMenuItemSelectedIcon(new ToolIcon(mRawIcon, ToolIcon.MENU_SELECTED)); } public void out(String s) { //System.out.println(this + ": " + s); //Log.debug(getClass().getSimpleName() + ": " + s); Log.debug(this + ": " + s); } protected static void outln(String s) { System.out.println("VueTool: " + s); } static class ToolIcon extends tufts.vue.gui.VueButtonIcon { static final int Width = 38; static final int Height = 26; protected ToolIcon(Icon rawIcon, int type) { super(rawIcon, type, Width, Height); super.isRadioButton = true; } } public Icon getRawIcon() { return mRawIcon; } public String toString() { final String name = tufts.Util.TERM_PURPLE + getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this)) + tufts.Util.TERM_CLEAR ; String params = getID(); if (getSelectedSubTool() != null) params += " sub=" + getSelectedSubTool(); if (getSelectionType() != null) { if (params != null) params += "; "; params += "type=" + getSelectionType(); } if (isTemporary()) { if (params != null) params += "; "; params += "TEMPORARY"; } if (params != null) return name + "[" + params + "]"; else return name; } /** * actionPerformed * Action interface * This method is called when the tool is activated by a user action * It notifies the parent tool, if any, and calls the abstract hook * handleSelection * @param pEvent the action event **/ public void actionPerformed(ActionEvent pEvent) { if (DEBUG.TOOL) System.out.println("\n" + this + " " + pEvent); VueTool parent = this.getParentTool(); if (parent != null) parent.setSelectedSubTool(this); if (parent != null) { //if (DEBUG.Enabled) Util.printStackTrace("actionPerformed tool switch to " + parent + " from " + VueToolbarController.getController().getActiveTool()); parent.handleToolSelection(true, VueToolbarController.getController().getActiveTool()); } if (this.hasSubTools()) VueToolbarController.getController().handleToolSelection(this.getSelectedSubTool()); else VueToolbarController.getController().handleToolSelection(this); } public JPanel getContextualPanel() { if (mToolPanel == null) mToolPanel = createToolPanel(); return mToolPanel; } public JPanel createToolPanel() { JPanel p = new JPanel(); p.add(new JLabel(getToolName())); return p; } }