• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
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 
17 package com.android.ide.common.api;
18 
19 import com.android.util.Pair;
20 
21 import java.net.URL;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.regex.Pattern;
25 
26 /**
27  * A {@link RuleAction} represents an action provided by an {@link IViewRule}, typically
28  * shown in a context menu or in the layout actions bar.
29  * <p/>
30  * Each action should have a reasonably unique ID. This is used when multiple nodes
31  * are selected to filter the actions down to just those actions that are supported
32  * across all selected nodes. If an action does not support multiple nodes, it can
33  * return false from {@link #supportsMultipleNodes()}.
34  * <p/>
35  * Actions can be grouped into a hierarchy of sub-menus using the {@link NestedAction} class,
36  * or into a flat submenu using the {@link Choices} class.
37  * <p/>
38  * Actions (including separators) all have a "sort priority", and this is used to
39  * sort the menu items or toolbar buttons into a specific order.
40  * <p>
41  * <b>NOTE: This is not a public or final API; if you rely on this be prepared
42  * to adjust your code for the next tools release.</b>
43  * </p>
44  */
45 public class RuleAction implements Comparable<RuleAction> {
46     /**
47      * Character used to split multiple checked choices.
48      * The pipe character "|" is used, to natively match Android resource flag separators.
49      */
50     public final static String CHOICE_SEP = "|"; //$NON-NLS-1$
51 
52     /**
53      * Same as {@link #CHOICE_SEP} but safe for use in regular expressions.
54      */
55     public final static String CHOICE_SEP_PATTERN = Pattern.quote(CHOICE_SEP);
56 
57     /**
58      * The unique id of the action.
59      * @see #getId()
60      */
61     private final String mId;
62     /**
63      * The UI-visible title of the action.
64      */
65     private final String mTitle;
66 
67     /** A URL pointing to an icon, or null */
68     private URL mIconUrl;
69 
70     /**
71      * A callback executed when the action is selected in the context menu.
72      */
73     private final IMenuCallback mCallback;
74 
75     /**
76      * The sorting priority of this item; actions can be sorted according to these
77      */
78     protected final int mSortPriority;
79 
80     /**
81      * Whether this action supports multiple nodes, see
82      * {@link #supportsMultipleNodes()} for details.
83      */
84     private final boolean mSupportsMultipleNodes;
85 
86     /**
87      * Special value which will insert a separator in the choices' submenu.
88      */
89     public final static String SEPARATOR = "----";
90 
91     // Factories
92 
93     /**
94      * Constructs a new separator which will be shown in places where separators
95      * are supported such as context menus
96      *
97      * @param sortPriority a priority used for sorting this action
98      * @return a new separator
99      */
createSeparator(int sortPriority)100     public static Separator createSeparator(int sortPriority) {
101         return new Separator(sortPriority, true /* supportsMultipleNodes*/);
102     }
103 
104     /**
105      * Constructs a new base {@link RuleAction} with its ID, title and action callback.
106      *
107      * @param id The unique ID of the action. Must not be null.
108      * @param title The title of the action. Must not be null.
109      * @param callback The callback executed when the action is selected.
110      *            Must not be null.
111      * @param iconUrl a URL pointing to an icon to use for this action, or null
112      * @param sortPriority a priority used for sorting this action
113      * @param supportsMultipleNodes whether this action supports multiple nodes,
114      *            see {@link #supportsMultipleNodes()} for details
115      * @return the new {@link RuleAction}
116      */
createAction(String id, String title, IMenuCallback callback, URL iconUrl, int sortPriority, boolean supportsMultipleNodes)117     public static RuleAction createAction(String id, String title,
118             IMenuCallback callback, URL iconUrl, int sortPriority, boolean supportsMultipleNodes) {
119         RuleAction action = new RuleAction(id, title, callback, sortPriority,
120                 supportsMultipleNodes);
121         action.setIconUrl(iconUrl);
122 
123         return action;
124     }
125 
126     /**
127      * Creates a new immutable toggle action.
128      *
129      * @param id The unique id of the action. Cannot be null.
130      * @param title The UI-visible title of the context menu item. Cannot be null.
131      * @param isChecked Whether the context menu item has a check mark.
132      * @param callback A callback to execute when the context menu item is
133      *            selected.
134      * @param iconUrl a URL pointing to an icon to use for this action, or null
135      * @param sortPriority a priority used for sorting this action
136      * @param supportsMultipleNodes whether this action supports multiple nodes,
137      *            see {@link #supportsMultipleNodes()} for details
138      * @return the new {@link Toggle}
139      */
createToggle(String id, String title, boolean isChecked, IMenuCallback callback, URL iconUrl, int sortPriority, boolean supportsMultipleNodes)140     public static Toggle createToggle(String id, String title, boolean isChecked,
141             IMenuCallback callback, URL iconUrl, int sortPriority,
142             boolean supportsMultipleNodes) {
143         Toggle toggle = new Toggle(id, title, isChecked, callback, sortPriority,
144                 supportsMultipleNodes);
145         toggle.setIconUrl(iconUrl);
146         return toggle;
147     }
148 
149     /**
150      * Creates a new immutable multiple-choice action with a defined ordered set
151      * of action children.
152      *
153      * @param id The unique id of the action. Cannot be null.
154      * @param title The title of the action to be displayed to the user
155      * @param provider Provides the actions to be shown as children of this
156      *            action
157      * @param callback A callback to execute when the context menu item is
158      *            selected.
159      * @param iconUrl the icon to use for the multiple choice action itself
160      * @param sortPriority the sorting priority to use for the multiple choice
161      *            action itself
162      * @param supportsMultipleNodes whether this action supports multiple nodes,
163      *            see {@link #supportsMultipleNodes()} for details
164      * @return the new {@link NestedAction}
165      */
createChoices(String id, String title, IMenuCallback callback, URL iconUrl, int sortPriority, boolean supportsMultipleNodes, ActionProvider provider)166     public static NestedAction createChoices(String id, String title,
167             IMenuCallback callback, URL iconUrl,
168             int sortPriority, boolean supportsMultipleNodes, ActionProvider provider) {
169         NestedAction choices = new NestedAction(id, title, provider, callback,
170                 sortPriority, supportsMultipleNodes);
171         choices.setIconUrl(iconUrl);
172         return choices;
173     }
174 
175     /**
176      * Creates a new immutable multiple-choice action with a defined ordered set
177      * of children.
178      *
179      * @param id The unique id of the action. Cannot be null.
180      * @param title The title of the action to be displayed to the user
181      * @param iconUrls The icon urls for the children items (may be null)
182      * @param ids The internal ids for the children
183      * @param current The id(s) of the current choice(s) that will be check
184      *            marked. Can be null. Can be an id not present in the choices
185      *            map. There can be more than one id separated by
186      *            {@link #CHOICE_SEP}.
187      * @param callback A callback to execute when the context menu item is
188      *            selected.
189      * @param titles The UI-visible titles of the children
190      * @param iconUrl the icon to use for the multiple choice action itself
191      * @param sortPriority the sorting priority to use for the multiple choice
192      *            action itself
193      * @param supportsMultipleNodes whether this action supports multiple nodes,
194      *            see {@link #supportsMultipleNodes()} for details
195      * @return the new {@link Choices}
196      */
createChoices(String id, String title, IMenuCallback callback, List<String> titles, List<URL> iconUrls, List<String> ids, String current, URL iconUrl, int sortPriority, boolean supportsMultipleNodes)197     public static Choices createChoices(String id, String title,
198             IMenuCallback callback, List<String> titles, List<URL> iconUrls, List<String> ids,
199             String current, URL iconUrl, int sortPriority, boolean supportsMultipleNodes) {
200         Choices choices = new Choices(id, title, callback, titles, iconUrls,
201                 ids, current, sortPriority, supportsMultipleNodes);
202         choices.setIconUrl(iconUrl);
203 
204         return choices;
205     }
206 
207     /**
208      * Creates a new immutable multiple-choice action with a defined ordered set
209      * of children.
210      *
211      * @param id The unique id of the action. Cannot be null.
212      * @param title The title of the action to be displayed to the user
213      * @param iconUrls The icon urls for the children items (may be null)
214      * @param current The id(s) of the current choice(s) that will be check
215      *            marked. Can be null. Can be an id not present in the choices
216      *            map. There can be more than one id separated by
217      *            {@link #CHOICE_SEP}.
218      * @param callback A callback to execute when the context menu item is
219      *            selected.
220      * @param iconUrl the icon to use for the multiple choice action itself
221      * @param sortPriority the sorting priority to use for the multiple choice
222      *            action itself
223      * @param supportsMultipleNodes whether this action supports multiple nodes,
224      *            see {@link #supportsMultipleNodes()} for details
225      * @param idsAndTitles a list of pairs (of ids and titles) to use for the
226      *            menu items
227      * @return the new {@link Choices}
228      */
createChoices(String id, String title, IMenuCallback callback, List<URL> iconUrls, String current, URL iconUrl, int sortPriority, boolean supportsMultipleNodes, List<Pair<String, String>> idsAndTitles)229     public static Choices createChoices(String id, String title,
230             IMenuCallback callback, List<URL> iconUrls,
231             String current, URL iconUrl, int sortPriority,
232             boolean supportsMultipleNodes, List<Pair<String, String>> idsAndTitles) {
233         int itemCount = idsAndTitles.size();
234         List<String> titles = new ArrayList<String>(itemCount);
235         List<String> ids = new ArrayList<String>(itemCount);
236         for (Pair<String, String> pair : idsAndTitles) {
237             ids.add(pair.getFirst());
238             titles.add(pair.getSecond());
239         }
240         Choices choices = new Choices(id, title, callback, titles, iconUrls,
241                 ids, current, sortPriority, supportsMultipleNodes);
242         choices.setIconUrl(iconUrl);
243         return choices;
244     }
245 
246     /**
247      * Creates a new immutable multiple-choice action with lazily computed children.
248      *
249      * @param id The unique id of the action. Cannot be null.
250      * @param title The title of the multiple-choice itself
251      * @param callback A callback to execute when the context menu item is
252      *            selected.
253      * @param provider the provider which provides choices lazily
254      * @param current The id(s) of the current choice(s) that will be check
255      *            marked. Can be null. Can be an id not present in the choice
256      *            alternatives. There can be more than one id separated by
257      *            {@link #CHOICE_SEP}.
258      * @param iconUrl the icon to use for the multiple choice action itself
259      * @param sortPriority the sorting priority to use for the multiple choice
260      *            action itself
261      * @param supportsMultipleNodes whether this action supports multiple nodes,
262      *            see {@link #supportsMultipleNodes()} for details
263      * @return the new {@link Choices}
264      */
createChoices(String id, String title, IMenuCallback callback, ChoiceProvider provider, String current, URL iconUrl, int sortPriority, boolean supportsMultipleNodes)265     public static Choices createChoices(String id, String title,
266             IMenuCallback callback, ChoiceProvider provider,
267             String current, URL iconUrl, int sortPriority, boolean supportsMultipleNodes) {
268         Choices choices = new DelayedChoices(id, title, callback,
269                 current, provider, sortPriority, supportsMultipleNodes);
270         choices.setIconUrl(iconUrl);
271         return choices;
272     }
273 
274     /**
275      * Creates a new {@link RuleAction} with the given id and the given title.
276      * Actions which have the same id and the same title are deemed equivalent.
277      *
278      * @param id The unique id of the action, which must be similar for all actions that
279      *           perform the same task. Cannot be null.
280      * @param title The UI-visible title of the action.
281      * @param callback A callback to execute when the context menu item is
282      *            selected.
283      * @param sortPriority a priority used for sorting this action
284      * @param supportsMultipleNodes the new return value for
285      *            {@link #supportsMultipleNodes()}
286      */
RuleAction(String id, String title, IMenuCallback callback, int sortPriority, boolean supportsMultipleNodes)287     private RuleAction(String id, String title, IMenuCallback callback, int sortPriority,
288             boolean supportsMultipleNodes) {
289         mId = id;
290         mTitle = title;
291         mSortPriority = sortPriority;
292         mSupportsMultipleNodes = supportsMultipleNodes;
293         mCallback = callback;
294     }
295 
296     /**
297      * Returns the unique id of the action. In the context of a multiple selection,
298      * actions which have the same id are collapsed together and must represent the same
299      * action. Cannot be null.
300      *
301      * @return the unique id of the action, never null
302      */
getId()303     public String getId() {
304         return mId;
305     }
306 
307     /**
308      * Returns the UI-visible title of the action, shown in the context menu.
309      * Cannot be null.
310      *
311      * @return the user name of the action, never null
312      */
getTitle()313     public String getTitle() {
314         return mTitle;
315     }
316 
317     /**
318      * Actions which have the same id and the same title are deemed equivalent.
319      */
320     @Override
equals(Object obj)321     public boolean equals(Object obj) {
322         if (obj instanceof RuleAction) {
323             RuleAction rhs = (RuleAction) obj;
324 
325             if (mId != rhs.mId && !(mId != null && mId.equals(rhs.mId))) return false;
326             if (mTitle != rhs.mTitle &&
327                     !(mTitle != null && mTitle.equals(rhs.mTitle))) return false;
328             return true;
329         }
330         return false;
331     }
332 
333     /**
334      * Whether this action supports multiple nodes. An action which supports
335      * multiple nodes can be applied to different nodes by passing in different
336      * nodes to its callback. Some actions are hardcoded for a specific node (typically
337      * one that isn't selected, such as an action which affects the parent of a selected
338      * node), and these actions will not be added to the context menu when more than
339      * one node is selected.
340      *
341      * @return true if this node supports multiple nodes
342      */
supportsMultipleNodes()343     public boolean supportsMultipleNodes() {
344         return mSupportsMultipleNodes;
345     }
346 
347     /**
348      * Actions which have the same id and the same title have the same hash code.
349      */
350     @Override
hashCode()351     public int hashCode() {
352         int h = mId == null ? 0 : mId.hashCode();
353         h = h ^ (mTitle == null ? 0 : mTitle.hashCode());
354         return h;
355     }
356 
357     /**
358      * Gets a URL pointing to an icon to use for this action, if any.
359      *
360      * @return a URL pointing to an icon to use for this action, or null
361      */
getIconUrl()362     public URL getIconUrl() {
363         return mIconUrl;
364     }
365 
366     /**
367      * Sets a URL pointing to an icon to use for this action, if any.
368      *
369      * @param iconUrl a URL pointing to an icon to use for this action, or null
370      * @return this action, to allow setter chaining
371      */
setIconUrl(URL iconUrl)372     public RuleAction setIconUrl(URL iconUrl) {
373         mIconUrl = iconUrl;
374 
375         return this;
376     }
377 
378     /**
379      * Return a priority used for sorting this action
380      *
381      * @return a priority used for sorting this action
382      */
getSortPriority()383     public int getSortPriority() {
384         return mSortPriority;
385     }
386 
387     /**
388      * Returns the callback executed when the action is selected in the
389      * context menu. Cannot be null.
390      *
391      * @return the callback, never null
392      */
getCallback()393     public IMenuCallback getCallback() {
394         return mCallback;
395     }
396 
397     // Implements Comparable<MenuAciton>
compareTo(RuleAction other)398     public int compareTo(RuleAction other) {
399         if (mSortPriority != other.mSortPriority) {
400             return mSortPriority - other.mSortPriority;
401         }
402 
403         return mTitle.compareTo(other.mTitle);
404     }
405 
406     @Override
toString()407     public String toString() {
408         return "RuleAction [id=" + mId + ", title=" + mTitle + ", priority=" + mSortPriority + "]";
409     }
410 
411     /** A separator to display between actions */
412     public static class Separator extends RuleAction {
413         /** Construct using the factory {@link #createSeparator(int)} */
Separator(int sortPriority, boolean supportsMultipleNodes)414         private Separator(int sortPriority, boolean supportsMultipleNodes) {
415             super("_separator", "", null, sortPriority,  //$NON-NLS-1$ //$NON-NLS-2$
416                     supportsMultipleNodes);
417         }
418     }
419 
420     /**
421      * A toggle is a simple on/off action, displayed as an item in a context menu
422      * with a check mark if the item is checked.
423      * <p/>
424      * Two toggles are equal if they have the same id, title and group-id.
425      * It is expected for the checked state and action callback to be different.
426      */
427     public static class Toggle extends RuleAction {
428         /**
429          * True if the item is displayed with a check mark.
430          */
431         private final boolean mIsChecked;
432 
433         /**
434          * Creates a new immutable toggle action.
435          *
436          * @param id The unique id of the action. Cannot be null.
437          * @param title The UI-visible title of the context menu item. Cannot be null.
438          * @param isChecked Whether the context menu item has a check mark.
439          * @param callback A callback to execute when the context menu item is
440          *            selected.
441          */
Toggle(String id, String title, boolean isChecked, IMenuCallback callback, int sortPriority, boolean supportsMultipleNodes)442         private Toggle(String id, String title, boolean isChecked,
443                 IMenuCallback callback, int sortPriority, boolean supportsMultipleNodes) {
444             super(id, title, callback, sortPriority, supportsMultipleNodes);
445             mIsChecked = isChecked;
446         }
447 
448         /**
449          * Returns true if the item is displayed with a check mark.
450          *
451          * @return true if the item is displayed with a check mark.
452          */
isChecked()453         public boolean isChecked() {
454             return mIsChecked;
455         }
456 
457         /**
458          * Two toggles are equal if they have the same id and title.
459          * It is acceptable for the checked state and action callback to be different.
460          */
461         @Override
equals(Object obj)462         public boolean equals(Object obj) {
463             return super.equals(obj);
464         }
465 
466         /**
467          * Two toggles have the same hash code if they have the same id and title.
468          */
469         @Override
hashCode()470         public int hashCode() {
471             return super.hashCode();
472         }
473     }
474 
475     /**
476      * An ordered list of choices the user can choose between. For choosing between
477      * actions, there is a {@link NestedAction} class.
478      */
479     public static class Choices extends RuleAction {
480         protected List<String> mTitles;
481         protected List<URL> mIconUrls;
482         protected List<String> mIds;
483         private boolean mRadio;
484 
485         /**
486          * One or more id for the checked choice(s) that will be check marked.
487          * Can be null. Can be an id not present in the choices map.
488          */
489         protected final String mCurrent;
490 
Choices(String id, String title, IMenuCallback callback, List<String> titles, List<URL> iconUrls, List<String> ids, String current, int sortPriority, boolean supportsMultipleNodes)491         private Choices(String id, String title, IMenuCallback callback,
492                 List<String> titles, List<URL> iconUrls, List<String> ids, String current,
493                 int sortPriority, boolean supportsMultipleNodes) {
494             super(id, title, callback, sortPriority, supportsMultipleNodes);
495             mTitles = titles;
496             mIconUrls = iconUrls;
497             mIds = ids;
498             mCurrent = current;
499         }
500 
501         /**
502          * Returns the list of urls to icons to display for each choice, or null
503          *
504          * @return the list of urls to icons to display for each choice, or null
505          */
getIconUrls()506         public List<URL> getIconUrls() {
507             return mIconUrls;
508         }
509 
510         /**
511          * Returns the list of ids for the menu choices, never null
512          *
513          * @return the list of ids for the menu choices, never null
514          */
getIds()515         public List<String> getIds() {
516             return mIds;
517         }
518 
519         /**
520          * Returns the titles to be displayed for the menu choices, never null
521          *
522          * @return the titles to be displayed for the menu choices, never null
523          */
getTitles()524         public List<String> getTitles() {
525             return mTitles;
526         }
527 
528         /**
529          * Returns the current value of the choice
530          *
531          * @return the current value of the choice, possibly null
532          */
getCurrent()533         public String getCurrent() {
534             return mCurrent;
535         }
536 
537         /**
538          * Set whether this choice list is best visualized as a radio group (instead of a
539          * dropdown)
540          *
541          * @param radio true if this choice list should be visualized as a radio group
542          */
setRadio(boolean radio)543         public void setRadio(boolean radio) {
544             mRadio = radio;
545         }
546 
547         /**
548          * Returns true if this choice list is best visualized as a radio group (instead
549          * of a dropdown)
550          *
551          * @return true if this choice list should be visualized as a radio group
552          */
isRadio()553         public boolean isRadio() {
554             return mRadio;
555         }
556     }
557 
558     /**
559      * An ordered list of actions the user can choose between. Similar to
560      * {@link Choices} but for actions instead.
561      */
562     public static class NestedAction extends RuleAction {
563         /** The provider to produce the list of nested actions when needed */
564         private final ActionProvider mProvider;
565 
NestedAction(String id, String title, ActionProvider provider, IMenuCallback callback, int sortPriority, boolean supportsMultipleNodes)566         private NestedAction(String id, String title, ActionProvider provider,
567                 IMenuCallback callback, int sortPriority,
568                 boolean supportsMultipleNodes) {
569             super(id, title, callback, sortPriority, supportsMultipleNodes);
570             mProvider = provider;
571         }
572 
573         /**
574          * Returns the nested actions available for the given node
575          *
576          * @param node the node to look up nested actions for
577          * @return a list of nested actions
578          */
getNestedActions(INode node)579         public List<RuleAction> getNestedActions(INode node) {
580             return mProvider.getNestedActions(node);
581         }
582     }
583 
584     /** Like {@link Choices}, but the set of choices is computed lazily */
585     private static class DelayedChoices extends Choices {
586         private final ChoiceProvider mProvider;
587 
DelayedChoices(String id, String title, IMenuCallback callback, String current, ChoiceProvider provider, int sortPriority, boolean supportsMultipleNodes)588         private DelayedChoices(String id, String title,
589                 IMenuCallback callback, String current, ChoiceProvider provider,
590                 int sortPriority, boolean supportsMultipleNodes) {
591             super(id, title, callback, null, null, null, current, sortPriority,
592                     supportsMultipleNodes);
593             mProvider = provider;
594         }
595 
ensureInitialized()596         private void ensureInitialized() {
597             if (mTitles == null) {
598                 mTitles = new ArrayList<String>();
599                 mIconUrls = new ArrayList<URL>();
600                 mIds = new ArrayList<String>();
601 
602                 mProvider.addChoices(mTitles, mIconUrls, mIds);
603             }
604         }
605 
606         @Override
getIconUrls()607         public List<URL> getIconUrls() {
608             ensureInitialized();
609             return mIconUrls;
610         }
611 
612         @Override
getIds()613         public List<String> getIds() {
614             ensureInitialized();
615             return mIds;
616         }
617 
618         @Override
getTitles()619         public List<String> getTitles() {
620             ensureInitialized();
621             return mTitles;
622         }
623     }
624 
625     /**
626      * Provides the set of nested action choices associated with a {@link NestedAction}
627      * object when they are needed. Useful for lazy initialization of context
628      * menus and popup menus until they are actually needed.
629      */
630     public interface ActionProvider {
631         /**
632          * Returns the nested actions available for the given node
633          *
634          * @param node the node to look up nested actions for
635          * @return a list of nested actions
636          */
getNestedActions(INode node)637         public List<RuleAction> getNestedActions(INode node);
638     }
639 
640     /**
641      * Provides the set of choices associated with an {@link Choices}
642      * object when they are needed. Useful for lazy initialization of context
643      * menus and popup menus until they are actually needed.
644      */
645     public interface ChoiceProvider {
646         /**
647          * Adds in the needed titles, iconUrls (if any) and ids.
648          * Use {@link RuleAction#SEPARATOR} to create separators.
649          *
650          * @param titles a list of titles that the provider should append to
651          * @param iconUrls a list of icon URLs that the provider should append to
652          * @param ids a list of ids that the provider should append to
653          */
addChoices(List<String> titles, List<URL> iconUrls, List<String> ids)654         public void addChoices(List<String> titles, List<URL> iconUrls, List<String> ids);
655     }
656 }
657