package org.jabref.gui.groups;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.Optional;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.jabref.Globals;
import org.jabref.gui.BasePanel;
import org.jabref.gui.actions.BaseAction;
import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.groups.GroupTreeNode;
import com.jgoodies.forms.builder.ButtonBarBuilder;
/**
* Created with IntelliJ IDEA.
* User: alver
* Date: 1/22/13
* Time: 6:24 PM
* To change this template use File | Settings | File Templates.
*/
public class GroupAddRemoveDialog implements BaseAction {
private final BasePanel panel;
private final boolean add;
private final boolean move;
private List<BibEntry> selection;
private JTree tree;
public GroupAddRemoveDialog(BasePanel panel, boolean add, boolean move) {
this.panel = panel;
this.add = add;
this.move = move;
}
@Override
public void action() throws Exception {
Optional<GroupTreeNode> groups = panel.getBibDatabaseContext().getMetaData().getGroups();
if (!groups.isPresent()) {
return;
}
selection = panel.getSelectedEntries();
final JDialog diag = new JDialog(panel.frame(),
(add ? (move ? Localization.lang("Move to group") : Localization.lang("Add to group")) : Localization
.lang("Remove from group")),
true);
JButton ok = new JButton(Localization.lang("OK"));
JButton cancel = new JButton(Localization.lang("Cancel"));
tree = new JTree(new GroupTreeNodeViewModel(groups.get()));
tree.setCellRenderer(new AddRemoveGroupTreeCellRenderer());
tree.setVisibleRowCount(22);
// tree.setPreferredSize(new Dimension(200, tree.getPreferredSize().height));
// The scrollbar appears when the preferred size of a component is greater than the size of the viewport. If one hard coded the preferred size, it will never change according to the expansion/collapse. Thus the scrollbar cannot appear accordingly.
//tree.setSelectionModel(new VetoableTreeSelectionModel());
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.addTreeSelectionListener(e -> {
GroupTreeNodeViewModel node = (GroupTreeNodeViewModel) e.getNewLeadSelectionPath().getLastPathComponent();
ok.setEnabled(checkGroupEnable(node));
});
//STA add expand and collapse all buttons
JButton jbExpandAll = new JButton("Expand All");
jbExpandAll.addActionListener(e -> expandAll(tree, true));
JButton jbCollapseAll = new JButton("Collapse All");
jbCollapseAll.addActionListener(e -> expandAll(tree, false));
//END add expand and collapse all buttons
ButtonBarBuilder bb = new ButtonBarBuilder();
bb.addGlue();
bb.addButton(ok);
bb.addButton(cancel);
bb.addButton(jbExpandAll);
bb.addButton(jbCollapseAll);
bb.addGlue();
bb.getPanel().setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
ok.addActionListener(actionEvent -> {
if (doAddOrRemove()) {
diag.dispose();
}
});
cancel.addActionListener(actionEvent -> diag.dispose());
ok.setEnabled(false);
JScrollPane sp = new JScrollPane(tree);
// Key bindings:
ActionMap am = sp.getActionMap();
InputMap im = sp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(Globals.getKeyPrefs().getKey(KeyBinding.CLOSE_DIALOG), "close");
am.put("close", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
diag.dispose();
}
});
diag.getContentPane().add(sp, BorderLayout.CENTER);
diag.getContentPane().add(bb.getPanel(), BorderLayout.SOUTH);
diag.pack();
diag.setLocationRelativeTo(panel.frame());
diag.setVisible(true);
}
// If "expand" is true, all nodes in the tree area expanded
// otherwise all nodes in the tree are collapsed:
private void expandAll(final JTree subtree, final boolean expand) {
SwingUtilities.invokeLater(() -> {
TreeNode root = ((TreeNode) subtree.getModel().getRoot());
// walk through the tree, beginning at the root:
expandAll(subtree, new TreePath(((DefaultTreeModel) subtree.getModel()).getPathToRoot(root)), expand);
tree.requestFocusInWindow();
});
}
private void expandAll(final JTree subtree, final TreePath parent, final boolean expand) {
// walk through the children:
TreeNode node = (TreeNode) parent.getLastPathComponent();
int numChildren = node.getChildCount();
if (numChildren > 0) {
for (int i = 0; i < numChildren; i++) {
TreeNode child = node.getChildAt(i);
TreePath path = parent.pathByAddingChild(child);
expandAll(subtree, path, expand);
}
}
// "expand" / "collapse" occurs from bottom to top:
if (expand) {
tree.expandPath(parent);
} else {
if (node.getParent() != null) {
tree.collapsePath(parent);
}
}
}
private boolean doAddOrRemove() {
TreePath path = tree.getSelectionPath();
if (path == null) {
return false;
} else {
GroupTreeNodeViewModel node = (GroupTreeNodeViewModel) path.getLastPathComponent();
if (checkGroupEnable(node)) {
if (add) {
node.addEntriesToGroup(Globals.stateManager.getSelectedEntries());
} else {
node.removeEntriesFromGroup(Globals.stateManager.getSelectedEntries());
}
return true;
} else {
return false;
}
}
}
/**
* Check if we can perform the action for this group. Determines whether
* the group should be shown in an enabled state, and if selecting it should
* leave the Ok button enabled.
* @param node The group to check
* @return true if this dialog's action can be performed on the group
*/
private boolean checkGroupEnable(GroupTreeNodeViewModel node) {
return (add ? node.canAddEntries(selection) : node.canRemoveEntries(selection));
}
class AddRemoveGroupTreeCellRenderer extends GroupTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
GroupTreeNodeViewModel node = (GroupTreeNodeViewModel) value;
if (checkGroupEnable(node)) {
c.setForeground(Color.black);
} else {
c.setForeground(Color.gray);
}
return c;
}
}
}