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