blob: 9a9f1bcc17efe889a20c9ffceb94e461beeed708 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-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 com.intellij.tools;
import com.intellij.CommonBundle;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.ui.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.PlatformIcons;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import java.awt.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseToolsPanel<T extends Tool> extends JPanel {
enum Direction {
UP {
@Override
public boolean isAvailable(final int index, final int childCount) {
return index != 0;
}
@Override
public int newIndex(final int index) {
return index - 1;
}
},
DOWN {
@Override
public boolean isAvailable(final int index, final int childCount) {
return index < childCount - 1;
}
@Override
public int newIndex(final int index) {
return index + 1;
}
};
public abstract boolean isAvailable(final int index, final int childCount);
public abstract int newIndex(final int index);
}
private final CheckboxTree myTree;
private final AnActionButton myAddButton;
private final AnActionButton myCopyButton;
private final AnActionButton myEditButton;
private final AnActionButton myMoveUpButton;
private final AnActionButton myMoveDownButton;
private final AnActionButton myRemoveButton;
private boolean myIsModified = false;
protected BaseToolsPanel() {
myTree = new CheckboxTree(
new CheckboxTree.CheckboxTreeCellRenderer() {
@Override
public void customizeRenderer(final JTree tree,
final Object value,
final boolean selected,
final boolean expanded,
final boolean leaf,
final int row,
final boolean hasFocus) {
if (!(value instanceof CheckedTreeNode)) return;
Object object = ((CheckedTreeNode)value).getUserObject();
if (object instanceof ToolsGroup) {
final String groupName = ((ToolsGroup)object).getName();
if (groupName != null) {
getTextRenderer().append(groupName, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
else {
getTextRenderer().append("[unnamed group]", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
}
else if (object instanceof Tool) {
getTextRenderer().append(((Tool)object).getName(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
}
},
new CheckedTreeNode(null)) {
@Override
protected void onDoubleClick(final CheckedTreeNode node) {
editSelected();
}
@Override
protected void onNodeStateChanged(final CheckedTreeNode node) {
myIsModified = true;
}
};
myTree.setRootVisible(false);
myTree.getEmptyText().setText(ToolsBundle.message("tools.not.configured"));
myTree.setSelectionModel(new DefaultTreeSelectionModel());
myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
setLayout(new BorderLayout());
add(ToolbarDecorator.createDecorator(myTree).setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
ToolEditorDialog dlg = createToolEditorDialog(ToolsBundle.message("tools.add.title"));
Tool tool = new Tool();
tool.setUseConsole(true);
tool.setFilesSynchronizedAfterRun(true);
tool.setShownInMainMenu(true);
tool.setShownInEditor(true);
tool.setShownInProjectViews(true);
tool.setShownInSearchResultsPopup(true);
tool.setEnabled(true);
dlg.setData(tool, getGroups());
dlg.show();
if (dlg.isOK()) {
insertNewTool(dlg.getData(), true);
}
myTree.requestFocus();
}
}).setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
removeSelected();
}
}).setEditAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
editSelected();
myTree.requestFocus();
}
}).setMoveUpAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
moveNode(Direction.UP);
myIsModified = true;
}
}).setMoveDownAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
moveNode(Direction.DOWN);
myIsModified = true;
}
}).addExtraAction(myCopyButton = new AnActionButton(ToolsBundle.message("tools.copy.button"), PlatformIcons.COPY_ICON) {
@Override
public void actionPerformed(AnActionEvent e) {
Tool originalTool = getSelectedTool();
if (originalTool != null) {
ToolEditorDialog dlg = createToolEditorDialog(ToolsBundle.message("tools.copy.title"));
Tool toolCopy = new Tool();
toolCopy.copyFrom(originalTool);
dlg.setData(toolCopy, getGroups());
dlg.show();
if (dlg.isOK()) {
insertNewTool(dlg.getData(), true);
}
myTree.requestFocus();
}
}
}).createPanel(), BorderLayout.CENTER);
myAddButton = ToolbarDecorator.findAddButton(this);
myEditButton = ToolbarDecorator.findEditButton(this);
myRemoveButton = ToolbarDecorator.findRemoveButton(this);
myMoveUpButton = ToolbarDecorator.findUpButton(this);
myMoveDownButton = ToolbarDecorator.findDownButton(this);
//TODO check edit and delete
myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
update();
}
});
setMinimumSize(myTree.getEmptyText().getPreferredSize());
}
public void reset() {
List<ToolsGroup<T>> groups = getToolsGroups();
for (ToolsGroup group : groups) {
insertNewGroup((ToolsGroup)group.copy());
}
if ((getTreeRoot()).getChildCount() > 0) {
myTree.setSelectionInterval(0, 0);
}
else {
myTree.getSelectionModel().clearSelection();
}
(getModel()).nodeStructureChanged(null);
TreeUtil.expand(myTree, 5);
myIsModified = false;
update();
}
protected abstract BaseToolManager<T> getToolManager();
protected List<ToolsGroup<T>> getToolsGroups() {
return getToolManager().getGroups();
}
private CheckedTreeNode insertNewGroup(final ToolsGroup<Tool> groupCopy) {
CheckedTreeNode root = getTreeRoot();
CheckedTreeNode groupNode = new CheckedTreeNode(groupCopy);
root.add(groupNode);
for (Tool tool : groupCopy.getElements()) {
insertNewTool(groupNode, tool);
}
return groupNode;
}
private CheckedTreeNode insertNewTool(final CheckedTreeNode groupNode, final Tool toolCopy) {
CheckedTreeNode toolNode = new CheckedTreeNode(toolCopy);
toolNode.setChecked(toolCopy.isEnabled());
((ToolsGroup)groupNode.getUserObject()).addElement(toolCopy);
groupNode.add(toolNode);
nodeWasInserted(toolNode);
return toolNode;
}
private CheckedTreeNode getTreeRoot() {
return (CheckedTreeNode)myTree.getModel().getRoot();
}
public void apply() throws IOException {
// unregister removed tools
BaseToolManager<T> toolManager = getToolManager();
toolManager.setTools(getGroupList());
myIsModified = false;
}
private ToolsGroup[] getGroupList() {
ArrayList<ToolsGroup> result = new ArrayList<ToolsGroup>();
MutableTreeNode root = (MutableTreeNode)myTree.getModel().getRoot();
for (int i = 0; i < root.getChildCount(); i++) {
final CheckedTreeNode node = (CheckedTreeNode)root.getChildAt(i);
for (int j = 0; j < node.getChildCount(); j++) {
final CheckedTreeNode toolNode = (CheckedTreeNode)node.getChildAt(j);
((Tool)toolNode.getUserObject()).setEnabled(toolNode.isChecked());
}
result.add((ToolsGroup)node.getUserObject());
}
return result.toArray(new ToolsGroup[result.size()]);
}
public boolean isModified() {
return myIsModified;
}
private void moveNode(final Direction direction) {
CheckedTreeNode node = getSelectedNode();
if (node != null) {
if (isMovingAvailable(node, direction)) {
moveNode(node, direction);
if (node.getUserObject() instanceof Tool) {
ToolsGroup group = (ToolsGroup)(((CheckedTreeNode)node.getParent()).getUserObject());
Tool tool = (Tool)node.getUserObject();
moveElementInsideGroup(tool, group, direction);
}
TreePath path = new TreePath(node.getPath());
myTree.getSelectionModel().setSelectionPath(path);
myTree.expandPath(path);
myTree.requestFocus();
}
}
}
private void moveElementInsideGroup(final Tool tool, final ToolsGroup group, Direction dir) {
if (dir == Direction.UP) {
group.moveElementUp(tool);
}
else {
group.moveElementDown(tool);
}
}
private void moveNode(final CheckedTreeNode toolNode, Direction dir) {
CheckedTreeNode parentNode = (CheckedTreeNode)toolNode.getParent();
int index = parentNode.getIndex(toolNode);
removeNodeFromParent(toolNode);
int newIndex = dir.newIndex(index);
parentNode.insert(toolNode, newIndex);
getModel().nodesWereInserted(parentNode, new int[]{newIndex});
}
private boolean isMovingAvailable(final CheckedTreeNode toolNode, Direction dir) {
TreeNode parent = toolNode.getParent();
int index = parent.getIndex(toolNode);
return dir.isAvailable(index, parent.getChildCount());
}
private void insertNewTool(final Tool newTool, boolean setSelection) {
CheckedTreeNode groupNode = findGroupNode(newTool.getGroup());
if (groupNode == null) {
groupNode = insertNewGroup(new ToolsGroup(newTool.getGroup()));
nodeWasInserted(groupNode);
}
CheckedTreeNode tool = insertNewTool(groupNode, newTool);
if (setSelection) {
TreePath treePath = new TreePath(tool.getPath());
myTree.expandPath(treePath);
myTree.getSelectionModel().setSelectionPath(treePath);
}
myIsModified = true;
}
private void nodeWasInserted(final CheckedTreeNode groupNode) {
(getModel()).nodesWereInserted(groupNode.getParent(), new int[]{groupNode.getParent().getChildCount() - 1});
}
private DefaultTreeModel getModel() {
return (DefaultTreeModel)myTree.getModel();
}
private CheckedTreeNode findGroupNode(final String group) {
for (int i = 0; i < getTreeRoot().getChildCount(); i++) {
CheckedTreeNode node = (CheckedTreeNode)getTreeRoot().getChildAt(i);
ToolsGroup g = (ToolsGroup)node.getUserObject();
if (Comparing.equal(group, g.getName())) return node;
}
return null;
}
@Nullable
private Tool getSelectedTool() {
CheckedTreeNode node = getSelectedToolNode();
if (node == null) return null;
return node.getUserObject() instanceof Tool ? (Tool)node.getUserObject() : null;
}
@Nullable
private ToolsGroup getSelectedToolGroup() {
CheckedTreeNode node = getSelectedToolNode();
if (node == null) return null;
return node.getUserObject() instanceof ToolsGroup ? (ToolsGroup)node.getUserObject() : null;
}
private void update() {
CheckedTreeNode node = getSelectedToolNode();
Tool selectedTool = getSelectedTool();
ToolsGroup selectedGroup = getSelectedToolGroup();
if (selectedTool != null) {
myAddButton.setEnabled(true);
myCopyButton.setEnabled(true);
myEditButton.setEnabled(true);
myMoveDownButton.setEnabled(isMovingAvailable(node, Direction.DOWN));
myMoveUpButton.setEnabled(isMovingAvailable(node, Direction.UP));
myRemoveButton.setEnabled(true);
}
else if (selectedGroup != null) {
myAddButton.setEnabled(true);
myCopyButton.setEnabled(false);
myEditButton.setEnabled(false);
myMoveDownButton.setEnabled(isMovingAvailable(node, Direction.DOWN));
myMoveUpButton.setEnabled(isMovingAvailable(node, Direction.UP));
myRemoveButton.setEnabled(true);
}
else {
myAddButton.setEnabled(true);
myCopyButton.setEnabled(false);
myEditButton.setEnabled(false);
myMoveDownButton.setEnabled(false);
myMoveUpButton.setEnabled(false);
myRemoveButton.setEnabled(false);
}
(getModel()).nodeStructureChanged(null);
myTree.repaint();
}
private void removeSelected() {
CheckedTreeNode node = getSelectedToolNode();
if (node != null) {
int result = Messages.showYesNoDialog(
this,
ToolsBundle.message("tools.delete.confirmation"),
CommonBundle.getWarningTitle(),
Messages.getWarningIcon()
);
if (result != Messages.YES) {
return;
}
myIsModified = true;
if (node.getUserObject() instanceof Tool) {
Tool tool = (Tool)node.getUserObject();
CheckedTreeNode parentNode = (CheckedTreeNode)node.getParent();
((ToolsGroup)parentNode.getUserObject()).removeElement(tool);
removeNodeFromParent(node);
if (parentNode.getChildCount() == 0) {
removeNodeFromParent(parentNode);
}
}
else if (node.getUserObject() instanceof ToolsGroup) {
removeNodeFromParent(node);
}
update();
myTree.requestFocus();
}
}
private void removeNodeFromParent(DefaultMutableTreeNode node) {
TreeNode parent = node.getParent();
int idx = parent.getIndex(node);
node.removeFromParent();
(getModel()).nodesWereRemoved(parent, new int[]{idx}, new TreeNode[]{node});
}
private void editSelected() {
CheckedTreeNode node = getSelectedToolNode();
if (node != null && node.getUserObject() instanceof Tool) {
Tool selected = (Tool)node.getUserObject();
if (selected != null) {
String oldGroupName = selected.getGroup();
ToolEditorDialog dlg = createToolEditorDialog(ToolsBundle.message("tools.edit.title"));
dlg.setData(selected, getGroups());
dlg.show();
if (dlg.isOK()) {
selected.copyFrom(dlg.getData());
String newGroupName = selected.getGroup();
if (!Comparing.equal(oldGroupName, newGroupName)) {
CheckedTreeNode oldGroupNode = (CheckedTreeNode)node.getParent();
removeNodeFromParent(node);
((ToolsGroup)oldGroupNode.getUserObject()).removeElement(selected);
if (oldGroupNode.getChildCount() == 0) {
removeNodeFromParent(oldGroupNode);
}
insertNewTool(selected, true);
}
else {
(getModel()).nodeChanged(node);
}
myIsModified = true;
update();
}
}
}
}
protected ToolEditorDialog createToolEditorDialog(String title) {
return new ToolEditorDialog(this, title);
}
private CheckedTreeNode getSelectedToolNode() {
TreePath selectionPath = myTree.getSelectionPath();
if (selectionPath != null) {
return (CheckedTreeNode)selectionPath.getLastPathComponent();
}
return null;
}
private CheckedTreeNode getSelectedNode() {
TreePath selectionPath = myTree.getSelectionPath();
if (selectionPath != null) {
return (CheckedTreeNode)selectionPath.getLastPathComponent();
}
return null;
}
private String[] getGroups() {
ArrayList<String> result = new ArrayList<String>();
ToolsGroup[] groups = getGroupList();
for (ToolsGroup group : groups) {
result.add(group.getName());
}
return ArrayUtil.toStringArray(result);
}
void addSelectionListener(TreeSelectionListener listener) {
myTree.getSelectionModel().addTreeSelectionListener(listener);
}
@Nullable
Tool getSingleSelectedTool() {
final TreePath[] selectionPaths = myTree.getSelectionPaths();
if (selectionPaths == null || selectionPaths.length != 1) {
return null;
}
Object toolOrToolGroup = ((CheckedTreeNode)selectionPaths[0].getLastPathComponent()).getUserObject();
if (toolOrToolGroup instanceof Tool) {
return (Tool)toolOrToolGroup;
}
return null;
}
public void selectTool(final String actionId) {
Object root = myTree.getModel().getRoot();
if (root == null || !(root instanceof CheckedTreeNode)) {
return;
}
final List<CheckedTreeNode> nodes = new ArrayList<CheckedTreeNode>();
new Object() {
@SuppressWarnings("unchecked")
public void collect(CheckedTreeNode node) {
if (node.isLeaf()) {
Object userObject = node.getUserObject();
if (userObject instanceof Tool && actionId.equals(((Tool)userObject).getActionId())) {
nodes.add(node);
}
}
else {
for (int i = 0; i < node.getChildCount(); i++) {
final TreeNode child = node.getChildAt(i);
if (child instanceof CheckedTreeNode) {
collect((CheckedTreeNode)child);
}
}
}
}
}.collect((CheckedTreeNode)root);
if (nodes.isEmpty()) {
return;
}
myTree.getSelectionModel().setSelectionPath(new TreePath(nodes.get(0).getPath()));
}
}