/* * Copyright 2000-2014 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.ui; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.border.CustomLineBorder; import com.intellij.ui.table.TableView; import com.intellij.util.SmartList; import com.intellij.util.ui.EditableModel; import com.intellij.util.ui.ElementProducer; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.Border; import java.awt.*; import java.util.*; import java.util.List; /** * @author Konstantin Bulenkov * * @see #createDecorator(javax.swing.JList) * @see #createDecorator(javax.swing.JTable) * @see #createDecorator(javax.swing.JTree) */ @SuppressWarnings("UnusedDeclaration") public abstract class ToolbarDecorator implements CommonActionsPanel.ListenerFactory { protected Border myPanelBorder; protected Border myToolbarBorder; protected boolean myAddActionEnabled; protected boolean myEditActionEnabled; protected boolean myRemoveActionEnabled; protected boolean myUpActionEnabled; protected boolean myDownActionEnabled; protected Border myActionsPanelBorder; private final List<AnActionButton> myExtraActions = new SmartList<>(); private ActionToolbarPosition myToolbarPosition; protected AnActionButtonRunnable myAddAction; protected AnActionButtonRunnable myEditAction; protected AnActionButtonRunnable myRemoveAction; protected AnActionButtonRunnable myUpAction; protected AnActionButtonRunnable myDownAction; private String myAddName; private String myEditName; private String myRemoveName; private String myMoveUpName; private String myMoveDownName; private AnActionButtonUpdater myAddActionUpdater = null; private AnActionButtonUpdater myRemoveActionUpdater = null; private AnActionButtonUpdater myEditActionUpdater = null; private AnActionButtonUpdater myMoveUpActionUpdater = null; private AnActionButtonUpdater myMoveDownActionUpdater = null; private Dimension myPreferredSize; private Dimension myMinimumSize; private CommonActionsPanel myActionsPanel; private Comparator<AnActionButton> myButtonComparator; private boolean myAsUsualTopToolbar = false; private Icon myAddIcon; private boolean myForcedDnD = false; protected abstract JComponent getComponent(); protected abstract void updateButtons(); protected void updateExtraElementActions(boolean someElementSelected) { for (AnActionButton action : myExtraActions) { if (action instanceof ElementActionButton) { action.setEnabled(someElementSelected); } } } public final CommonActionsPanel getActionsPanel() { return myActionsPanel; } public ToolbarDecorator initPosition() { setToolbarPosition(SystemInfo.isMac ? ActionToolbarPosition.BOTTOM : ActionToolbarPosition.RIGHT); return this; } public ToolbarDecorator setAsUsualTopToolbar() { myAsUsualTopToolbar = true; setToolbarPosition(ActionToolbarPosition.TOP); return this; } public static ToolbarDecorator createDecorator(@NotNull JTable table) { return new TableToolbarDecorator(table, null).initPosition(); } public static ToolbarDecorator createDecorator(@NotNull JTree tree) { return createDecorator(tree, null); } private static ToolbarDecorator createDecorator(@NotNull JTree tree, @Nullable ElementProducer<?> producer) { return new TreeToolbarDecorator(tree, producer).initPosition(); } public static ToolbarDecorator createDecorator(@NotNull JList list) { return new ListToolbarDecorator(list, null).initPosition(); } public static ToolbarDecorator createDecorator(@NotNull JList list, EditableModel editableModel) { return new ListToolbarDecorator(list, editableModel).initPosition(); } public static <T> ToolbarDecorator createDecorator(@NotNull TableView<T> table, @Nullable ElementProducer<T> producer) { return new TableToolbarDecorator(table, producer).initPosition(); } public ToolbarDecorator disableAddAction() { myAddActionEnabled = false; return this; } public ToolbarDecorator disableRemoveAction() { myRemoveActionEnabled = false; return this; } public ToolbarDecorator disableUpAction() { myUpActionEnabled = false; return this; } public ToolbarDecorator disableUpDownActions() { myUpActionEnabled = false; myDownActionEnabled = false; return this; } public ToolbarDecorator disableDownAction() { myDownActionEnabled = false; return this; } public ToolbarDecorator setPanelBorder(Border border) { myPanelBorder = border; return this; } public ToolbarDecorator setToolbarBorder(Border border) { myActionsPanelBorder = border; return this; } public ToolbarDecorator setButtonComparator(Comparator<AnActionButton> buttonComparator) { myButtonComparator = buttonComparator; return this; } public ToolbarDecorator setButtonComparator(String...actionNames) { final List<String> names = Arrays.asList(actionNames); myButtonComparator = (o1, o2) -> { final String t1 = o1.getTemplatePresentation().getText(); final String t2 = o2.getTemplatePresentation().getText(); if (t1 == null || t2 == null) return 0; final int ind1 = names.indexOf(t1); final int ind2 = names.indexOf(t2); if (ind1 == -1 && ind2 >= 0) return 1; if (ind2 == -1 && ind1 >= 0) return -1; return ind1 - ind2; }; return this; } public ToolbarDecorator setLineBorder(int top, int left, int bottom, int right) { return setToolbarBorder(new CustomLineBorder(top, left, bottom, right)); } public ToolbarDecorator addExtraAction(@NotNull AnActionButton action) { myExtraActions.add(action); return this; } public ToolbarDecorator addExtraActions(AnActionButton... actions) { for (AnActionButton action : actions) { if (action != null) { addExtraAction(action); } } return this; } public ToolbarDecorator setToolbarPosition(ActionToolbarPosition position) { myToolbarPosition = position; myActionsPanelBorder = new CustomLineBorder(myToolbarPosition == ActionToolbarPosition.BOTTOM ? 1 : 0, myToolbarPosition == ActionToolbarPosition.RIGHT ? 1 : 0, myToolbarPosition == ActionToolbarPosition.TOP ? 1 : 0, myToolbarPosition == ActionToolbarPosition.LEFT ? 1 : 0); return this; } public ToolbarDecorator setAddAction(AnActionButtonRunnable action) { myAddActionEnabled = action != null; myAddAction = action; return this; } public ToolbarDecorator setEditAction(AnActionButtonRunnable action) { myEditActionEnabled = action != null; myEditAction = action; return this; } public ToolbarDecorator setRemoveAction(AnActionButtonRunnable action) { myRemoveActionEnabled = action != null; myRemoveAction = action; return this; } public ToolbarDecorator setMoveUpAction(AnActionButtonRunnable action) { myUpActionEnabled = action != null; myUpAction = action; return this; } public ToolbarDecorator setMoveDownAction(AnActionButtonRunnable action) { myDownActionEnabled = action != null; myDownAction = action; return this; } public ToolbarDecorator setAddActionName(String name) { myAddName = name; return this; } public ToolbarDecorator setEditActionName(String name) { myEditName = name; return this; } public ToolbarDecorator setRemoveActionName(String name) { myRemoveName = name; return this; } public ToolbarDecorator setMoveUpActionName(String name) { myMoveUpName = name; return this; } public ToolbarDecorator setMoveDownActionName(String name) { myMoveDownName = name; return this; } public ToolbarDecorator setAddActionUpdater(AnActionButtonUpdater updater) { myAddActionUpdater = updater; return this; } public ToolbarDecorator setRemoveActionUpdater(AnActionButtonUpdater updater) { myRemoveActionUpdater = updater; return this; } public ToolbarDecorator setEditActionUpdater(AnActionButtonUpdater updater) { myEditActionUpdater = updater; return this; } public ToolbarDecorator setMoveUpActionUpdater(AnActionButtonUpdater updater) { myMoveUpActionUpdater = updater; return this; } public ToolbarDecorator setMoveDownActionUpdater(AnActionButtonUpdater updater) { myMoveDownActionUpdater = updater; return this; } public ToolbarDecorator setForcedDnD() { myForcedDnD = true; return this; } public ToolbarDecorator setActionGroup(@NotNull ActionGroup actionGroup) { AnAction[] actions = actionGroup.getChildren(null); for (AnAction action : actions) { if (!(action instanceof Separator)) { addExtraAction(AnActionButton.fromAction(action)); } } return this; } public ToolbarDecorator setPreferredSize(Dimension size) { myPreferredSize = size; return this; } public ToolbarDecorator setMinimumSize(Dimension size) { myMinimumSize = size; return this; } public ToolbarDecorator setVisibleRowCount(int rowCount) { return this;//do nothing by default } public ToolbarDecorator setAddIcon(Icon addIcon) { myAddIcon = addIcon; return this; } /** * @return panel that contains wrapped component (with added scrollpane) and toolbar panel. */ public JPanel createPanel() { final CommonActionsPanel.Buttons[] buttons = getButtons(); final JComponent contextComponent = getComponent(); myActionsPanel = new CommonActionsPanel(this, contextComponent, myToolbarPosition, myExtraActions.toArray(new AnActionButton[myExtraActions.size()]), myButtonComparator, myAddName, myRemoveName, myMoveUpName, myMoveDownName, myEditName, myAddIcon, buttons); final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(contextComponent, true); if (myPreferredSize != null) { scrollPane.setPreferredSize(myPreferredSize); } if (myMinimumSize != null) { scrollPane.setMinimumSize(myMinimumSize); } final JPanel panel = new JPanel(new BorderLayout()) { @Override public void addNotify() { super.addNotify(); updateButtons(); } }; panel.add(scrollPane, BorderLayout.CENTER); panel.add(myActionsPanel, getPlacement(myToolbarPosition)); installUpdaters(); updateButtons(); installDnD(); panel.putClientProperty(ActionToolbar.ACTION_TOOLBAR_PROPERTY_KEY, myActionsPanel.getComponent(0)); Border mainBorder = myPanelBorder != null ? myPanelBorder : IdeBorderFactory.createBorder(SideBorder.ALL); if (myAsUsualTopToolbar) { scrollPane.setBorder(mainBorder); } else { myActionsPanel.setBorder(myActionsPanelBorder); panel.setBorder(mainBorder); } return panel; } private void installUpdaters() { if (myAddActionEnabled && myAddAction != null && myAddActionUpdater != null) { myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.ADD).addCustomUpdater(myAddActionUpdater); } if (myEditActionEnabled && myEditAction != null && myEditActionUpdater != null) { myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.EDIT).addCustomUpdater(myEditActionUpdater); } if (myRemoveActionEnabled && myRemoveAction != null && myRemoveActionUpdater != null) { myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.REMOVE).addCustomUpdater(myRemoveActionUpdater); } if (myUpActionEnabled && myUpAction != null && myMoveUpActionUpdater != null) { myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.UP).addCustomUpdater(myMoveUpActionUpdater); } if (myDownActionEnabled && myDownAction != null && myMoveDownActionUpdater != null) { myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.DOWN).addCustomUpdater(myMoveDownActionUpdater); } } protected void installDnD() { if ((myForcedDnD || (myUpAction != null && myUpActionEnabled && myDownAction != null && myDownActionEnabled)) && !ApplicationManager.getApplication().isHeadlessEnvironment() && isModelEditable()) { installDnDSupport(); } } protected abstract void installDnDSupport(); protected abstract boolean isModelEditable(); @NotNull static Object getPlacement(ActionToolbarPosition position) { switch (position) { case TOP: return BorderLayout.NORTH; case LEFT: return BorderLayout.WEST; case BOTTOM: return BorderLayout.SOUTH; case RIGHT: return BorderLayout.EAST; } return BorderLayout.SOUTH; } private CommonActionsPanel.Buttons[] getButtons() { final ArrayList<CommonActionsPanel.Buttons> buttons = new ArrayList<>(); final HashMap<CommonActionsPanel.Buttons, Pair<Boolean, AnActionButtonRunnable>> map = new HashMap<>(); map.put(CommonActionsPanel.Buttons.ADD, Pair.create(myAddActionEnabled, myAddAction)); map.put(CommonActionsPanel.Buttons.REMOVE, Pair.create(myRemoveActionEnabled, myRemoveAction)); map.put(CommonActionsPanel.Buttons.EDIT, Pair.create(myEditActionEnabled, myEditAction)); map.put(CommonActionsPanel.Buttons.UP, Pair.create(myUpActionEnabled, myUpAction)); map.put(CommonActionsPanel.Buttons.DOWN, Pair.create(myDownActionEnabled, myDownAction)); for (CommonActionsPanel.Buttons button : CommonActionsPanel.Buttons.values()) { final Pair<Boolean, AnActionButtonRunnable> action = map.get(button); if (action != null && action.first != null && action.first && action.second != null) { buttons.add(button); } } return buttons.toArray(new CommonActionsPanel.Buttons[buttons.size()]); } @Override public CommonActionsPanel.Listener createListener(final CommonActionsPanel panel) { return new CommonActionsPanel.Listener() { @Override public void doAdd() { if (myAddAction != null) { myAddAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.ADD)); } } @Override public void doEdit() { if (myEditAction != null) { myEditAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.EDIT)); } } @Override public void doRemove() { if (myRemoveAction != null) { myRemoveAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.REMOVE)); } } @Override public void doUp() { if (myUpAction != null) { myUpAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.UP)); } } @Override public void doDown() { if (myDownAction != null) { myDownAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.DOWN)); } } }; } public static AnActionButton findAddButton(@NotNull JComponent container) { return findButton(container, CommonActionsPanel.Buttons.ADD); } public static AnActionButton findEditButton(@NotNull JComponent container) { return findButton(container, CommonActionsPanel.Buttons.EDIT); } public static AnActionButton findRemoveButton(@NotNull JComponent container) { return findButton(container, CommonActionsPanel.Buttons.REMOVE); } public static AnActionButton findUpButton(@NotNull JComponent container) { return findButton(container, CommonActionsPanel.Buttons.UP); } public static AnActionButton findDownButton(@NotNull JComponent container) { return findButton(container, CommonActionsPanel.Buttons.DOWN); } private static AnActionButton findButton(JComponent comp, CommonActionsPanel.Buttons type) { final CommonActionsPanel panel = UIUtil.findComponentOfType(comp, CommonActionsPanel.class); if (panel != null) { return panel.getAnActionButton(type); } //noinspection ConstantConditions return null; } /** * Marker interface, button will be disabled if no selected element */ public abstract static class ElementActionButton extends AnActionButton { public ElementActionButton(String text, String description, @Nullable Icon icon) { super(text, description, icon); } public ElementActionButton(String text, Icon icon) { super(text, icon); } public ElementActionButton() { } public ElementActionButton(String text) { super(text); } } }