/* Copyright (C) 2003-2011 JabRef contributors.
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 net.sf.jabref.groups;
import javax.swing.undo.AbstractUndoableEdit;
import net.sf.jabref.Globals;
class UndoableAddOrRemoveGroup extends AbstractUndoableEdit {
/** The root of the global groups tree */
private final GroupTreeNode m_groupsRootHandle;
/** The subtree that was added or removed */
private final GroupTreeNode m_subtreeBackup;
/**
* In case of removing a node but keeping all of its children, the number of
* children has to be stored.
*/
private final int m_subtreeRootChildCount;
/** The path to the edited subtree's root node */
private final int[] m_pathToNode;
/**
* The type of the editing (ADD_NODE, REMOVE_NODE_KEEP_CHILDREN,
* REMOVE_NODE_AND_CHILDREN)
*/
private final int m_editType;
private final GroupSelector m_groupSelector;
private boolean m_revalidate = true;
/** Adding of a single node (group). */
public static final int ADD_NODE = 0;
/** Removal of a single node. Children, if any, are kept. */
public static final int REMOVE_NODE_KEEP_CHILDREN = 1;
/** Removal of a node and all of its children. */
public static final int REMOVE_NODE_AND_CHILDREN = 2;
/**
* Creates an object that can undo/redo an edit event.
*
* @param groupsRoot
* The global groups root.
* @param editType
* The type of editing (ADD_NODE, REMOVE_NODE_KEEP_CHILDREN,
* REMOVE_NODE_AND_CHILDREN)
* @param editedNode
* The edited node (which was added or will be removed). The node
* must be a descendant of node <b>groupsRoot</b>! This means
* that, in case of adding, you first have to add it to the tree,
* then call this constructor. When removing, you first have to
* call this constructor, then remove the node.
*/
public UndoableAddOrRemoveGroup(GroupSelector gs, GroupTreeNode groupsRoot,
GroupTreeNode editedNode, int editType) {
m_groupSelector = gs;
m_groupsRootHandle = groupsRoot;
m_editType = editType;
m_subtreeRootChildCount = editedNode.getChildCount();
// storing a backup of the whole subtree is not required when children
// are kept
m_subtreeBackup = editType != REMOVE_NODE_KEEP_CHILDREN ? editedNode
.deepCopy() : new GroupTreeNode(editedNode.getGroup().deepCopy());
// remember path to edited node. this cannot be stored as a reference,
// because the reference itself might change. the method below is more
// robust.
m_pathToNode = editedNode.getIndexedPath();
}
public String getUndoPresentationName() {
return Globals.lang("Undo") + ": " + getName();
}
public String getName() {
switch (m_editType) {
case ADD_NODE:
return Globals.lang("add group");
case REMOVE_NODE_KEEP_CHILDREN:
return Globals.lang("remove group (keep subgroups)");
case REMOVE_NODE_AND_CHILDREN:
return Globals.lang("remove group and subgroups");
}
return "? (" + Globals.lang("unknown edit") + ")";
}
public String getRedoPresentationName() {
return Globals.lang("Redo") + ": " + getName();
}
public void undo() {
super.undo();
doOperation(true);
}
public void redo() {
super.redo();
doOperation(false);
}
private void doOperation(boolean undo) {
GroupTreeNode cursor = m_groupsRootHandle;
final int childIndex = m_pathToNode[m_pathToNode.length - 1];
// traverse path up to butlast element
for (int i = 0; i < m_pathToNode.length - 1; ++i)
cursor = (GroupTreeNode) cursor.getChildAt(m_pathToNode[i]);
if (undo) {
switch (m_editType) {
case ADD_NODE:
cursor.remove(childIndex);
break;
case REMOVE_NODE_KEEP_CHILDREN:
// move all children to newNode, then add newNode
GroupTreeNode newNode = m_subtreeBackup.deepCopy();
for (int i = childIndex; i < childIndex
+ m_subtreeRootChildCount; ++i) {
newNode.add((GroupTreeNode) cursor.getChildAt(childIndex));
}
cursor.insert(newNode, childIndex);
break;
case REMOVE_NODE_AND_CHILDREN:
cursor.insert(m_subtreeBackup.deepCopy(), childIndex);
break;
}
} else { // redo
switch (m_editType) {
case ADD_NODE:
cursor.insert(m_subtreeBackup.deepCopy(), childIndex);
break;
case REMOVE_NODE_KEEP_CHILDREN:
// remove node, then insert all children
GroupTreeNode removedNode = (GroupTreeNode) cursor
.getChildAt(childIndex);
cursor.remove(childIndex);
while (removedNode.getChildCount() > 0)
cursor.insert((GroupTreeNode) removedNode.getFirstChild(),
childIndex);
break;
case REMOVE_NODE_AND_CHILDREN:
cursor.remove(childIndex);
break;
}
}
if (m_revalidate)
m_groupSelector.revalidateGroups();
}
/**
* Call this method to decide if the group list should be immediately
* revalidated by this operation. Default is true.
*
* @param val
* a <code>boolean</code> value
*/
public void setRevalidate(boolean val) {
m_revalidate = val;
}
}