Home | History | Annotate | Download | only in api
      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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    393     public IMenuCallback getCallback() {
    394         return mCallback;
    395     }
    396 
    397     // Implements Comparable<MenuAciton>
    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
    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)} */
    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          */
    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          */
    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
    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
    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 
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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 
    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          */
    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 
    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 
    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
    607         public List<URL> getIconUrls() {
    608             ensureInitialized();
    609             return mIconUrls;
    610         }
    611 
    612         @Override
    613         public List<String> getIds() {
    614             ensureInitialized();
    615             return mIds;
    616         }
    617 
    618         @Override
    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          */
    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          */
    654         public void addChoices(List<String> titles, List<URL> iconUrls, List<String> ids);
    655     }
    656 }
    657