action groups cleanup work in progress
[fedora-idea.git] / platform-api / src / com / intellij / openapi / actionSystem / DefaultActionGroup.java
blob2ff78dcf2d0c8e1b3360ad11e69790b28b0659b7
1 /*
2 * Copyright 2000-2007 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.openapi.actionSystem;
18 import com.intellij.openapi.util.Pair;
19 import org.jetbrains.annotations.Nullable;
20 import org.jetbrains.annotations.NotNull;
22 import java.util.ArrayList;
24 /**
25 * A default implementation of {@link ActionGroup}. Provides the ability
26 * to add children actions and separators between them. In most of the
27 * cases you will be using this implementation but note that there are
28 * cases (for example "Recent files" dialog) where children are determined
29 * on rules different than just positional constraints, that's when you need
30 * to implement your own <code>ActionGroup</code>.
32 * @see Constraints
34 public class DefaultActionGroup extends ActionGroup {
35 /**
36 * Contains instances of AnAction
38 private ArrayList<AnAction> mySortedChildren;
39 /**
40 * Contains instances of Pair
42 private ArrayList<Pair<AnAction,Constraints>> myPairs;
44 public DefaultActionGroup(){
45 this(null, false);
48 public DefaultActionGroup(String shortName, boolean popup){
49 super(shortName, popup);
50 mySortedChildren = new ArrayList<AnAction>();
51 myPairs = new ArrayList<Pair<AnAction, Constraints>>();
54 /**
55 * Adds the specified action to the tail.
57 * @param action Action to be added
59 public final void add(@NotNull AnAction action, @NotNull ActionManager actionManager){
60 add(action, new Constraints(Anchor.LAST, null), actionManager);
63 public final void add(@NotNull AnAction action){
64 add(action, new Constraints(Anchor.LAST, null));
67 /**
68 * Adds a separator to the tail.
70 public final void addSeparator(){
71 add(Separator.getInstance());
74 /**
75 * Adds the specified action with the specified constraint.
77 * @param action Action to be added; cannot be null
78 * @param constraint Constraint to be used for determining action's position; cannot be null
79 * @throws IllegalArgumentException in case when:
80 * <li>action is null
81 * <li>constraint is null
82 * <li>action is already in the group
84 public final void add(@NotNull AnAction action, @NotNull Constraints constraint){
85 add(action, constraint, ActionManager.getInstance());
88 public final void add(@NotNull AnAction action, @NotNull Constraints constraint, @NotNull ActionManager actionManager) {
89 if (action == this) {
90 throw new IllegalArgumentException("Cannot add a group to itself");
92 // Check that action isn't already registered
93 if (!(action instanceof Separator)) {
94 if (mySortedChildren.contains(action)) {
95 throw new IllegalArgumentException("cannot add an action twice");
97 for (Pair<AnAction, Constraints> pair : myPairs) {
98 if (action.equals(pair.first)) {
99 throw new IllegalArgumentException("cannot add an action twice");
104 constraint = (Constraints)constraint.clone();
106 if (constraint.myAnchor == Anchor.FIRST) {
107 mySortedChildren.add(0, action);
109 else if (constraint.myAnchor == Anchor.LAST) {
110 mySortedChildren.add(action);
112 else {
113 if (addToSortedList(action, constraint, actionManager)) {
114 actionAdded(action, actionManager);
116 else {
117 myPairs.add(new Pair<AnAction, Constraints>(action, constraint));
122 private void actionAdded(AnAction addedAction, ActionManager actionManager){
123 String addedActionId;
124 if(addedAction instanceof ActionStub){
125 addedActionId=((ActionStub)addedAction).getId();
126 }else{
127 addedActionId=actionManager.getId(addedAction);
129 if (addedActionId == null){
130 return;
132 outer: while(myPairs.size() > 0){
133 for(int i = 0; i < myPairs.size(); i++){
134 Pair<AnAction, Constraints> pair = myPairs.get(i);
135 if (addToSortedList(pair.first, pair.second, actionManager)){
136 myPairs.remove(i);
137 continue outer;
140 break;
144 private boolean addToSortedList(AnAction action, Constraints constraint, ActionManager actionManager){
145 int index = findIndex(constraint.myRelativeToActionId, mySortedChildren, actionManager);
146 if (index == -1){
147 return false;
149 if (constraint.myAnchor == Anchor.BEFORE){
150 mySortedChildren.add(index, action);
152 else{
153 mySortedChildren.add(index + 1, action);
155 return true;
158 private static int findIndex(String actionId, ArrayList<AnAction> actions, ActionManager actionManager) {
159 for (int i = 0; i < actions.size(); i++) {
160 AnAction action = actions.get(i);
161 if (action instanceof ActionStub) {
162 if (((ActionStub)action).getId().equals(actionId)) {
163 return i;
166 else {
167 String id = actionManager.getId(action);
168 if (id != null && id.equals(actionId)) {
169 return i;
173 return -1;
177 * Removes specified action from group.
179 * @param action Action to be removed
181 public final void remove(AnAction action){
182 if (!mySortedChildren.remove(action)){
183 for(int i = 0; i < myPairs.size(); i++){
184 Pair<AnAction, Constraints> pair = myPairs.get(i);
185 if (pair.first.equals(action)){
186 myPairs.remove(i);
187 break;
194 * Removes all children actions (separators as well) from the group.
196 public final void removeAll(){
197 mySortedChildren.clear();
198 myPairs.clear();
202 * Returns group's children in the order determined by constraints.
204 * @param e not used
206 * @return An array of children actions
208 @NotNull
209 public final AnAction[] getChildren(@Nullable AnActionEvent e){
210 // Mix sorted actions and pairs
211 int sortedSize = mySortedChildren.size();
212 AnAction[] children = new AnAction[sortedSize + myPairs.size()];
213 for(int i = 0; i < sortedSize; i++){
214 children[i] = mySortedChildren.get(i);
216 for(int i = 0; i < myPairs.size(); i++){
217 children[i + sortedSize] = myPairs.get(i).first;
219 // Replace ActionStubs with real actions
220 outer: for(int i=0;i<children.length;i++){
221 AnAction action=children[i];
222 if(!(action instanceof ActionStub)){
223 continue;
225 ActionStub stub=(ActionStub)action;
226 // resolve action
227 ActionManager actionManager = e != null ? e.getActionManager() : ActionManager.getInstance();
228 AnAction actualAction = actionManager.getAction(stub.getId());
230 // Find in sorted children first
231 int index=mySortedChildren.indexOf(stub);
232 if(index!=-1){
233 children[i]=actualAction;
234 mySortedChildren.set(index,actualAction);
235 continue;
237 // Try to find action within pairs
238 for(int j=0;j<myPairs.size();j++){
239 Pair<AnAction, Constraints> pair=myPairs.get(j);
240 if(pair.first.equals(stub)){
241 children[i]=actualAction;
242 myPairs.set(j,new Pair<AnAction, Constraints>(actualAction,pair.second));
243 continue outer;
246 throw new IllegalStateException("unknown stub: "+stub.getId());
248 return children;
252 * Returns the number of contained children (including separators).
254 * @return number of children in the group
256 public final int getChildrenCount(){
257 return mySortedChildren.size() + myPairs.size();
260 @NotNull
261 public final AnAction[] getChildActionsOrStubs(@Nullable AnActionEvent e){
262 // Mix sorted actions and pairs
263 int sortedSize = mySortedChildren.size();
264 AnAction[] children = new AnAction[sortedSize + myPairs.size()];
265 for(int i = 0; i < sortedSize; i++){
266 children[i] = mySortedChildren.get(i);
268 for(int i = 0; i < myPairs.size(); i++){
269 children[i + sortedSize] = myPairs.get(i).first;
271 return children;
274 public final void addAll(ActionGroup group) {
275 for (AnAction each : group.getChildren(null)) {
276 add(each);