diff options
author | Tor Norbye <tnorbye@google.com> | 2011-08-09 15:58:34 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2011-08-15 09:39:51 -0700 |
commit | 94986e745141118cace0391da1b4dc8533408751 (patch) | |
tree | 00edab058574d09cde76fe6da539b5da220a378d /eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api | |
parent | a0e782572d9e5d31b83f8053b4425131196f57e2 (diff) | |
download | sdk-94986e745141118cace0391da1b4dc8533408751.zip sdk-94986e745141118cace0391da1b4dc8533408751.tar.gz sdk-94986e745141118cace0391da1b4dc8533408751.tar.bz2 |
Action API improvements
This changeset changes the Actions mechanism for view rules to add
support for the following:
* Delayed computation of submenu contents. Before this, a view rule
would have to produce the full tree of actions to be shown in menus
and submenus - for example including all the properties, and in turn
all the enumerated values for those properties and so on. Now
there's a Provider interface which can be used to compute these menu
items only when the menu is actually opened. The properties menu now
takes advantage of this.
This was also necessary to implement the following new feature:
* The layout editor context menu now also lists not just the
properties for the currently selected views, but also the
properties for the parents. For example, if you open the context
menu, you'll see the properties for the button you just right
clicked on, but there will also be a "frameLayout1" submenu
containing the actions for the <FrameLayout> parent of the button,
and a "linearLayout1" submenu for the parent linear layout. This
is useful when a parent layout doesn't have blank space on its own
so it is difficult to target.
A future CL will use the lazy initialization to add more options to
the properties menu.
* Support for arbitrary nesting. Submenus can contain submenus can
contain other submenus etc.
* Custom ordering. This changeset moves the "sort priority" concept
(which was already used for layout actions) up to all actions, which
makes it easier for rules to cooperate on ordering because instead
of appending or prepending to the superclass' context menu result,
actions can now just be initialized with a sorting priority value
which makes it trivial to interleave actions regardless of who adds
them. This also makes it a lot easier to use custom ordering in
choice menus where the ordering used to be alphabetically sorting on
keys.
* Improved support for multiselection. The callback interface now
takes a list of nodes to apply the callback to, and actions can
indicate whether they support multiple nodes. This makes it possible
for actions to more directly support the case where you apply an
action to multiple nodes. As before, the available actions in the
context menu is limited such that it only shows the actions common
to all. But now those actions can do something specific. For
example, if you select "Edit Text..." on many nodes, you will get
the input-string dialog once, and then the value is applied to
all. Similarly, if you select "Edit Id..." it will ask for a
separate id for each value (and you can cancel out of this loop).
There are various API changes too. Since the Choices action (which had
a map-based set of values) was removed, the OrderedChoices is now
renamed Choices. The Actions subclass of MenuAction which all actions
also extended has simply been moved up to the top level MenuAction.
And MenuAction has been renamed to RuleActions since they are used
not just for menus but for toolbars etc and the key thing about this
interface is that they are intended for use by rules.
Change-Id: If49f75213f2041ebfef7e84254d70d219bb766ab
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api')
4 files changed, 681 insertions, 684 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IMenuCallback.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IMenuCallback.java index 1906436..80f77b8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IMenuCallback.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IMenuCallback.java @@ -16,9 +16,13 @@ package com.android.ide.common.api; +import java.util.List; + /** - * Callback interface for {@link MenuAction}s. The callback performs the actual - * work of the menu. + * Callback interface for a {@link RuleAction}. The callback performs the actual + * work of the action, and this level of indirection allows multiple actions (which + * typically do not have their own class, only their own instances) to share a single + * implementation callback class. * <p> * <b>NOTE: This is not a public or final API; if you rely on this be prepared * to adjust your code for the next tools release.</b> @@ -26,14 +30,15 @@ package com.android.ide.common.api; */ public interface IMenuCallback { /** - * Performs the actual work promised by the {@link MenuAction}. - * - * @param action The MenuAction being applied. + * Performs the actual work promised by the {@link RuleAction}. + * @param action The action being applied. + * @param selectedNodes The nodes to apply the action to * @param valueId For a Choices action, the string id of the selected choice * @param newValue For a toggle or for a flag, true if the item is being * checked, false if being unchecked. For enums this is not * useful; however for flags it allows one to add or remove items * to the flag's choices. */ - void action(MenuAction menuAction, String valueId, Boolean newValue); + void action(RuleAction action, List<? extends INode> selectedNodes, String valueId, + Boolean newValue); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java index a16db28..d29ef71 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java @@ -67,19 +67,22 @@ public interface IViewRule { * If null is returned, the GLE will automatically shorten the class name using its * own heuristic, which is to keep the first 2 package components and the class name. * The class name is the <code>fqcn</code> argument that was given - * to {@link #onInitialize(String)}. + * to {@link #onInitialize(String,IClientRulesEngine)}. * * @return Null for the default behavior or a shortened string. */ String getDisplayName(); /** - * Invoked by the Rules Engine to retrieve a set of actions to customize + * Invoked by the Rules Engine to produce a set of actions to customize * the context menu displayed for this view. The result is not cached and the * method is invoked every time the context menu is about to be shown. + * <p> + * The order of the menu items is determined by the sort priority set on + * the actions. * <p/> - * Most rules should consider returning <code>super.getContextMenu(node)</code> - * and appending their own custom menu actions, if any. + * Most rules should consider calling super.{@link #addContextMenuActions(List, INode)} + * as well. * <p/> * Menu actions are either toggles or fixed lists with one currently-selected * item. It is expected that the rule will need to recreate the actions with @@ -87,16 +90,18 @@ public interface IViewRule { * is not cached. However rules are encouraged to cache some or all of the result * to speed up following calls if it makes sense. * - * @return Null for no context menu, or a new {@link MenuAction} describing one - * or more actions to display in the context menu. + * @param actions a list of actions to add new context menu actions into. The order + * of the actions in this list is not important; it will be sorted by + * {@link RuleAction#getSortPriority()} later. + * @param node the node to add actions for. */ - List<MenuAction> getContextMenu(INode node); + void addContextMenuActions(List<RuleAction> actions, INode node); /** * Invoked by the Rules Engine to ask the parent layout for the set of layout actions * to display in the layout bar. The layout rule should add these into the provided * list. The order the items are added in does not matter; the - * {@link MenuAction#getSortPriority()} values will be used to sort the actions prior + * {@link RuleAction#getSortPriority()} values will be used to sort the actions prior * to display, which makes it easier for parent rules and deriving rules to interleave * their respective actions. * @@ -104,7 +109,7 @@ public interface IViewRule { * @param parentNode the parent of the selection, or the selection itself if the root * @param targets the targeted/selected nodes, if any */ - void addLayoutActions(List<MenuAction> actions, + void addLayoutActions(List<RuleAction> actions, INode parentNode, List<? extends INode> targets); // ==== Selection ==== diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java deleted file mode 100755 index 3e912f8..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java +++ /dev/null @@ -1,669 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.android.ide.common.api; - -import com.android.annotations.Nullable; - -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * A menu action represents one item in the context menu displayed by the GLE canvas. - * <p/> - * Each action should have a reasonably unique ID. By default actions are stored using - * the lexicographical order of the IDs. - * Duplicated IDs will be ignored -- that is the first one found will be used. - * <p/> - * When the canvas has a multiple selection, only actions that are present in <em>all</em> - * the selected nodes are shown. Moreover, for a given ID all actions must be equal, for - * example they must have the same title and choice but not necessarily the same selection. <br/> - * This allows the canvas to only display compatible actions that will work on all selected - * elements. - * <p/> - * Actions can be grouped in sub-menus if necessary. Whether groups (sub-menus) can contain - * other groups is implementation dependent. Currently the canvas does not support this, but - * we may decide to change this behavior later if deemed useful. - * <p/> - * All actions and groups are sorted by their ID, using String's natural sorting order. - * The only way to change this sorting is by choosing the IDs so they the result end up - * sorted as you want it. - * <p/> - * The {@link MenuAction} is abstract. Users should instantiate either {@link Toggle}, - * {@link Choices} or {@link Group} instead. These classes are immutable. - * <p> - * <b>NOTE: This is not a public or final API; if you rely on this be prepared - * to adjust your code for the next tools release.</b> - * </p> - */ -public abstract class MenuAction implements Comparable<MenuAction> { - - /** - * The unique id of the action. - * @see #getId() - */ - private final String mId; - /** - * The UI-visible title of the action. - */ - private final String mTitle; - - /** A URL pointing to an icon, or null */ - private URL mIconUrl; - - /** - * The sorting priority of this item; actions can be sorted according to these - */ - protected int mSortPriority; - - // Factories - - public static MenuAction createSeparator() { - return new Separator(sNextSortPriority++); - } - - public static MenuAction createSeparator(int sortPriority) { - return new Separator(sortPriority); - } - - - public static MenuAction createAction(String id, String title, String groupId, - IMenuCallback callback) { - MenuAction.Action action = new MenuAction.Action(id, title, groupId, callback); - action.setSortPriority(sNextSortPriority++); - return action; - } - - public static MenuAction createAction(String id, String title, String groupId, - IMenuCallback callback, URL iconUrl, int sortPriority) { - MenuAction action = new MenuAction.Action(id, title, groupId, callback); - action.setIconUrl(iconUrl); - action.setSortPriority(sortPriority); - return action; - } - - public static MenuAction createToggle(String id, String title, boolean isChecked, - IMenuCallback callback) { - Toggle action = new Toggle(id, title, isChecked, callback); - action.setSortPriority(sNextSortPriority++); - return action; - } - - public static MenuAction createToggle(String id, String title, boolean isChecked, - IMenuCallback callback, URL iconUrl, int sortPriority) { - Toggle toggle = new Toggle(id, title, isChecked, callback); - toggle.setIconUrl(iconUrl); - toggle.setSortPriority(sortPriority); - return toggle; - } - - public static MenuAction createChoices(String id, String title, String groupId, - IMenuCallback callback, List<String> titles, List<URL> iconUrls, List<String> ids, - String current) { - OrderedChoices action = new OrderedChoices(id, title, groupId, callback, titles, iconUrls, - ids, current); - action.setSortPriority(sNextSortPriority++); - return action; - } - - public static OrderedChoices createChoices(String id, String title, String groupId, - IMenuCallback callback, List<String> titles, List<URL> iconUrls, List<String> ids, - String current, URL iconUrl, int sortPriority) { - OrderedChoices choices = new OrderedChoices(id, title, groupId, callback, titles, iconUrls, - ids, current); - choices.setIconUrl(iconUrl); - choices.setSortPriority(sortPriority); - return choices; - } - - public static OrderedChoices createChoices(String id, String title, String groupId, - IMenuCallback callback, ChoiceProvider provider, - String current, URL iconUrl, int sortPriority) { - OrderedChoices choices = new DelayedOrderedChoices(id, title, groupId, callback, - current, provider); - choices.setIconUrl(iconUrl); - choices.setSortPriority(sortPriority); - return choices; - } - - /** - * Creates a new {@link MenuAction} with the given id and the given title. - * Actions which have the same id and the same title are deemed equivalent. - * - * @param id The unique id of the action, which must be similar for all actions that - * perform the same task. Cannot be null. - * @param title The UI-visible title of the action. - */ - private MenuAction(String id, String title) { - mId = id; - mTitle = title; - } - - /** - * Returns the unique id of the action. In the context of a multiple selection, - * actions which have the same id are collapsed together and must represent the same - * action. Cannot be null. - */ - public String getId() { - return mId; - } - - /** - * Returns the UI-visible title of the action, shown in the context menu. - * Cannot be null. - */ - public String getTitle() { - return mTitle; - } - - /** - * Actions which have the same id and the same title are deemed equivalent. - */ - @Override - public boolean equals(Object obj) { - if (obj instanceof MenuAction) { - MenuAction rhs = (MenuAction) obj; - - if (mId != rhs.mId && !(mId != null && mId.equals(rhs.mId))) return false; - if (mTitle != rhs.mTitle && - !(mTitle != null && mTitle.equals(rhs.mTitle))) return false; - return true; - } - return false; - } - - /** - * Actions which have the same id and the same title have the same hash code. - */ - @Override - public int hashCode() { - int h = mId == null ? 0 : mId.hashCode(); - h = h ^ (mTitle == null ? 0 : mTitle.hashCode()); - return h; - } - - /** - * Gets a URL pointing to an icon to use for this action, if any. - * - * @return a URL pointing to an icon to use for this action, or null - */ - public URL getIconUrl() { - return mIconUrl; - } - - /** - * Sets a URL pointing to an icon to use for this action, if any. - * - * @param iconUrl a URL pointing to an icon to use for this action, or null - */ - public void setIconUrl(URL iconUrl) { - mIconUrl = iconUrl; - } - - /** - * Sets a priority used for sorting this action - * - * @param sortPriority a priority used for sorting this action - */ - public void setSortPriority(int sortPriority) { - mSortPriority = sortPriority; - } - - private static int sNextSortPriority = 0; - - /** - * Return a priority used for sorting this action - * - * @return a priority used for sorting this action - */ - public int getSortPriority() { - return mSortPriority; - } - - // Implements Comparable<MenuAciton> - public int compareTo(MenuAction other) { - if (mSortPriority != other.mSortPriority) { - return mSortPriority - other.mSortPriority; - } - - return mTitle.compareTo(other.mTitle); - } - - /** - * A group of actions, displayed in a sub-menu. - * <p/> - * Note that group can be seen as a "group declaration": the group does not hold a list - * actions that it will contain. This merely let the canvas create a sub-menu with the - * given title and actions that define this group-id will be placed in the sub-menu. - * <p/> - * The current canvas has the following implementation details: <br/> - * - There's only one level of sub-menu. - * That is you can't have a sub-menu inside another sub-menu. - * This is expressed by the fact that groups do not have a parent group-id. <br/> - * - It is not currently necessary to define a group before defining actions that refer - * to that group. Moreover, in the context of a multiple selection, one view could - * contribute actions to any group even if created by another view. Both practices - * are discouraged. <br/> - * - Actions which group-id do not match any known group will simply be placed in the - * root context menu. <br/> - * - Empty groups do not create visible sub-menus. <br/> - * These implementations details may change in the future and should not be relied upon. - */ - public static class Group extends MenuAction { - - /** - * Constructs a new group of actions. - * - * @param id The id of the group. Must be unique. Cannot be null. - * @param title The UI-visible title of the group, shown in the sub-menu. - */ - public Group(String id, String title) { - super(id, title); - } - } - - /** - * The base class for {@link Toggle} and {@link Choices}. - */ - public static class Action extends MenuAction { - - /** - * A callback executed when the action is selected in the context menu. - */ - private final IMenuCallback mCallback; - - /** - * An optional group id, to place the action in a given sub-menu. - * @null This value can be null. - */ - @Nullable - private final String mGroupId; - - /** - * Constructs a new base {@link MenuAction} with its ID, title and action callback. - * - * @param id The unique ID of the action. Must not be null. - * @param title The title of the action. Must not be null. - * @param groupId The optional group id, to place the action in a given sub-menu. - * Can be null. - * @param callback The callback executed when the action is selected. - * Must not be null. - */ - public Action(String id, String title, String groupId, IMenuCallback callback) { - super(id, title); - mGroupId = groupId; - mCallback = callback; - } - - /** - * Returns the callback executed when the action is selected in the - * context menu. Cannot be null. - */ - public IMenuCallback getCallback() { - return mCallback; - } - - /** - * Returns the optional id of an existing group or null - * @null This value can be null. - */ - @Nullable - public String getGroupId() { - return mGroupId; - } - - /** - * Two actions are equal if the have the same id, title and group-id. - */ - @Override - public boolean equals(Object obj) { - if (obj instanceof Action && super.equals(obj)) { - Action rhs = (Action) obj; - return mGroupId == rhs.mGroupId || - (mGroupId != null && mGroupId.equals(rhs.mGroupId)); - } - return false; - } - - /** - * Two actions have the same hash code if the have the same id, title and group-id. - */ - @Override - public int hashCode() { - int h = super.hashCode(); - h = h ^ (mGroupId == null ? 0 : mGroupId.hashCode()); - return h; - } - } - - /** A separator to display between actions */ - public static class Separator extends MenuAction { - /** Construct using the factory {@link #createSeparator(int)} */ - private Separator(int sortPriority) { - super("_separator", ""); //$NON-NLS-1$ //$NON-NLS-2$ - mSortPriority = sortPriority; - } - } - - /** - * A toggle is a simple on/off action, displayed as an item in a context menu - * with a check mark if the item is checked. - * <p/> - * Two toggles are equal if they have the same id, title and group-id. - * It is expected for the checked state and action callback to be different. - */ - public static class Toggle extends Action { - /** - * True if the item is displayed with a check mark. - */ - private final boolean mIsChecked; - - /** - * Creates a new immutable toggle action. - * This action has no group-id and will show up in the root of the context menu. - * - * @param id The unique id of the action. Cannot be null. - * @param title The UI-visible title of the context menu item. Cannot be null. - * @param isChecked Whether the context menu item has a check mark. - * @param callback A callback to execute when the context menu item is - * selected. - */ - public Toggle(String id, String title, boolean isChecked, IMenuCallback callback) { - this(id, title, isChecked, null /* group-id */, callback); - } - - /** - * Creates a new immutable toggle action. - * - * @param id The unique id of the action. Cannot be null. - * @param title The UI-visible title of the context menu item. Cannot be null. - * @param isChecked Whether the context menu item has a check mark. - * @param groupId The optional group id, to place the action in a given sub-menu. - * Can be null. - * @param callback A callback to execute when the context menu item is - * selected. - */ - public Toggle(String id, String title, boolean isChecked, String groupId, - IMenuCallback callback) { - super(id, title, groupId, callback); - mIsChecked = isChecked; - } - - /** - * Returns true if the item is displayed with a check mark. - */ - public boolean isChecked() { - return mIsChecked; - } - - /** - * Two toggles are equal if they have the same id, title and group-id. - * It is acceptable for the checked state and action callback to be different. - */ - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - - /** - * Two toggles have the same hash code if they have the same id, title and group-id. - */ - @Override - public int hashCode() { - return super.hashCode(); - } - } - - /** - * Like {@link Choices}, but with an explicit ordering among the children, and with - * optional icons on each child choice - */ - public static class OrderedChoices extends Action { - protected List<String> mTitles; - protected List<URL> mIconUrls; - protected List<String> mIds; - private boolean mRadio; - - /** - * One or more id for the checked choice(s) that will be check marked. - * Can be null. Can be an id not present in the choices map. - */ - protected final String mCurrent; - - public OrderedChoices(String id, String title, String groupId, IMenuCallback callback, - List<String> titles, List<URL> iconUrls, List<String> ids, String current) { - super(id, title, groupId, callback); - mTitles = titles; - mIconUrls = iconUrls; - mIds = ids; - mCurrent = current; - } - - public List<URL> getIconUrls() { - return mIconUrls; - } - - public List<String> getIds() { - return mIds; - } - - public List<String> getTitles() { - return mTitles; - } - - public String getCurrent() { - return mCurrent; - } - - /** - * Set whether this choice list is best visualized as a radio group (instead of a - * dropdown) - * - * @param radio true if this choice list should be visualized as a radio group - */ - public void setRadio(boolean radio) { - mRadio = radio; - } - - /** - * Returns true if this choice list is best visualized as a radio group (instead - * of a dropdown) - * - * @return true if this choice list should be visualized as a radio group - */ - public boolean isRadio() { - return mRadio; - } - } - - /** Provides the set of choices associated with an {@link OrderedChoices} object - when they are needed. Useful for lazy initialization of context menus and popup menus - until they are actually needed. */ - public interface ChoiceProvider { - /** - * Adds in the needed titles, iconUrls (if any) and ids. - * - * @param titles a list of titles that the provider should append to - * @param iconUrls a list of icon URLs that the provider should append to - * @param ids a list of ids that the provider should append to - */ - public void addChoices(List<String> titles, List<URL> iconUrls, List<String> ids); - } - - /** Like {@link OrderedChoices}, but the set of choices is computed lazily */ - private static class DelayedOrderedChoices extends OrderedChoices { - private final ChoiceProvider mProvider; - - public DelayedOrderedChoices(String id, String title, String groupId, - IMenuCallback callback, String current, ChoiceProvider provider) { - super(id, title, groupId, callback, null, null, null, current); - mProvider = provider; - } - - private void ensureInitialized() { - if (mTitles == null) { - mTitles = new ArrayList<String>(); - mIconUrls = new ArrayList<URL>(); - mIds = new ArrayList<String>(); - - mProvider.addChoices(mTitles, mIconUrls, mIds); - } - } - - @Override - public List<URL> getIconUrls() { - ensureInitialized(); - return mIconUrls; - } - - @Override - public List<String> getIds() { - ensureInitialized(); - return mIds; - } - - @Override - public List<String> getTitles() { - ensureInitialized(); - return mTitles; - } - } - - /** - * A "choices" is a one-out-of-many-choices action, displayed as a sub-menu with one or more - * items, with either zero or more of them being checked. - * <p/> - * Implementation detail: empty choices will not be displayed in the context menu. - * <p/> - * Choice items are sorted by their id, using String's natural sorting order. - * <p/> - * Two multiple choices are equal if they have the same id, title, group-id and choices. - * It is expected for the current state and action callback to be different. - */ - public static class Choices extends Action { - - /** - * Special value which will insert a separator in the choices' submenu. - */ - public final static String SEPARATOR = "----"; - - /** - * Character used to split multiple checked choices, see {@link #getCurrent()}. - * The pipe character "|" is used, to natively match Android resource flag separators. - */ - public final static String CHOICE_SEP = "|"; - - /** - * A non-null map of id=>choice-title. The map could be empty but not null. - */ - private final Map<String, String> mChoices; - /** - * One or more id for the checked choice(s) that will be check marked. - * Can be null. Can be an id not present in the choices map. - * If more than one choice, they must be separated by {@link #CHOICE_SEP}. - */ - private final String mCurrent; - - /** - * Creates a new immutable multiple-choice action. - * This action has no group-id and will show up in the root of the context menu. - * - * @param id The unique id of the action. Cannot be null. - * @param title The UI-visible title of the context menu item. Cannot be null. - * @param choices A map id=>title for all the multiple-choice items. Cannot be null. - * @param current The id(s) of the current choice(s) that will be check marked. - * Can be null. Can be an id not present in the choices map. - * There can be more than one id separated by {@link #CHOICE_SEP}. - * @param callback A callback to execute when the context menu item is - * selected. - */ - public Choices(String id, String title, - Map<String, String> choices, - String current, - IMenuCallback callback) { - this(id, title, choices, current, null /* group-id */, callback); - } - - /** - * Creates a new immutable multiple-choice action. - * - * @param id The unique id of the action. Cannot be null. - * @param title The UI-visible title of the context menu item. Cannot be null. - * @param choices A map id=>title for all the multiple-choice items. Cannot be null. - * @param current The id(s) of the current choice(s) that will be check marked. - * Can be null. Can be an id not present in the choices map. - * There can be more than one id separated by {@link #CHOICE_SEP}. - * @param groupId The optional group id, to place the action in a given sub-menu. - * Can be null. - * @param callback A callback to execute when the context menu item is - * selected. - */ - public Choices(String id, String title, - Map<String, String> choices, - String current, - String groupId, - IMenuCallback callback) { - super(id, title, groupId, callback); - mChoices = choices; - mCurrent = current; - } - - /** - * Return the map of id=>choice-title. The map could be empty but not null. - */ - public Map<String, String> getChoices() { - return mChoices; - } - - /** - * Returns the id(s) of the current choice(s) that are check marked. - * Can be null. Can be an id not present in the choices map. - * There can be more than one id separated by {@link #CHOICE_SEP}. - */ - public String getCurrent() { - return mCurrent; - } - - /** - * Two multiple choices are equal if they have the same id, title, group-id and choices. - * It is acceptable for the current state and action callback to be different. - */ - @Override - public boolean equals(Object obj) { - if (obj instanceof Choices && super.equals(obj)) { - Choices rhs = (Choices) obj; - return mChoices.equals(rhs.mChoices); - } - return false; - } - - /** - * Two multiple choices have the same hash code if they have the same id, title, - * group-id and choices. - */ - @Override - public int hashCode() { - int h = super.hashCode(); - - if (mChoices != null) { - h = h ^ mChoices.hashCode(); - } - return h; - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/RuleAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/RuleAction.java new file mode 100755 index 0000000..2ebab36 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/RuleAction.java @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.common.api; + +import com.android.util.Pair; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * A {@link RuleAction} represents an action provided by an {@link IViewRule}, typically + * shown in a context menu or in the layout actions bar. + * <p/> + * Each action should have a reasonably unique ID. This is used when multiple nodes + * are selected to filter the actions down to just those actions that are supported + * across all selected nodes. If an action does not support multiple nodes, it can + * return false from {@link #supportsMultipleNodes()}. + * <p/> + * Actions can be grouped into a hierarchy of sub-menus using the {@link NestedAction} class, + * or into a flat submenu using the {@link Choices} class. + * <p/> + * Actions (including separators) all have a "sort priority", and this is used to + * sort the menu items or toolbar buttons into a specific order. + * <p> + * <b>NOTE: This is not a public or final API; if you rely on this be prepared + * to adjust your code for the next tools release.</b> + * </p> + */ +public class RuleAction implements Comparable<RuleAction> { + /** + * Character used to split multiple checked choices. + * The pipe character "|" is used, to natively match Android resource flag separators. + */ + public final static String CHOICE_SEP = "|"; //$NON-NLS-1$ + + /** + * Same as {@link #CHOICE_SEP} but safe for use in regular expressions. + */ + public final static String CHOICE_SEP_PATTERN = Pattern.quote(CHOICE_SEP); + + /** + * The unique id of the action. + * @see #getId() + */ + private final String mId; + /** + * The UI-visible title of the action. + */ + private final String mTitle; + + /** A URL pointing to an icon, or null */ + private URL mIconUrl; + + /** + * A callback executed when the action is selected in the context menu. + */ + private final IMenuCallback mCallback; + + /** + * The sorting priority of this item; actions can be sorted according to these + */ + protected final int mSortPriority; + + /** + * Whether this action supports multiple nodes, see + * {@link #supportsMultipleNodes()} for details. + */ + private final boolean mSupportsMultipleNodes; + + /** + * Special value which will insert a separator in the choices' submenu. + */ + public final static String SEPARATOR = "----"; + + // Factories + + /** + * Constructs a new separator which will be shown in places where separators + * are supported such as context menus + * + * @param sortPriority a priority used for sorting this action + * @return a new separator + */ + public static Separator createSeparator(int sortPriority) { + return new Separator(sortPriority, true /* supportsMultipleNodes*/); + } + + /** + * Constructs a new base {@link RuleAction} with its ID, title and action callback. + * + * @param id The unique ID of the action. Must not be null. + * @param title The title of the action. Must not be null. + * @param callback The callback executed when the action is selected. + * Must not be null. + * @param iconUrl a URL pointing to an icon to use for this action, or null + * @param sortPriority a priority used for sorting this action + * @param supportsMultipleNodes whether this action supports multiple nodes, + * see {@link #supportsMultipleNodes()} for details + * @return the new {@link RuleAction} + */ + public static RuleAction createAction(String id, String title, + IMenuCallback callback, URL iconUrl, int sortPriority, boolean supportsMultipleNodes) { + RuleAction action = new RuleAction(id, title, callback, sortPriority, + supportsMultipleNodes); + action.setIconUrl(iconUrl); + + return action; + } + + /** + * Creates a new immutable toggle action. + * + * @param id The unique id of the action. Cannot be null. + * @param title The UI-visible title of the context menu item. Cannot be null. + * @param isChecked Whether the context menu item has a check mark. + * @param callback A callback to execute when the context menu item is + * selected. + * @param iconUrl a URL pointing to an icon to use for this action, or null + * @param sortPriority a priority used for sorting this action + * @param supportsMultipleNodes whether this action supports multiple nodes, + * see {@link #supportsMultipleNodes()} for details + * @return the new {@link Toggle} + */ + public static Toggle createToggle(String id, String title, boolean isChecked, + IMenuCallback callback, URL iconUrl, int sortPriority, + boolean supportsMultipleNodes) { + Toggle toggle = new Toggle(id, title, isChecked, callback, sortPriority, + supportsMultipleNodes); + toggle.setIconUrl(iconUrl); + return toggle; + } + + /** + * Creates a new immutable multiple-choice action with a defined ordered set + * of action children. + * + * @param id The unique id of the action. Cannot be null. + * @param title The title of the action to be displayed to the user + * @param provider Provides the actions to be shown as children of this + * action + * @param callback A callback to execute when the context menu item is + * selected. + * @param iconUrl the icon to use for the multiple choice action itself + * @param sortPriority the sorting priority to use for the multiple choice + * action itself + * @param supportsMultipleNodes whether this action supports multiple nodes, + * see {@link #supportsMultipleNodes()} for details + * @return the new {@link NestedAction} + */ + public static NestedAction createChoices(String id, String title, + IMenuCallback callback, URL iconUrl, + int sortPriority, boolean supportsMultipleNodes, ActionProvider provider) { + NestedAction choices = new NestedAction(id, title, provider, callback, + sortPriority, supportsMultipleNodes); + choices.setIconUrl(iconUrl); + return choices; + } + + /** + * Creates a new immutable multiple-choice action with a defined ordered set + * of children. + * + * @param id The unique id of the action. Cannot be null. + * @param title The title of the action to be displayed to the user + * @param iconUrls The icon urls for the children items (may be null) + * @param ids The internal ids for the children + * @param current The id(s) of the current choice(s) that will be check + * marked. Can be null. Can be an id not present in the choices + * map. There can be more than one id separated by + * {@link #CHOICE_SEP}. + * @param callback A callback to execute when the context menu item is + * selected. + * @param titles The UI-visible titles of the children + * @param iconUrl the icon to use for the multiple choice action itself + * @param sortPriority the sorting priority to use for the multiple choice + * action itself + * @param supportsMultipleNodes whether this action supports multiple nodes, + * see {@link #supportsMultipleNodes()} for details + * @return the new {@link Choices} + */ + public static Choices createChoices(String id, String title, + IMenuCallback callback, List<String> titles, List<URL> iconUrls, List<String> ids, + String current, URL iconUrl, int sortPriority, boolean supportsMultipleNodes) { + Choices choices = new Choices(id, title, callback, titles, iconUrls, + ids, current, sortPriority, supportsMultipleNodes); + choices.setIconUrl(iconUrl); + + return choices; + } + + /** + * Creates a new immutable multiple-choice action with a defined ordered set + * of children. + * + * @param id The unique id of the action. Cannot be null. + * @param title The title of the action to be displayed to the user + * @param iconUrls The icon urls for the children items (may be null) + * @param current The id(s) of the current choice(s) that will be check + * marked. Can be null. Can be an id not present in the choices + * map. There can be more than one id separated by + * {@link #CHOICE_SEP}. + * @param callback A callback to execute when the context menu item is + * selected. + * @param iconUrl the icon to use for the multiple choice action itself + * @param sortPriority the sorting priority to use for the multiple choice + * action itself + * @param supportsMultipleNodes whether this action supports multiple nodes, + * see {@link #supportsMultipleNodes()} for details + * @param idsAndTitles a list of pairs (of ids and titles) to use for the + * menu items + * @return the new {@link Choices} + */ + public static Choices createChoices(String id, String title, + IMenuCallback callback, List<URL> iconUrls, + String current, URL iconUrl, int sortPriority, + boolean supportsMultipleNodes, List<Pair<String, String>> idsAndTitles) { + int itemCount = idsAndTitles.size(); + List<String> titles = new ArrayList<String>(itemCount); + List<String> ids = new ArrayList<String>(itemCount); + for (Pair<String, String> pair : idsAndTitles) { + ids.add(pair.getFirst()); + titles.add(pair.getSecond()); + } + Choices choices = new Choices(id, title, callback, titles, iconUrls, + ids, current, sortPriority, supportsMultipleNodes); + choices.setIconUrl(iconUrl); + return choices; + } + + /** + * Creates a new immutable multiple-choice action with lazily computed children. + * + * @param id The unique id of the action. Cannot be null. + * @param title The title of the multiple-choice itself + * @param callback A callback to execute when the context menu item is + * selected. + * @param provider the provider which provides choices lazily + * @param current The id(s) of the current choice(s) that will be check + * marked. Can be null. Can be an id not present in the choice + * alternatives. There can be more than one id separated by + * {@link #CHOICE_SEP}. + * @param iconUrl the icon to use for the multiple choice action itself + * @param sortPriority the sorting priority to use for the multiple choice + * action itself + * @param supportsMultipleNodes whether this action supports multiple nodes, + * see {@link #supportsMultipleNodes()} for details + * @return the new {@link Choices} + */ + public static Choices createChoices(String id, String title, + IMenuCallback callback, ChoiceProvider provider, + String current, URL iconUrl, int sortPriority, boolean supportsMultipleNodes) { + Choices choices = new DelayedChoices(id, title, callback, + current, provider, sortPriority, supportsMultipleNodes); + choices.setIconUrl(iconUrl); + return choices; + } + + /** + * Creates a new {@link RuleAction} with the given id and the given title. + * Actions which have the same id and the same title are deemed equivalent. + * + * @param id The unique id of the action, which must be similar for all actions that + * perform the same task. Cannot be null. + * @param title The UI-visible title of the action. + * @param callback A callback to execute when the context menu item is + * selected. + * @param sortPriority a priority used for sorting this action + * @param supportsMultipleNodes the new return value for + * {@link #supportsMultipleNodes()} + */ + private RuleAction(String id, String title, IMenuCallback callback, int sortPriority, + boolean supportsMultipleNodes) { + mId = id; + mTitle = title; + mSortPriority = sortPriority; + mSupportsMultipleNodes = supportsMultipleNodes; + mCallback = callback; + } + + /** + * Returns the unique id of the action. In the context of a multiple selection, + * actions which have the same id are collapsed together and must represent the same + * action. Cannot be null. + * + * @return the unique id of the action, never null + */ + public String getId() { + return mId; + } + + /** + * Returns the UI-visible title of the action, shown in the context menu. + * Cannot be null. + * + * @return the user name of the action, never null + */ + public String getTitle() { + return mTitle; + } + + /** + * Actions which have the same id and the same title are deemed equivalent. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof RuleAction) { + RuleAction rhs = (RuleAction) obj; + + if (mId != rhs.mId && !(mId != null && mId.equals(rhs.mId))) return false; + if (mTitle != rhs.mTitle && + !(mTitle != null && mTitle.equals(rhs.mTitle))) return false; + return true; + } + return false; + } + + /** + * Whether this action supports multiple nodes. An action which supports + * multiple nodes can be applied to different nodes by passing in different + * nodes to its callback. Some actions are hardcoded for a specific node (typically + * one that isn't selected, such as an action which affects the parent of a selected + * node), and these actions will not be added to the context menu when more than + * one node is selected. + * + * @return true if this node supports multiple nodes + */ + public boolean supportsMultipleNodes() { + return mSupportsMultipleNodes; + } + + /** + * Actions which have the same id and the same title have the same hash code. + */ + @Override + public int hashCode() { + int h = mId == null ? 0 : mId.hashCode(); + h = h ^ (mTitle == null ? 0 : mTitle.hashCode()); + return h; + } + + /** + * Gets a URL pointing to an icon to use for this action, if any. + * + * @return a URL pointing to an icon to use for this action, or null + */ + public URL getIconUrl() { + return mIconUrl; + } + + /** + * Sets a URL pointing to an icon to use for this action, if any. + * + * @param iconUrl a URL pointing to an icon to use for this action, or null + * @return this action, to allow setter chaining + */ + public RuleAction setIconUrl(URL iconUrl) { + mIconUrl = iconUrl; + + return this; + } + + /** + * Return a priority used for sorting this action + * + * @return a priority used for sorting this action + */ + public int getSortPriority() { + return mSortPriority; + } + + /** + * Returns the callback executed when the action is selected in the + * context menu. Cannot be null. + * + * @return the callback, never null + */ + public IMenuCallback getCallback() { + return mCallback; + } + + // Implements Comparable<MenuAciton> + public int compareTo(RuleAction other) { + if (mSortPriority != other.mSortPriority) { + return mSortPriority - other.mSortPriority; + } + + return mTitle.compareTo(other.mTitle); + } + + @Override + public String toString() { + return "RuleAction [id=" + mId + ", title=" + mTitle + ", priority=" + mSortPriority + "]"; + } + + /** A separator to display between actions */ + public static class Separator extends RuleAction { + /** Construct using the factory {@link #createSeparator(int)} */ + private Separator(int sortPriority, boolean supportsMultipleNodes) { + super("_separator", "", null, sortPriority, //$NON-NLS-1$ //$NON-NLS-2$ + supportsMultipleNodes); + } + } + + /** + * A toggle is a simple on/off action, displayed as an item in a context menu + * with a check mark if the item is checked. + * <p/> + * Two toggles are equal if they have the same id, title and group-id. + * It is expected for the checked state and action callback to be different. + */ + public static class Toggle extends RuleAction { + /** + * True if the item is displayed with a check mark. + */ + private final boolean mIsChecked; + + /** + * Creates a new immutable toggle action. + * + * @param id The unique id of the action. Cannot be null. + * @param title The UI-visible title of the context menu item. Cannot be null. + * @param isChecked Whether the context menu item has a check mark. + * @param callback A callback to execute when the context menu item is + * selected. + */ + private Toggle(String id, String title, boolean isChecked, + IMenuCallback callback, int sortPriority, boolean supportsMultipleNodes) { + super(id, title, callback, sortPriority, supportsMultipleNodes); + mIsChecked = isChecked; + } + + /** + * Returns true if the item is displayed with a check mark. + * + * @return true if the item is displayed with a check mark. + */ + public boolean isChecked() { + return mIsChecked; + } + + /** + * Two toggles are equal if they have the same id and title. + * It is acceptable for the checked state and action callback to be different. + */ + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + /** + * Two toggles have the same hash code if they have the same id and title. + */ + @Override + public int hashCode() { + return super.hashCode(); + } + } + + /** + * An ordered list of choices the user can choose between. For choosing between + * actions, there is a {@link NestedAction} class. + */ + public static class Choices extends RuleAction { + protected List<String> mTitles; + protected List<URL> mIconUrls; + protected List<String> mIds; + private boolean mRadio; + + /** + * One or more id for the checked choice(s) that will be check marked. + * Can be null. Can be an id not present in the choices map. + */ + protected final String mCurrent; + + private Choices(String id, String title, IMenuCallback callback, + List<String> titles, List<URL> iconUrls, List<String> ids, String current, + int sortPriority, boolean supportsMultipleNodes) { + super(id, title, callback, sortPriority, supportsMultipleNodes); + mTitles = titles; + mIconUrls = iconUrls; + mIds = ids; + mCurrent = current; + } + + /** + * Returns the list of urls to icons to display for each choice, or null + * + * @return the list of urls to icons to display for each choice, or null + */ + public List<URL> getIconUrls() { + return mIconUrls; + } + + /** + * Returns the list of ids for the menu choices, never null + * + * @return the list of ids for the menu choices, never null + */ + public List<String> getIds() { + return mIds; + } + + /** + * Returns the titles to be displayed for the menu choices, never null + * + * @return the titles to be displayed for the menu choices, never null + */ + public List<String> getTitles() { + return mTitles; + } + + /** + * Returns the current value of the choice + * + * @return the current value of the choice, possibly null + */ + public String getCurrent() { + return mCurrent; + } + + /** + * Set whether this choice list is best visualized as a radio group (instead of a + * dropdown) + * + * @param radio true if this choice list should be visualized as a radio group + */ + public void setRadio(boolean radio) { + mRadio = radio; + } + + /** + * Returns true if this choice list is best visualized as a radio group (instead + * of a dropdown) + * + * @return true if this choice list should be visualized as a radio group + */ + public boolean isRadio() { + return mRadio; + } + } + + /** + * An ordered list of actions the user can choose between. Similar to + * {@link Choices} but for actions instead. + */ + public static class NestedAction extends RuleAction { + /** The provider to produce the list of nested actions when needed */ + private final ActionProvider mProvider; + + private NestedAction(String id, String title, ActionProvider provider, + IMenuCallback callback, int sortPriority, + boolean supportsMultipleNodes) { + super(id, title, callback, sortPriority, supportsMultipleNodes); + mProvider = provider; + } + + /** + * Returns the nested actions available for the given node + * + * @param node the node to look up nested actions for + * @return a list of nested actions + */ + public List<RuleAction> getNestedActions(INode node) { + return mProvider.getNestedActions(node); + } + } + + /** Like {@link Choices}, but the set of choices is computed lazily */ + private static class DelayedChoices extends Choices { + private final ChoiceProvider mProvider; + + private DelayedChoices(String id, String title, + IMenuCallback callback, String current, ChoiceProvider provider, + int sortPriority, boolean supportsMultipleNodes) { + super(id, title, callback, null, null, null, current, sortPriority, + supportsMultipleNodes); + mProvider = provider; + } + + private void ensureInitialized() { + if (mTitles == null) { + mTitles = new ArrayList<String>(); + mIconUrls = new ArrayList<URL>(); + mIds = new ArrayList<String>(); + + mProvider.addChoices(mTitles, mIconUrls, mIds); + } + } + + @Override + public List<URL> getIconUrls() { + ensureInitialized(); + return mIconUrls; + } + + @Override + public List<String> getIds() { + ensureInitialized(); + return mIds; + } + + @Override + public List<String> getTitles() { + ensureInitialized(); + return mTitles; + } + } + + /** + * Provides the set of nested action choices associated with a {@link NestedAction} + * object when they are needed. Useful for lazy initialization of context + * menus and popup menus until they are actually needed. + */ + public interface ActionProvider { + /** + * Returns the nested actions available for the given node + * + * @param node the node to look up nested actions for + * @return a list of nested actions + */ + public List<RuleAction> getNestedActions(INode node); + } + + /** + * Provides the set of choices associated with an {@link Choices} + * object when they are needed. Useful for lazy initialization of context + * menus and popup menus until they are actually needed. + */ + public interface ChoiceProvider { + /** + * Adds in the needed titles, iconUrls (if any) and ids. + * Use {@link RuleAction#SEPARATOR} to create separators. + * + * @param titles a list of titles that the provider should append to + * @param iconUrls a list of icon URLs that the provider should append to + * @param ids a list of ids that the provider should append to + */ + public void addChoices(List<String> titles, List<URL> iconUrls, List<String> ids); + } +} |