/* * 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 java.lang.*; import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import tufts.vue.LinkTool.LinkModeTool; /** * Track the selection and change the current contextual tool panel as appropriate, * as well as relaying selection events to the current contextual tool panel. * Also maintin list of VueToolSelectionListeners, for those who want to know * when the currently active tool changes. * * This could use a re-write, along with VueToolPanel, VueTool, and the way * contextual toolbars are handled. * * @version $Revision: 1.82 $ / $Date: 2010-02-03 19:17:41 $ / $Author: mike $ * **/ public class VueToolbarController implements LWSelection.Listener, LWComponent.Listener { private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(VueToolbarController.class); /** the list of tool names is under this key in the resources **/ public static final String DefaultToolsKey = VUE.isApplet() ? "appletToolbarToolNames" : "mainToolbarToolNames"; private static VueToolbarController sController; private VueToolPanel mToolPanel = null; /* a list of tool selection listeners private Vector mToolSelectionListeners = new Vector(); */ /** a list of available tools **/ private VueTool[] mVueTools = null; private int[] separatorPositions = null; /* current contextual panel **/ //private JPanel mContextualPanel = null; // the currently selected tool private VueTool mSelectedTool = null; private LWCToolPanel mLWCToolPanel; static public void destroyController() { sController = null; } /** * @return the singleton controller **/ static public VueToolbarController getController() { if (sController == null) sController = new VueToolbarController(); return sController; } static public VueTool getActiveTool() { if (sController == null) return null; else return sController.getSelectedTool(); } /** * Load the tools as defined in the resource properties file, and * create the VueToolPanel. **/ protected VueToolbarController() { mVueTools = VueToolUtils.loadTools(DefaultToolsKey); separatorPositions = VueToolUtils.getSeparatorPositions(DefaultToolsKey); mToolPanel = createDefaultToolbar(); VUE.addActiveListener(VueTool.class, this); configureOntologyTools(); } /** @return array of all the top-level VueTool's */ public VueTool[] getTopLevelTools() { return mVueTools; } /* private LWCToolPanel getLWCToolPanel() { if (mLWCToolPanel == null) mLWCToolPanel = new LWCToolPanel(); return mLWCToolPanel; } */ /** * There's no good way to dynamically control tools, and if there's a real case for doing this, * there probably needs to be a better way. But I'm not even sure this will stay around so this * is a bit of hack to allow it. */ private Component ontologyNodeComponent; private Component ontologyLinkComponent; private void configureOntologyTools() { final VueTool nodeTool = VueTool.getInstance(tufts.vue.NodeTool.NodeModeTool.class); final VueTool linkTool = VueTool.getInstance(tufts.vue.LinkTool.LinkModeTool.class); final VueTool ontologyNodeTool = VueTool.getInstance(tufts.vue.NodeTool.OntologyNodeTool.class); final VueTool ontologyLinkTool = VueTool.getInstance(tufts.vue.LinkTool.OntologyLinkModeTool.class); final int ONTOLOGY_NODE_POSITION=1; final int ONTOLOGY_LINK_POSITION=1; Map buttons = getToolbar().getToolButtons(); PaletteButton parentButton = (PaletteButton)buttons.get(nodeTool.getParentTool().getID()); PaletteButton parentLinkButton = (PaletteButton)buttons.get(linkTool.getParentTool().getID()); Component[] c = parentButton.mPopup.getComponents(); ontologyNodeComponent = c[ONTOLOGY_NODE_POSITION]; c = parentLinkButton.mPopup.getComponents(); ontologyLinkComponent = c[ONTOLOGY_LINK_POSITION]; parentButton.removePopupComponent(ontologyNodeComponent); parentLinkButton.removePopupComponent(ontologyLinkComponent); /* edu.tufts.vue.ontology.ui.OntologyBrowser.getBrowser().getDockWindow().addComponentListener(new ComponentAdapter() { public void componentHidden(ComponentEvent arg0) { hideOntologicalTools(); } public void componentShown(ComponentEvent arg0) { showOntologicalTools(); } });*/ } public void showOntologicalTools() { final VueTool nodeTool = VueTool.getInstance(tufts.vue.NodeTool.NodeModeTool.class); final VueTool linkTool = VueTool.getInstance(tufts.vue.LinkTool.LinkModeTool.class); Map buttons = getToolbar().getToolButtons(); PaletteButton parentButton = (PaletteButton)buttons.get(nodeTool.getParentTool().getID()); PaletteButton parentLinkButton = (PaletteButton)buttons.get(linkTool.getParentTool().getID()); parentButton.addPopupComponent(ontologyNodeComponent); parentLinkButton.addPopupComponent(ontologyLinkComponent); parentButton.repaint(); parentLinkButton.repaint(); } public void hideOntologicalTools() { final VueTool nodeTool = VueTool.getInstance(tufts.vue.NodeTool.NodeModeTool.class); final VueTool linkTool = VueTool.getInstance(tufts.vue.LinkTool.LinkModeTool.class); final Component ontologyNodeComponent; final Component ontologyLinkComponent; final int ONTOLOGY_NODE_POSITION=1; final int ONTOLOGY_LINK_POSITION=1; Map buttons = getToolbar().getToolButtons(); PaletteButton parentButton = (PaletteButton)buttons.get(nodeTool.getParentTool().getID()); PaletteButton parentLinkButton = (PaletteButton)buttons.get(linkTool.getParentTool().getID()); Component[] c = parentButton.mPopup.getComponents(); ontologyNodeComponent = c[ONTOLOGY_NODE_POSITION]; c = parentLinkButton.mPopup.getComponents(); ontologyLinkComponent = c[ONTOLOGY_LINK_POSITION]; parentButton.removePopupComponent(ontologyNodeComponent); parentLinkButton.removePopupComponent(ontologyLinkComponent); VueTool tool = getToolbar().getSelectedTool(); //System.out.println(nodeTool.getID()); if (tool.getID().equals(nodeTool.getParentTool().getID())) { getToolbar().setSelectedTool(linkTool); getToolbar().setSelectedTool(nodeTool); nodeTool.setSelectedSubTool(nodeTool); setSelectedTool(nodeTool); } else if (tool.getID().equals(linkTool.getParentTool().getID())) { getToolbar().setSelectedTool(nodeTool); getToolbar().setSelectedTool(linkTool); nodeTool.setSelectedSubTool(linkTool); setSelectedTool(linkTool); } else { getToolbar().setSelectedTool(nodeTool); getToolbar().setSelectedTool(linkTool); getToolbar().setSelectedTool(tool); } parentButton.repaint(); parentLinkButton.repaint(); } /** * A factory method to generate the toolbar. Tools must already * be loaded before calling. **/ private VueToolPanel createDefaultToolbar() { VueToolPanel toolbar = new VueToolPanel(); toolbar.addTools(mVueTools,separatorPositions); toolbar.addTool(PresentationTool.getTool(), false); return toolbar; } /** * This method returns the selected VueTool * @returns currently active VueTool **/ public VueTool getSelectedTool() { if (getToolbar() == null) return null; else return getToolbar().getSelectedTool(); } /** introspected listener callback */ public void activeChanged(ActiveEvent e, VueTool tool) { setSelectedTool(tool); } /** * This method sets teh slected VueTool * It will attempt to update teh button group in the Toolbar * and notify listners by calling selectionChanged. * * @param VeuTool - selected tool **/ public void setSelectedTool(VueTool pTool) { final VueTool activeTool = getSelectedTool(); if (DEBUG.TOOL) Log.debug("setSelectedTool: " + pTool); if (activeTool != null && !activeTool.permitsToolChange()) { Log.warn("tool change not permitted to: " + pTool + "; active tool denies: " + activeTool); //return; } if( mToolPanel != null) { mToolPanel.setSelectedTool( pTool); } handleToolSelection( pTool); } /** * This method is called when a tool selection event happens. * It will notify all toolbar **/ final void handleToolSelection(VueTool pTool) { if (DEBUG.TOOL) Log.debug(String.format("[%s] handleToolSelection:: %s", mSelectedTool, pTool)); VueTool rootTool = pTool; // TODO: need to seriously refactor this code: the tools shouldn't be self activating // -- e.g., their installing their cursor even if the tool change is denied, so // we're going to allow the tool selection for now... if (mSelectedTool != null && !mSelectedTool.permitsToolChange()) { Log.warn("tool change not permitted to: " + pTool + "; active tool denies: " + mSelectedTool); } if (pTool.getParentTool() != null && pTool.getClass() == tufts.vue.VueSimpleTool.class) rootTool = pTool.getParentTool(); final VueTool oldActive = mSelectedTool; mSelectedTool = rootTool; // notify listeners VUE.setActive(VueTool.class, this, rootTool); if (oldActive != null && oldActive != mSelectedTool) oldActive.handleToolSelection(false, mSelectedTool); mSelectedTool.handleToolSelection(true, oldActive); } /** * @return the instance of a known tool * @param pID id (name) of the tool **/ /* public VueTool getTool(String pID) { for (VueTool t : mAllTools) if (t.getID().equals(pID)) return t; tufts.Util.printStackTrace("failed to find tool [" + pID + "]"); return null; } */ /** * @return the VueToolPanel **/ public VueToolPanel getToolbar() { return mToolPanel; } /* * addToolSelectionListener * Adds a VueToolSelectionListener to receive notification when * a tool is activated. * @param VueToolSelectionListener the listener object * public void addToolSelectionListener( VueToolSelectionListener pListener) { mToolSelectionListeners.add( pListener); } public void removeToolSelectionListener( VueToolSelectionListener pListener) { mToolSelectionListeners.remove( pListener ); } */ // protected void handleToolSelection(VueTool pTool) // { // if (DEBUG.TOOL) out("handleToolSelection " + pTool); // VueTool rootTool = pTool; // // TODO: need to seriously refactor this code: the tools shouldn't be self activating // // -- e.g., their installing their cursor even if the tool change is denied, so // // we're going to allow the tool selection for now... // if (mSelectedTool != null && !mSelectedTool.permitsToolChange()) { // Log.warn("tool change not permitted to: " + pTool + "; active tool denies: " + mSelectedTool); // //return; // } // if (pTool.getParentTool() != null && pTool.getClass() == tufts.vue.VueSimpleTool.class) // rootTool = pTool.getParentTool(); // // String selectionID = pTool.getSelectionID(); // /* // // check to see if it is the same selection... // if (mCurSelectionID.equals(selectionID)) { // if (DEBUG.TOOL) out("same selection: noop"); // return; // } // if (rootTool == mSelectedTool) { // if (DEBUG.TOOL) out("same root tool: noop"); // return; // } // */ // // allow re-selection of existing tool: handy for use as "apply" to a current selection // // if the changing the active sub-tool can have an effect on selected objects // //mCurSelectionID = selectionID; // // update contextual panel // //updateContextualToolPanel(); // /** OLD CODE // JPanel contextualPanel = rootTool.getContextualPanel(); // if( contextualPanel == null) { // contextualPanel = getContextualToolForSelection(); // } // getToolbar().setContextualToolPanel( contextualPanel ); // END OLD CODE ****/ // final VueTool oldActive = mSelectedTool; // mSelectedTool = rootTool; // // notify listeners // VUE.setActive(VueTool.class, this, rootTool); // if (oldActive != null && oldActive != mSelectedTool) // oldActive.handleToolSelection(false, mSelectedTool); // mSelectedTool.handleToolSelection(true, oldActive); // /* // int size = mToolSelectionListeners.size(); // for (int i = 0; i < size; i++) { // VueToolSelectionListener listener = (VueToolSelectionListener) mToolSelectionListeners.get(i); // if (DEBUG.TOOL) System.out.println("\tVueToolbarController -> toolSelected " + listener); // listener.toolSelected(rootTool); // } // */ // } /* * This method checks to see if the current tool has * a contextual tool. If so, it uses that panel. If * not it uses the selection to get a panel. ** private void updateContextualToolPanel() { VueTool tool = getSelectedTool(); if (tool.getParentTool() != null) tool = tool.getParentTool(); JPanel panel = tool.getContextualPanel(); if (panel == null) panel = getContextualPanelForSelection(); if (panel != null) initContextualPanelFromSelection(panel); // getToolbar().setContextualToolPanel(panel); } */ public JPanel getSuggestedContextualPanel() { return null; } /* * @return the suggest tool panel to use based on the current state * of the controller (selected tool) and the map selection. ** // todo: the tool itself should be able to specify this -- at least for single selection public JPanel getSuggestedContextualPanel() { JPanel panel = null; VueToolbarController controller = VueToolbarController.getController(); LWSelection selection = VUE.getSelection(); VueTool tool = null; if( (!selection.isEmpty() ) && ( selection.allOfSameType()) ) { LWComponent c = (LWComponent) selection.get(0); if (c instanceof LWNode) { if ( false && ((LWNode)c).isTextNode()) tool = controller.getTool("textTool"); else tool = controller.getTool("nodeTool" ); } else if (c instanceof LWLink) { tool = controller.getTool("linkTool"); } else if (c instanceof LWImage) { tool = controller.getTool("imageTool"); } else tool = controller.getTool("nodeTool" ); // make node tool the default for now } if (tool != null) panel = tool.getContextualPanel(); if (DEBUG.SELECTION) System.out.println("getSuggestedContextualPanel returning " + panel); return panel; } private JPanel getContextualPanelForSelection() { JPanel panel = null; LWSelection selection = VUE.getSelection(); // TODO BUG: LWSelection.allOfSameType doesn't distinguish between text nodes & regular nodes! // if we end up keeping text nodes around, go make them a real subclass if (selection.size() > 0 && selection.allOfSameType()) { LWComponent c = selection.first(); if (c instanceof LWNode) { if ( false && ((LWNode) c).isTextNode()) panel = TextTool.getTextToolPanel(); else panel = NodeTool.getNodeToolPanel(); } else if (c instanceof LWLink) panel = LinkTool.getLinkToolPanel(); else panel = NodeTool.getNodeToolPanel(); // the generic default for now } else { panel = getLWCToolPanel(); //panel = null; } if (DEBUG.SELECTION) System.out.println("getContextualPanelForSelection returning " + panel); return panel; } private void initContextualPanelFromSelection(JPanel panel) { /* LWSelection selection = VUE.getSelection(); LWComponent c = null; if (selection != null && selection.size() > 0) { // TODO: something more sophisticated than just loading // all the props from the first item in the selection and // disregarding all the others. c = selection.first(); } else return; * // Maybe better: interested tool panels should listen to selection themselves // The only value we get from this right now is that only the active tool panel // will get loadValues called, letting inactive ones keep their state, which is // handy for some cases, but as long as the tool could know if it's active or not, // we'd have all the functionality we need, and in a much simpler & more effective // manner. if (panel instanceof LWCToolPanel) ((LWCToolPanel)panel).loadSelection(VUE.getSelection()); else { if (DEBUG.TOOL) System.out.println(this + " IGNORING initContextualPanelFromSelection on non LWCToolPanel " + panel); } } */ /** * The implemenation of the LWSelection.Listener * This method updates toolbars based on the new selection **/ private LWComponent singleSelection = null; public void selectionChanged(LWSelection s) { if (s.size() == 1) { if (singleSelection != null) singleSelection.removeLWCListener(this); singleSelection = s.first(); //loadValues(singleSelection); singleSelection.addLWCListener(this); } else if (s.size() > 1) { // todo: if we are to populate the tool bar properties when // there's a multiple selection, what do we use? //loadValues(s.first()); } if (s.size() != 1 && singleSelection != null) { singleSelection.removeLWCListener(this); singleSelection = null; } //updateContextualToolPanel(); if (singleSelection != null) loadToolValue(null, singleSelection); } public void LWCChanged(LWCEvent e) { loadToolValue(e.key, e.component); } /** * Load the value from src, indicated by propertyKey, into any of our tool's that are LWPropertyProducer's. * If propertyKey is null, all producers will attempt to load what there interested in, even if * the object doesn't support the property (in which case the value will be null and should be ignored). */ // todo: cache the property producers private void loadToolValue(Object propertyKey, LWComponent src) { if (src == null) return; for (VueTool tool : mVueTools) { if (tool instanceof LWEditor) { LWEditor editor = (LWEditor) tool; if (propertyKey == null) propertyKey = editor.getPropertyKey(); else if (propertyKey != editor.getPropertyKey()) continue; if (src.supportsProperty(propertyKey)) { Object value = src.getPropertyValue(editor.getPropertyKey()); if (DEBUG.TOOL) out("loadToolValue: loading producer " + editor + " key=" + editor.getPropertyKey() + " with val=" + value + " from " + src); editor.displayValue(value); } } } } private void out(String s) { Log.debug(String.format("[%s] %s", mSelectedTool, s)); } @Override public String toString() { return "VueToolbarController[" + mSelectedTool + "]"; } private void debug(String pMsg) { if( sDebug ) System.out.println( pMsg); } private static boolean sDebug = false; public static void main(String[] args) { System.out.println("VueToolbarController:main"); DEBUG.Enabled = true; //DEBUG.INIT = true; DEBUG.TOOL = true; VUE.init(args); tufts.Util.displayComponent(getController().mToolPanel); /* //FontEditorPanel.sFontNames = new String[] { "Lucida Sans Typewriter", "Courier", "Arial" }; // so doesn't bother to load system fonts VueToolbarController controller = getController(); //JComponent comp = controller.createDefaultToolbar(); VueToolPanel comp = controller.mToolPanel; comp.setContextualToolPanel(new NodeToolPanel()); JFrame frame = new JFrame(comp.getClass().getName()); comp.setSize(comp.getPreferredSize()); frame.setContentPane(comp); frame.pack(); frame.validate(); VueUtil.centerOnScreen(frame); frame.show(); */ } }