/* 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; } }