IDEA-103464 Adding environment variables to a Tomcat server is a UX nightmare
[idea/community.git] / platform / platform-api / src / com / intellij / ui / ToolbarDecorator.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.ui;
17
18 import com.intellij.openapi.actionSystem.*;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.util.Pair;
21 import com.intellij.openapi.util.SystemInfo;
22 import com.intellij.ui.border.CustomLineBorder;
23 import com.intellij.ui.table.TableView;
24 import com.intellij.util.SmartList;
25 import com.intellij.util.ui.EditableModel;
26 import com.intellij.util.ui.ElementProducer;
27 import com.intellij.util.ui.UIUtil;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 import javax.swing.*;
32 import javax.swing.border.Border;
33 import java.awt.*;
34 import java.util.*;
35 import java.util.List;
36
37 /**
38  * @author Konstantin Bulenkov
39  *
40  * @see #createDecorator(javax.swing.JList)
41  * @see #createDecorator(javax.swing.JTable)
42  * @see #createDecorator(javax.swing.JTree)
43  */
44 @SuppressWarnings("UnusedDeclaration")
45 public abstract class ToolbarDecorator implements CommonActionsPanel.ListenerFactory {
46   protected Border myPanelBorder;
47   protected Border myToolbarBorder;
48   protected boolean myAddActionEnabled;
49   protected boolean myEditActionEnabled;
50   protected boolean myRemoveActionEnabled;
51   protected boolean myUpActionEnabled;
52   protected boolean myDownActionEnabled;
53   protected Border myActionsPanelBorder;
54   private final List<AnActionButton> myExtraActions = new SmartList<AnActionButton>();
55   private ActionToolbarPosition myToolbarPosition;
56   protected AnActionButtonRunnable myAddAction;
57   protected AnActionButtonRunnable myEditAction;
58   protected AnActionButtonRunnable myRemoveAction;
59   protected AnActionButtonRunnable myUpAction;
60   protected AnActionButtonRunnable myDownAction;
61   private String myAddName;
62   private String myEditName;
63   private String myRemoveName;
64   private String myMoveUpName;
65   private String myMoveDownName;
66   private AnActionButtonUpdater myAddActionUpdater = null;
67   private AnActionButtonUpdater myRemoveActionUpdater = null;
68   private AnActionButtonUpdater myEditActionUpdater = null;
69   private AnActionButtonUpdater myMoveUpActionUpdater = null;
70   private AnActionButtonUpdater myMoveDownActionUpdater = null;
71   private Dimension myPreferredSize;
72   private CommonActionsPanel myActionsPanel;
73   private Comparator<AnActionButton> myButtonComparator;
74   private boolean myAsUsualTopToolbar = false;
75   private Icon myAddIcon;
76   private boolean myForcedDnD = false;
77
78   protected abstract JComponent getComponent();
79
80   protected abstract void updateButtons();
81
82   protected void updateExtraElementActions(boolean someElementSelected) {
83     for (AnActionButton action : myExtraActions) {
84       if (action instanceof ElementActionButton) {
85         action.setEnabled(someElementSelected);
86       }
87     }
88   }
89
90   public final CommonActionsPanel getActionsPanel() {
91     return myActionsPanel;
92   }
93
94   public ToolbarDecorator initPosition() {
95     setToolbarPosition(SystemInfo.isMac ? ActionToolbarPosition.BOTTOM : ActionToolbarPosition.RIGHT);
96     return this;
97   }
98
99   public ToolbarDecorator setAsUsualTopToolbar() {
100     myAsUsualTopToolbar = true;
101     setToolbarPosition(ActionToolbarPosition.TOP);
102     return this;
103   }
104
105   public static ToolbarDecorator createDecorator(@NotNull JTable table) {
106     return new TableToolbarDecorator(table, null).initPosition();
107   }
108   
109   public static ToolbarDecorator createDecorator(@NotNull JTree tree) {
110     return createDecorator(tree, null);
111   }
112
113   private static ToolbarDecorator createDecorator(@NotNull JTree tree, @Nullable ElementProducer<?> producer) {
114     return new TreeToolbarDecorator(tree, producer).initPosition();
115   }
116
117   public static ToolbarDecorator createDecorator(@NotNull JList list) {
118     return new ListToolbarDecorator(list, null).initPosition();
119   }
120
121   public static ToolbarDecorator createDecorator(@NotNull JList list, EditableModel editableModel) {
122     return new ListToolbarDecorator(list, editableModel).initPosition();
123   }
124
125   public static <T> ToolbarDecorator  createDecorator(@NotNull TableView<T> table, @Nullable ElementProducer<T> producer) {
126     return new TableToolbarDecorator(table, producer).initPosition();
127   }
128
129   public ToolbarDecorator disableAddAction() {
130     myAddActionEnabled = false;
131     return this;
132   }
133
134   public ToolbarDecorator disableRemoveAction() {
135     myRemoveActionEnabled = false;
136     return this;
137   }
138
139   public ToolbarDecorator disableUpAction() {
140     myUpActionEnabled = false;
141     return this;
142   }
143
144   public ToolbarDecorator disableUpDownActions() {
145     myUpActionEnabled = false;
146     myDownActionEnabled = false;
147     return this;
148   }
149
150   public ToolbarDecorator disableDownAction() {
151     myDownActionEnabled = false;
152     return this;
153   }
154
155   public ToolbarDecorator setPanelBorder(Border border) {
156     myPanelBorder = border;
157     return this;
158   }
159
160   public ToolbarDecorator setToolbarBorder(Border border) {
161     myActionsPanelBorder = border;
162     return this;
163   }
164
165   public ToolbarDecorator setButtonComparator(Comparator<AnActionButton> buttonComparator) {
166     myButtonComparator = buttonComparator;
167     return this;
168   }
169   
170   public ToolbarDecorator setButtonComparator(String...actionNames) {
171     final List<String> names = Arrays.asList(actionNames);
172     myButtonComparator = new Comparator<AnActionButton>() {
173       @Override
174       public int compare(AnActionButton o1, AnActionButton o2) {
175         final String t1 = o1.getTemplatePresentation().getText();
176         final String t2 = o2.getTemplatePresentation().getText();
177         if (t1 == null || t2 == null) return 0;
178         
179         final int ind1 = names.indexOf(t1);
180         final int ind2 = names.indexOf(t2);
181         if (ind1 == -1 && ind2 >= 0) return 1;
182         if (ind2 == -1 && ind1 >= 0) return -1;
183         return ind1 - ind2;
184       }
185     };
186     return this;
187   }
188
189   public ToolbarDecorator setLineBorder(int top, int left, int bottom, int right) {
190     return setToolbarBorder(new CustomLineBorder(top, left, bottom, right));
191   }
192
193   public ToolbarDecorator addExtraAction(AnActionButton action) {
194     if (action != null) {
195       myExtraActions.add(action);
196     }
197     return this;
198   }
199
200   public ToolbarDecorator addExtraActions(AnActionButton... actions) {
201     for (AnActionButton action : actions) {
202       addExtraAction(action);
203     }
204     return this;
205   }
206
207   public ToolbarDecorator setToolbarPosition(ActionToolbarPosition position) {
208     myToolbarPosition = position;
209     myActionsPanelBorder = new CustomLineBorder(myToolbarPosition == ActionToolbarPosition.BOTTOM ? 1 : 0,
210                                                 myToolbarPosition == ActionToolbarPosition.RIGHT ? 1 : 0,
211                                                 myToolbarPosition == ActionToolbarPosition.TOP ? 1 : 0,
212                                                 myToolbarPosition == ActionToolbarPosition.LEFT ? 1 : 0);
213     return this;
214   }
215
216   public ToolbarDecorator setAddAction(AnActionButtonRunnable action) {
217     myAddActionEnabled = action != null;
218     myAddAction = action;
219     return this;
220   }
221
222   public ToolbarDecorator setEditAction(AnActionButtonRunnable action) {
223     myEditActionEnabled = action != null;
224     myEditAction = action;
225     return this;
226   }
227
228   public ToolbarDecorator setRemoveAction(AnActionButtonRunnable action) {
229     myRemoveActionEnabled = action != null;
230     myRemoveAction = action;
231     return this;
232   }
233
234   public ToolbarDecorator setMoveUpAction(AnActionButtonRunnable action) {
235     myUpActionEnabled = action != null;
236     myUpAction = action;
237     return this;
238   }
239
240   public ToolbarDecorator setMoveDownAction(AnActionButtonRunnable action) {
241     myDownActionEnabled = action != null;
242     myDownAction = action;
243     return this;
244   }
245
246   public ToolbarDecorator setAddActionName(String name) {
247     myAddName = name;
248     return this;
249   }
250
251   public ToolbarDecorator setEditActionName(String name) {
252     myEditName = name;
253     return this;
254   }
255
256   public ToolbarDecorator setRemoveActionName(String name) {
257     myRemoveName = name;
258     return this;
259   }
260
261   public ToolbarDecorator setMoveUpActionName(String name) {
262     myMoveUpName = name;
263     return this;
264   }
265
266   public ToolbarDecorator setMoveDownActionName(String name) {
267     myMoveDownName = name;
268     return this;
269   }
270
271   public ToolbarDecorator setAddActionUpdater(AnActionButtonUpdater updater) {
272     myAddActionUpdater = updater;
273     return this;
274   }
275
276   public ToolbarDecorator setRemoveActionUpdater(AnActionButtonUpdater updater) {
277     myRemoveActionUpdater = updater;
278     return this;
279   }
280
281   public ToolbarDecorator setEditActionUpdater(AnActionButtonUpdater updater) {
282     myEditActionUpdater = updater;
283     return this;
284   }
285
286   public ToolbarDecorator setMoveUpActionUpdater(AnActionButtonUpdater updater) {
287     myMoveUpActionUpdater = updater;
288     return this;
289   }
290
291   public ToolbarDecorator setMoveDownActionUpdater(AnActionButtonUpdater updater) {
292     myMoveDownActionUpdater = updater;
293     return this;
294   }
295
296   public ToolbarDecorator setForcedDnD() {
297     myForcedDnD = true;
298     return this;
299
300   }
301
302   public ToolbarDecorator setActionGroup(@NotNull ActionGroup actionGroup) {
303     AnAction[] actions = actionGroup.getChildren(null);
304     for (AnAction action : actions) {
305       if (!(action instanceof Separator)) {
306         addExtraAction(AnActionButton.fromAction(action));
307       }
308     }
309     return this;
310   }
311
312   public ToolbarDecorator setPreferredSize(Dimension size) {
313     myPreferredSize = size;
314     return this;
315   }
316
317   public ToolbarDecorator setVisibleRowCount(int rowCount) {
318     return this;//do nothing by default
319   }
320
321   public ToolbarDecorator setAddIcon(Icon addIcon) {
322     myAddIcon = addIcon;
323     return this;
324   }
325
326   /**
327    * @return panel that contains wrapped component (with added scrollpane) and toolbar panel.
328    */
329   public JPanel createPanel() {
330     final CommonActionsPanel.Buttons[] buttons = getButtons();
331     final JComponent contextComponent = getComponent();
332     myActionsPanel = new CommonActionsPanel(this, contextComponent,
333                              myToolbarPosition,
334                              myExtraActions.toArray(new AnActionButton[myExtraActions.size()]),
335                              myButtonComparator,
336                              myAddName, myRemoveName, myMoveUpName, myMoveDownName, myEditName,
337                              myAddIcon, buttons);
338     final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(contextComponent, true);
339     if (myPreferredSize != null) {
340       scrollPane.setPreferredSize(myPreferredSize);
341     }
342     final JPanel panel = new JPanel(new BorderLayout()) {
343       @Override
344       public void addNotify() {
345         super.addNotify();
346         updateButtons();
347       }
348     };
349     panel.add(scrollPane, BorderLayout.CENTER);
350     panel.add(myActionsPanel, getPlacement());
351     installUpdaters();
352     updateButtons();
353     installDnD();
354     panel.putClientProperty(ActionToolbar.ACTION_TOOLBAR_PROPERTY_KEY, myActionsPanel.getComponent(0));
355
356     Border mainBorder = myPanelBorder != null ? myPanelBorder : IdeBorderFactory.createBorder(SideBorder.ALL);  
357     if (myAsUsualTopToolbar) {
358       scrollPane.setBorder(mainBorder);
359     } else {
360       myActionsPanel.setBorder(myActionsPanelBorder);
361       panel.setBorder(mainBorder);
362     }
363     return panel;
364   }
365
366   private void installUpdaters() {
367     if (myAddActionEnabled && myAddAction != null && myAddActionUpdater != null) {
368       myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.ADD).addCustomUpdater(myAddActionUpdater);
369     }
370     if (myEditActionEnabled && myEditAction != null && myEditActionUpdater != null) {
371       myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.EDIT).addCustomUpdater(myEditActionUpdater);
372     }
373     if (myRemoveActionEnabled && myRemoveAction != null && myRemoveActionUpdater != null) {
374       myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.REMOVE).addCustomUpdater(myRemoveActionUpdater);
375     }
376     if (myUpActionEnabled && myUpAction != null && myMoveUpActionUpdater != null) {
377       myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.UP).addCustomUpdater(myMoveUpActionUpdater);
378     }
379     if (myDownActionEnabled && myDownAction != null && myMoveDownActionUpdater != null) {
380       myActionsPanel.getAnActionButton(CommonActionsPanel.Buttons.DOWN).addCustomUpdater(myMoveDownActionUpdater);
381     }
382   }
383
384   protected void installDnD() {
385     if ((myForcedDnD || (myUpAction != null && myUpActionEnabled
386         && myDownAction != null && myDownActionEnabled))
387         && !ApplicationManager.getApplication().isHeadlessEnvironment()
388         && isModelEditable()) {
389       installDnDSupport();
390     }
391   }
392
393   protected abstract void installDnDSupport();
394
395   protected abstract boolean isModelEditable();
396
397   private Object getPlacement() {
398     switch (myToolbarPosition) {
399       case TOP: return BorderLayout.NORTH;
400       case LEFT: return BorderLayout.WEST;
401       case BOTTOM: return BorderLayout.SOUTH;
402       case RIGHT: return BorderLayout.EAST;
403     }
404     return BorderLayout.SOUTH;
405   }
406
407   private CommonActionsPanel.Buttons[] getButtons() {
408     final ArrayList<CommonActionsPanel.Buttons> buttons = new ArrayList<CommonActionsPanel.Buttons>();
409     final HashMap<CommonActionsPanel.Buttons, Pair<Boolean, AnActionButtonRunnable>> map =
410       new HashMap<CommonActionsPanel.Buttons, Pair<Boolean, AnActionButtonRunnable>>();
411     map.put(CommonActionsPanel.Buttons.ADD, Pair.create(myAddActionEnabled, myAddAction));
412     map.put(CommonActionsPanel.Buttons.REMOVE, Pair.create(myRemoveActionEnabled, myRemoveAction));
413     map.put(CommonActionsPanel.Buttons.EDIT, Pair.create(myEditActionEnabled, myEditAction));
414     map.put(CommonActionsPanel.Buttons.UP, Pair.create(myUpActionEnabled, myUpAction));
415     map.put(CommonActionsPanel.Buttons.DOWN, Pair.create(myDownActionEnabled, myDownAction));
416
417     for (CommonActionsPanel.Buttons button : CommonActionsPanel.Buttons.values()) {
418       final Pair<Boolean, AnActionButtonRunnable> action = map.get(button);
419       if (action != null && action.first != null && action.first && action.second != null) {
420         buttons.add(button);
421       }
422     }
423
424     return buttons.toArray(new CommonActionsPanel.Buttons[buttons.size()]);
425   }
426
427   @Override
428   public CommonActionsPanel.Listener createListener(final CommonActionsPanel panel) {
429     return new CommonActionsPanel.Listener() {
430       @Override
431       public void doAdd() {
432         if (myAddAction != null) {
433           myAddAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.ADD));
434         }
435       }
436
437       @Override
438       public void doEdit() {
439         if (myEditAction != null) {
440           myEditAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.EDIT));
441         }
442       }
443
444       @Override
445       public void doRemove() {
446         if (myRemoveAction != null) {
447           myRemoveAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.REMOVE));
448         }
449       }
450
451       @Override
452       public void doUp() {
453         if (myUpAction != null) {
454           myUpAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.UP));
455         }
456       }
457
458       @Override
459       public void doDown() {
460         if (myDownAction != null) {
461           myDownAction.run(panel.getAnActionButton(CommonActionsPanel.Buttons.DOWN));
462         }
463       }
464     };
465   }
466   
467   public static AnActionButton findAddButton(@NotNull JComponent container) {
468     return findButton(container, CommonActionsPanel.Buttons.ADD);
469   }
470
471   public static AnActionButton findEditButton(@NotNull JComponent container) {
472     return findButton(container, CommonActionsPanel.Buttons.EDIT);
473   }
474
475   public static AnActionButton findRemoveButton(@NotNull JComponent container) {
476     return findButton(container, CommonActionsPanel.Buttons.REMOVE);
477   }
478
479   public static AnActionButton findUpButton(@NotNull JComponent container) {
480     return findButton(container, CommonActionsPanel.Buttons.UP);
481   }
482
483   public static AnActionButton findDownButton(@NotNull JComponent container) {
484     return findButton(container, CommonActionsPanel.Buttons.DOWN);
485   }
486
487   private static AnActionButton findButton(JComponent comp, CommonActionsPanel.Buttons type) {
488     final CommonActionsPanel panel = UIUtil.findComponentOfType(comp, CommonActionsPanel.class);
489     if (panel != null) {
490       return panel.getAnActionButton(type);
491     }
492     //noinspection ConstantConditions
493     return null;
494   }
495
496   /**
497    * Marker interface, button will be disabled if no selected element
498    */
499   public abstract static class ElementActionButton extends AnActionButton {
500     public ElementActionButton(String text, String description, @Nullable Icon icon) {
501       super(text, description, icon);
502     }
503
504     public ElementActionButton(String text, Icon icon) {
505       super(text, icon);
506     }
507
508     public ElementActionButton() {
509     }
510
511     public ElementActionButton(String text) {
512       super(text);
513     }
514   }
515 }