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