blob: 136c08fb24f067aafbcf0de6087d87dbe15f2f60 [file] [log] [blame]
/*
* Copyright 2000-2009 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.ide.ui.customization;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.keymap.impl.ui.Group;
import com.intellij.openapi.util.Pair;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.diff.Diff;
import com.intellij.util.diff.FilesTooBigForDiffException;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
/**
* User: anna
* Date: Mar 30, 2005
*/
public class CustomizationUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.ui.customization.CustomizationUtil");
private CustomizationUtil() {
}
public static ActionGroup correctActionGroup(final ActionGroup group,
final CustomActionsSchema schema,
final String defaultGroupName,
final String rootGroupName) {
if (!schema.isCorrectActionGroup(group, defaultGroupName)){
return group;
}
String text = group.getTemplatePresentation().getText();
final int mnemonic = group.getTemplatePresentation().getMnemonic();
if (text != null) {
for (int i = 0; i < text.length(); i++) {
if (Character.toUpperCase(text.charAt(i)) == mnemonic) {
text = text.replaceFirst(String.valueOf(text.charAt(i)), "_" + text.charAt(i));
break;
}
}
}
return new CustomisedActionGroup(text, group.isPopup(), group, schema, defaultGroupName, rootGroupName);
}
static AnAction [] getReordableChildren(ActionGroup group,
CustomActionsSchema schema,
String defaultGroupName,
String rootGroupName,
AnActionEvent e) {
String text = group.getTemplatePresentation().getText();
ActionManager actionManager = ActionManager.getInstance();
final ArrayList<AnAction> reorderedChildren = new ArrayList<AnAction>();
ContainerUtil.addAll(reorderedChildren, group.getChildren(e));
final List<ActionUrl> actions = schema.getActions();
for (ActionUrl actionUrl : actions) {
if ((actionUrl.getParentGroup().equals(text) ||
actionUrl.getParentGroup().equals(defaultGroupName) ||
actionUrl.getParentGroup().equals(actionManager.getId(group)) && actionUrl.getRootGroup().equals(rootGroupName))) {
AnAction componentAction = actionUrl.getComponentAction();
if (componentAction != null) {
if (actionUrl.getActionType() == ActionUrl.ADDED) {
if (componentAction == group) {
LOG.error("Attempt to add group to itself; group ID=" + actionManager.getId(group));
continue;
}
if (reorderedChildren.size() > actionUrl.getAbsolutePosition()) {
reorderedChildren.add(actionUrl.getAbsolutePosition(), componentAction);
}
else {
reorderedChildren.add(componentAction);
}
}
else if (actionUrl.getActionType() == ActionUrl.DELETED && reorderedChildren.size() > actionUrl.getAbsolutePosition()) {
final AnAction anAction = reorderedChildren.get(actionUrl.getAbsolutePosition());
if (anAction.getTemplatePresentation().getText() == null
? (componentAction.getTemplatePresentation().getText() != null &&
componentAction.getTemplatePresentation().getText().length() > 0)
: !anAction.getTemplatePresentation().getText().equals(componentAction.getTemplatePresentation().getText())) {
continue;
}
reorderedChildren.remove(actionUrl.getAbsolutePosition());
}
}
}
}
for (int i = 0; i < reorderedChildren.size(); i++) {
if (reorderedChildren.get(i) instanceof ActionGroup) {
final ActionGroup groupToCorrect = (ActionGroup)reorderedChildren.get(i);
final AnAction correctedAction = correctActionGroup(groupToCorrect, schema, "", rootGroupName);
reorderedChildren.set(i, correctedAction);
}
}
return reorderedChildren.toArray(new AnAction[reorderedChildren.size()]);
}
public static void optimizeSchema(final JTree tree, final CustomActionsSchema schema) {
//noinspection HardCodedStringLiteral
Group rootGroup = new Group("root", null, null);
DefaultMutableTreeNode root = new DefaultMutableTreeNode(rootGroup);
root.removeAllChildren();
schema.fillActionGroups(root);
final JTree defaultTree = new Tree(new DefaultTreeModel(root));
final ArrayList<ActionUrl> actions = new ArrayList<ActionUrl>();
TreeUtil.traverseDepth((TreeNode)tree.getModel().getRoot(), new TreeUtil.Traverse() {
public boolean accept(Object node) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node;
if (treeNode.isLeaf()) {
return true;
}
final ActionUrl url = getActionUrl(new TreePath(treeNode.getPath()), 0);
url.getGroupPath().add(((Group)treeNode.getUserObject()).getName());
final TreePath treePath = getTreePath(defaultTree, url);
if (treePath != null) {
final DefaultMutableTreeNode visited = (DefaultMutableTreeNode)treePath.getLastPathComponent();
final ActionUrl[] defaultUserObjects = getChildUserObjects(visited, url);
final ActionUrl[] currentUserObjects = getChildUserObjects(treeNode, url);
computeDiff(defaultUserObjects, currentUserObjects, actions);
} else {
//customizations at the new place
url.getGroupPath().remove(url.getParentGroup());
if (actions.contains(url)){
url.getGroupPath().add(((Group)treeNode.getUserObject()).getName());
actions.addAll(schema.getChildActions(url));
}
}
return true;
}
});
schema.setActions(actions);
}
private static void computeDiff(final ActionUrl[] defaultUserObjects,
final ActionUrl[] currentUserObjects,
final ArrayList<ActionUrl> actions) {
Diff.Change change = null;
try {
change = Diff.buildChanges(defaultUserObjects, currentUserObjects);
}
catch (FilesTooBigForDiffException e) {
LOG.info(e);
}
while (change != null) {
for (int i = 0; i < change.deleted; i++) {
final int idx = change.line0 + i;
ActionUrl currentUserObject = defaultUserObjects[idx];
currentUserObject.setActionType(ActionUrl.DELETED);
currentUserObject.setAbsolutePosition(idx);
actions.add(currentUserObject);
}
for (int i = 0; i < change.inserted; i++) {
final int idx = change.line1 + i;
ActionUrl currentUserObject = currentUserObjects[idx];
currentUserObject.setActionType(ActionUrl.ADDED);
currentUserObject.setAbsolutePosition(idx);
actions.add(currentUserObject);
}
change = change.link;
}
}
public static TreePath getPathByUserObjects(JTree tree, TreePath treePath){
List<String> path = new ArrayList<String>();
for (int i = 0; i < treePath.getPath().length; i++) {
Object o = ((DefaultMutableTreeNode)treePath.getPath()[i]).getUserObject();
if (o instanceof Group) {
path.add(((Group)o).getName());
}
}
return getTreePath(0, path, tree.getModel().getRoot(), tree);
}
public static ActionUrl getActionUrl(final TreePath treePath, int actionType) {
ActionUrl url = new ActionUrl();
for (int i = 0; i < treePath.getPath().length - 1; i++) {
Object o = ((DefaultMutableTreeNode)treePath.getPath()[i]).getUserObject();
if (o instanceof Group) {
url.getGroupPath().add(((Group)o).getName());
}
}
final DefaultMutableTreeNode component = ((DefaultMutableTreeNode)treePath.getLastPathComponent());
url.setComponent(component.getUserObject());
DefaultMutableTreeNode node = component;
final TreeNode parent = node.getParent();
url.setAbsolutePosition(parent != null ? parent.getIndex(node) : 0);
url.setActionType(actionType);
return url;
}
public static TreePath getTreePath(JTree tree, ActionUrl url) {
return getTreePath(0, url.getGroupPath(), tree.getModel().getRoot(), tree);
}
@Nullable
private static TreePath getTreePath(final int positionInPath, final List<String> path, final Object root, JTree tree) {
if (!(root instanceof DefaultMutableTreeNode)) return null;
final DefaultMutableTreeNode treeNode = ((DefaultMutableTreeNode)root);
final Object userObject = treeNode.getUserObject();
final String pathElement;
if (path.size() > positionInPath) {
pathElement = path.get(positionInPath);
}
else {
return null;
}
if (pathElement == null) return null;
if (!(userObject instanceof Group)) return null;
if (!pathElement.equals(((Group)userObject).getName())) return null;
TreePath currentPath = new TreePath(treeNode.getPath());
if (positionInPath == path.size() - 1) {
return currentPath;
}
for (int j = 0; j < treeNode.getChildCount(); j++) {
final TreeNode child = treeNode.getChildAt(j);
currentPath = getTreePath(positionInPath + 1, path, child, tree);
if (currentPath != null) {
break;
}
}
return currentPath;
}
private static ActionUrl[] getChildUserObjects(DefaultMutableTreeNode node, ActionUrl parent) {
ArrayList<ActionUrl> result = new ArrayList<ActionUrl>();
ArrayList<String> groupPath = new ArrayList<String>();
groupPath.addAll(parent.getGroupPath());
for (int i = 0; i < node.getChildCount(); i++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode)node.getChildAt(i);
ActionUrl url = new ActionUrl();
url.setGroupPath(groupPath);
final Object userObject = child.getUserObject();
url.setComponent(userObject instanceof Pair ? ((Pair)userObject).first : userObject);
result.add(url);
}
return result.toArray(new ActionUrl[result.size()]);
}
public static MouseListener installPopupHandler(JComponent component, @NotNull final String groupId, final String place) {
if (ApplicationManager.getApplication() == null) return new MouseAdapter(){};
PopupHandler popupHandler = new PopupHandler() {
public void invokePopup(Component comp, int x, int y) {
ActionGroup group = (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(groupId);
final ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(place, group);
popupMenu.getComponent().show(comp, x, y);
}
};
component.addMouseListener(popupHandler);
return popupHandler;
}
}