/*FreeMind - A Program for creating and viewing Mindmaps
*Copyright (C) 2000-2004 Joerg Mueller, Daniel Polansky, Christian Foltin and others.
*
*See COPYING for Details
*
*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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Created on 26.07.2004
*/
package freemind.modes.mindmapmode.actions;
import java.awt.event.ActionEvent;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import freemind.controller.MenuItemEnabledListener;
import freemind.controller.MenuItemSelectedListener;
import freemind.controller.actions.generated.instance.CompoundAction;
import freemind.controller.actions.generated.instance.HookNodeAction;
import freemind.controller.actions.generated.instance.NodeChildParameter;
import freemind.controller.actions.generated.instance.NodeListMember;
import freemind.controller.actions.generated.instance.XmlAction;
import freemind.extensions.HookFactory;
import freemind.extensions.HookInstanciationMethod;
import freemind.extensions.NodeHook;
import freemind.extensions.PermanentNodeHook;
import freemind.extensions.PermanentNodeHookAdapter;
import freemind.main.Tools;
import freemind.main.XMLElement;
import freemind.modes.MindMapNode;
import freemind.modes.mindmapmode.MindMapController;
import freemind.modes.mindmapmode.actions.xml.ActionPair;
import freemind.modes.mindmapmode.actions.xml.ActorXml;
public class NodeHookAction extends FreemindAction implements HookAction,
ActorXml, MenuItemEnabledListener, MenuItemSelectedListener {
String _hookName;
MindMapController mMindMapController;
public MindMapController getController() {
return mMindMapController;
}
private static Logger logger;
public NodeHookAction(String hookName, MindMapController controller) {
super(hookName, (ImageIcon) null, null);
this._hookName = hookName;
this.mMindMapController = controller;
if (logger == null)
logger = controller.getFrame().getLogger(this.getClass().getName());
controller.getActionFactory().registerActor(this, getDoActionClass());
}
public void actionPerformed(ActionEvent arg0) {
// check, which method of invocation:
//
mMindMapController.getFrame().setWaitingCursor(true);
invoke(mMindMapController.getSelected(),
mMindMapController.getSelecteds());
mMindMapController.getFrame().setWaitingCursor(false);
}
public void addHook(MindMapNode focussed, List selecteds, String hookName, Properties pHookProperties) {
HookNodeAction doAction = createHookNodeAction(focussed, selecteds,
hookName, pHookProperties);
XmlAction undoAction = null;
// this is the non operation:
undoAction = new CompoundAction();
if (getInstanciationMethod(hookName).isPermanent()) {
// double application = remove.
undoAction = createHookNodeUndoAction(focussed, selecteds, hookName);
}
if (getInstanciationMethod(hookName).isUndoable()) {
getController().doTransaction(
(String) getValue(NAME),
new ActionPair(doAction, undoAction));
} else {
// direct invocation without undo and such stuff.
invoke(focussed, selecteds, hookName, null);
}
}
private XmlAction createHookNodeUndoAction(MindMapNode focussed,
List selecteds, String hookName) {
CompoundAction undoAction = new CompoundAction();
HookNodeAction hookNodeAction = createHookNodeAction(focussed,
selecteds, hookName, null);
undoAction.addChoice(hookNodeAction);
HookInstanciationMethod instMethod = getInstanciationMethod(hookName);
// get destination nodes
Collection destinationNodes = instMethod.getDestinationNodes(
mMindMapController, focussed, selecteds);
MindMapNode adaptedFocussedNode = instMethod.getCenterNode(
mMindMapController, focussed, selecteds);
// test if hook already present
if (instMethod.isAlreadyPresent(mMindMapController, hookName,
adaptedFocussedNode)) {
// remove the hook:
for (Iterator i = destinationNodes.iterator(); i.hasNext();) {
MindMapNode currentDestinationNode = (MindMapNode) i.next();
// find the hook in the current node, if present:
for (Iterator j = currentDestinationNode.getActivatedHooks()
.iterator(); j.hasNext();) {
PermanentNodeHook hook = (PermanentNodeHook) j.next();
if (hook.getName().equals(hookName)) {
XMLElement child = new XMLElement();
hook.save(child);
if (child.countChildren() == 1) {
XMLElement parameters = (XMLElement) child
.getChildren().firstElement();
if (Tools.safeEquals(parameters.getName(),
PermanentNodeHookAdapter.PARAMETERS)) {
// standard save mechanism
for (Iterator it = parameters
.enumerateAttributeNames(); it
.hasNext();) {
String name = (String) it.next();
NodeChildParameter nodeHookChild = new NodeChildParameter();
nodeHookChild.setKey(name);
nodeHookChild.setValue(parameters
.getStringAttribute(name));
hookNodeAction
.addNodeChildParameter(nodeHookChild);
}
} else {
logger.warning("Unusual save mechanism, implement me.");
}
} else {
logger.warning("Unusual save mechanism, implement me.");
}
/*
* fc, 30.7.2004: we have to break. otherwise the
* collection is modified at two points (i.e., the
* collection is not valid anymore after removing one
* element). But this is no problem, as there exist only
* "once" plugins currently.
*/
break;
}
}
}
}
return undoAction;
}
public void invoke(MindMapNode focussed, List selecteds) {
addHook(focussed, selecteds, _hookName, null);
}
private void invoke(MindMapNode focussed, List selecteds, String hookName,
XMLElement pXmlParent) {
logger.finest("invoke(selecteds) called.");
HookInstanciationMethod instMethod = getInstanciationMethod(hookName);
// get destination nodes
Collection destinationNodes = instMethod.getDestinationNodes(
mMindMapController, focussed, selecteds);
MindMapNode adaptedFocussedNode = instMethod.getCenterNode(
mMindMapController, focussed, selecteds);
// test if hook already present
if (instMethod.isAlreadyPresent(mMindMapController, hookName,
adaptedFocussedNode)) {
// remove the hook:
for (Iterator i = destinationNodes.iterator(); i.hasNext();) {
MindMapNode currentDestinationNode = (MindMapNode) i.next();
// find the hook ini the current node, if present:
for (Iterator j = currentDestinationNode.getActivatedHooks()
.iterator(); j.hasNext();) {
PermanentNodeHook hook = (PermanentNodeHook) j.next();
if (hook.getName().equals(hookName)) {
currentDestinationNode.removeHook(hook);
mMindMapController.nodeChanged(currentDestinationNode);
/*
* fc, 30.7.2004: we have to break. otherwise the
* collection is modified at two points (i.e., the
* collection is not valid anymore after removing one
* element). But this is no problem, as there exist only
* "once" plugins currently.
*/
break;
}
}
}
} else {
// add the hook
for (Iterator it = destinationNodes.iterator(); it.hasNext();) {
MindMapNode currentDestinationNode = (MindMapNode) it.next();
NodeHook hook = mMindMapController.createNodeHook(hookName,
currentDestinationNode, mMindMapController.getMap());
logger.finest("created hook " + hookName);
// set parameters, if present
if (pXmlParent != null && hook instanceof PermanentNodeHook) {
((PermanentNodeHook) hook).loadFrom(pXmlParent);
}
// call invoke.
currentDestinationNode.invokeHook(hook);
if (hook instanceof PermanentNodeHook) {
PermanentNodeHook permHook = (PermanentNodeHook) hook;
logger.finest("This is a permanent hook " + hookName);
// the focused receives the focus:
if (currentDestinationNode == adaptedFocussedNode) {
permHook.onFocusNode(mMindMapController
.getNodeView(currentDestinationNode));
}
// using this method, the map is dirty now. This is
// important to
// guarantee, that the hooks are saved.
mMindMapController.nodeChanged(currentDestinationNode);
}
}
finishInvocation(focussed, selecteds, adaptedFocussedNode,
destinationNodes);
}
}
/**
* @param focussed
* The real focussed node
* @param selecteds
* The list of selected nodes
* @param adaptedFocussedNode
* The calculated focussed node (if the hook specifies, that the
* hook should apply to root, then this is the root node).
* @param destinationNodes
* The calculated list of selected nodes (see last)
*/
private void finishInvocation(MindMapNode focussed, List selecteds,
MindMapNode adaptedFocussedNode, Collection destinationNodes) {
// restore selection only, if nothing selected.
if (getController().getView().getSelecteds().size() == 0) {
// select all destination nodes:
getController().select(focussed, selecteds);
}
}
/**
*/
private HookInstanciationMethod getInstanciationMethod(String hookName) {
HookFactory factory = getHookFactory();
// determine instanciation method
HookInstanciationMethod instMethod = factory
.getInstanciationMethod(hookName);
return instMethod;
}
/**
*/
private HookFactory getHookFactory() {
HookFactory factory = mMindMapController.getHookFactory();
return factory;
}
/*
* (non-Javadoc)
*
* @see
* freemind.controller.MenuItemEnabledListener#isEnabled(javax.swing.JMenuItem
* , javax.swing.Action)
*/
public boolean isEnabled(JMenuItem item, Action action) {
if (mMindMapController.getView() == null) {
return false;
}
HookFactory factory = getHookFactory();
Object baseClass = factory.getPluginBaseClass(_hookName);
if (baseClass != null) {
if (baseClass instanceof MenuItemEnabledListener) {
MenuItemEnabledListener listener = (MenuItemEnabledListener) baseClass;
return listener.isEnabled(item, action);
}
}
return true;
}
public HookNodeAction createHookNodeAction(MindMapNode focussed,
List selecteds, String hookName, Properties pHookProperties) {
HookNodeAction hookNodeAction = new HookNodeAction();
hookNodeAction.setNode(focussed.getObjectId(getController()));
hookNodeAction.setHookName(hookName);
// selectedNodes list
for (Iterator i = selecteds.iterator(); i.hasNext();) {
MindMapNode node = (MindMapNode) i.next();
NodeListMember nodeListMember = new NodeListMember();
nodeListMember.setNode(node.getObjectId(getController()));
hookNodeAction.addNodeListMember(nodeListMember);
}
if(pHookProperties != null) {
for (Iterator it = pHookProperties.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
NodeChildParameter nodeChildParameter = new NodeChildParameter();
nodeChildParameter.setKey((String) entry.getKey());
nodeChildParameter.setValue((String) entry.getValue());
hookNodeAction.addNodeChildParameter(nodeChildParameter);
}
}
return hookNodeAction;
}
public void act(XmlAction action) {
if (action instanceof HookNodeAction) {
HookNodeAction hookNodeAction = (HookNodeAction) action;
MindMapNode selected = getController().getNodeFromID(
hookNodeAction.getNode());
Vector selecteds = new Vector();
for (Iterator i = hookNodeAction.getListNodeListMemberList()
.iterator(); i.hasNext();) {
NodeListMember node = (NodeListMember) i.next();
selecteds.add(getController().getNodeFromID(node.getNode()));
}
// reconstruct child-xml:
XMLElement xmlParent = new XMLElement();
xmlParent.setName(hookNodeAction.getHookName());
XMLElement child = new XMLElement();
xmlParent.addChild(child);
child.setName(PermanentNodeHookAdapter.PARAMETERS);
for (Iterator it = hookNodeAction.getListNodeChildParameterList()
.iterator(); it.hasNext();) {
NodeChildParameter childParameter = (NodeChildParameter) it
.next();
child.setAttribute(childParameter.getKey(),
childParameter.getValue());
}
invoke(selected, selecteds, hookNodeAction.getHookName(), xmlParent);
}
}
public Class getDoActionClass() {
return HookNodeAction.class;
}
/**
*/
public String getHookName() {
return _hookName;
}
public boolean isSelected(JMenuItem pCheckItem, Action pAction) {
// test if plugin has its own method:
HookFactory factory = getHookFactory();
Object baseClass = factory.getPluginBaseClass(_hookName);
if (baseClass != null) {
if (baseClass instanceof MenuItemSelectedListener) {
MenuItemSelectedListener listener = (MenuItemSelectedListener) baseClass;
return listener.isSelected(pCheckItem, pAction);
}
}
MindMapNode focussed = mMindMapController.getSelected();
List selecteds = mMindMapController.getSelecteds();
HookInstanciationMethod instMethod = getInstanciationMethod(_hookName);
// get destination nodes
Collection destinationNodes = instMethod.getDestinationNodes(
mMindMapController, focussed, selecteds);
MindMapNode adaptedFocussedNode = instMethod.getCenterNode(
mMindMapController, focussed, selecteds);
// test if hook already present
return instMethod.isAlreadyPresent(mMindMapController, _hookName,
adaptedFocussedNode);
}
public void removeHook(MindMapNode pFocussed, List pSelecteds,
String pHookName) {
HookNodeAction undoAction = createHookNodeAction(pFocussed, pSelecteds,
pHookName, null);
XmlAction doAction = null;
// this is the non operation:
doAction = new CompoundAction();
if (getInstanciationMethod(pHookName).isPermanent()) {
// double application = remove.
doAction = createHookNodeUndoAction(pFocussed, pSelecteds,
pHookName);
}
getController().doTransaction(
(String) getValue(NAME), new ActionPair(undoAction, doAction));
}
}