1 /* 2 * AppCommand.java 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 package org.rstudio.core.client.command; 16 17 import com.google.gwt.aria.client.MenuitemRole; 18 import com.google.gwt.aria.client.Roles; 19 import com.google.gwt.event.dom.client.ClickEvent; 20 import com.google.gwt.event.dom.client.ClickHandler; 21 import com.google.gwt.event.shared.HandlerManager; 22 import com.google.gwt.event.shared.HandlerRegistration; 23 import com.google.gwt.resources.client.ImageResource; 24 import com.google.gwt.safehtml.shared.SafeHtml; 25 import com.google.gwt.safehtml.shared.SafeHtmlBuilder; 26 import com.google.gwt.user.client.Command; 27 import com.google.gwt.user.client.ui.Image; 28 import com.google.gwt.user.client.ui.MenuItem; 29 30 import org.rstudio.core.client.ElementIds; 31 import org.rstudio.core.client.SafeHtmlUtil; 32 import org.rstudio.core.client.StringUtil; 33 import org.rstudio.core.client.command.impl.DesktopMenuCallback; 34 import org.rstudio.core.client.dom.DomUtils; 35 import org.rstudio.core.client.resources.ImageResource2x; 36 import org.rstudio.core.client.theme.res.ThemeResources; 37 import org.rstudio.core.client.theme.res.ThemeStyles; 38 import org.rstudio.core.client.widget.Toolbar; 39 import org.rstudio.core.client.widget.ToolbarButton; 40 import org.rstudio.studio.client.RStudio; 41 import org.rstudio.studio.client.RStudioGinjector; 42 import org.rstudio.studio.client.application.Desktop; 43 import org.rstudio.studio.client.application.ui.RStudioThemes; 44 import org.rstudio.studio.client.common.satellite.Satellite; 45 import org.rstudio.studio.client.common.satellite.SatelliteManager; 46 47 public class AppCommand implements Command, ClickHandler, ImageResourceProvider 48 { 49 private class CommandToolbarButton extends ToolbarButton implements 50 EnabledChangedHandler, VisibleChangedHandler 51 { CommandToolbarButton(String buttonLabel, String buttonTitle, ImageResourceProvider imageResourceProvider, AppCommand command, boolean synced)52 public CommandToolbarButton(String buttonLabel, String buttonTitle, 53 ImageResourceProvider imageResourceProvider, AppCommand command, 54 boolean synced) 55 { 56 super(buttonLabel, buttonTitle, imageResourceProvider, command); 57 command_ = command; 58 synced_ = synced; 59 } 60 61 @Override onAttach()62 protected void onAttach() 63 { 64 if (synced_) 65 { 66 setEnabled(command_.isEnabled()); 67 setVisible(command_.isVisible()); 68 handlerReg_ = command_.addEnabledChangedHandler(this); 69 handlerReg2_ = command_.addVisibleChangedHandler(this); 70 } 71 72 parentToolbar_ = getParentToolbar(); 73 74 super.onAttach(); 75 ElementIds.assignElementId(getElement(), "tb_" + ElementIds.idSafeString(getId())); 76 } 77 78 @Override onDetach()79 protected void onDetach() 80 { 81 super.onDetach(); 82 83 if (synced_) 84 { 85 handlerReg_.removeHandler(); 86 handlerReg2_.removeHandler(); 87 } 88 } 89 onEnabledChanged(EnabledChangedEvent event)90 public void onEnabledChanged(EnabledChangedEvent event) 91 { 92 setEnabled(command_.isEnabled()); 93 } 94 onVisibleChanged(VisibleChangedEvent event)95 public void onVisibleChanged(VisibleChangedEvent event) 96 { 97 setVisible(command_.isVisible()); 98 if (command_.isVisible()) 99 setEnabled(command_.isEnabled()); 100 101 parentToolbar_.invalidateSeparators(); 102 } 103 104 private final AppCommand command_; 105 private boolean synced_ = true; 106 private HandlerRegistration handlerReg_; 107 private HandlerRegistration handlerReg2_; 108 private Toolbar parentToolbar_; 109 } 110 AppCommand()111 public AppCommand() 112 { 113 if (Desktop.hasDesktopFrame()) 114 { 115 // If this command is destined for the main window, do not allow 116 // satellite windows to alter the global state of the command. 117 // 118 // Note that we can't use the more robust isCurrentWindowSatellite() 119 // here since AppCommand callbacks run on app init (before the 120 // satellite callbacks are wired up). 121 // 122 // We also need to execute this check at runtime (not in the constructor) 123 // since windowMode is not set when the AppCommand is constructed. 124 addEnabledChangedHandler((event) -> 125 { 126 if (!StringUtil.equals(getWindowMode(), WINDOW_MODE_MAIN) || 127 StringUtil.isNullOrEmpty(RStudio.getSatelliteView())) 128 { 129 DesktopMenuCallback.setCommandEnabled(id_, enabled_); 130 } 131 }); 132 addVisibleChangedHandler((event) -> 133 { 134 if (!StringUtil.equals(getWindowMode(), WINDOW_MODE_MAIN) || 135 StringUtil.isNullOrEmpty(RStudio.getSatelliteView())) 136 { 137 DesktopMenuCallback.setCommandVisible(id_, visible_); 138 } 139 }); 140 } 141 } 142 executeFromShortcut()143 void executeFromShortcut() 144 { 145 executedFromShortcut_ = true; 146 doExecute(); 147 } 148 execute()149 public void execute() 150 { 151 executedFromShortcut_ = false; 152 doExecute(); 153 } 154 doExecute()155 private void doExecute() 156 { 157 assert enabled_ : "AppCommand executed when it was not enabled"; 158 if (!enabled_) 159 return; 160 assert visible_ : "AppCommand executed when it was not visible"; 161 if (!visible_) 162 return; 163 164 // if this window is a satellite but the command only wants to be handled 165 // in the a different window, execute the command there instead 166 Satellite satellite = RStudioGinjector.INSTANCE.getSatellite(); 167 if (getWindowMode() != WINDOW_MODE_ANY && 168 Satellite.isCurrentWindowSatellite() && 169 satellite.getSatelliteName() != getWindowMode()) 170 { 171 if (getWindowMode() == WINDOW_MODE_MAIN) 172 { 173 // raise the main window if it's not a background command 174 satellite.focusMainWindow(); 175 } 176 else if (getWindowMode() == WINDOW_MODE_BACKGROUND && 177 Desktop.hasDesktopFrame()) 178 { 179 // for background commands, we still want the main window to be 180 // as visible as possible, so bring it up behind the current window 181 // (this is of course only possible in desktop mode) 182 Desktop.getFrame().bringMainFrameBehindActive(); 183 } 184 185 // satellites don't fire commands peer-to-peer--route it to the main 186 // window for processing 187 SatelliteManager mgr = RStudioGinjector.INSTANCE.getSatelliteManager(); 188 mgr.dispatchCommand(this, null); 189 return; 190 } 191 192 if (enableNoHandlerAssertions_) 193 { 194 assert handlers_.getHandlerCount(CommandEvent.TYPE) > 0 195 : "AppCommand executed but nobody was listening: " + getId(); 196 } 197 198 CommandEvent event = new CommandEvent(this); 199 RStudioGinjector.INSTANCE.getEventBus().fireEvent(event); 200 handlers_.fireEvent(event); 201 } 202 isEnabled()203 public boolean isEnabled() 204 { 205 return enabled_ && isVisible(); // jcheng 06/30/2010: Hmmmm, smells weird. 206 } 207 208 /** 209 * Determines whether there are any handlers established that will execute 210 * when this command runs. This is useful for determining if the command 211 * will do anything when executed. 212 * 213 * @return Whether this command has handlers. 214 */ hasCommandHandlers()215 public boolean hasCommandHandlers() 216 { 217 return handlers_.getHandlerCount(CommandEvent.TYPE) > 0; 218 } 219 setEnabled(boolean enabled)220 public void setEnabled(boolean enabled) 221 { 222 setEnabled(enabled, false); 223 } 224 setEnabled(boolean enabled, boolean force)225 public void setEnabled(boolean enabled, boolean force) 226 { 227 if (force || enabled != enabled_) 228 { 229 enabled_ = enabled; 230 handlers_.fireEvent(new EnabledChangedEvent(this)); 231 } 232 } 233 isVisible()234 public boolean isVisible() 235 { 236 return visible_; 237 } 238 setVisible(boolean visible)239 public void setVisible(boolean visible) 240 { 241 if (!removed_ && visible != visible_) 242 { 243 visible_ = visible; 244 handlers_.fireEvent(new VisibleChangedEvent(this)); 245 } 246 } 247 248 /** 249 * Restores a command which was formerly removed. The command must still be made 250 * visible and enabled in order to work. 251 */ restore()252 public void restore() 253 { 254 removed_ = false; 255 } 256 isCheckable()257 public boolean isCheckable() 258 { 259 return checkable_; 260 } 261 setCheckable(boolean isCheckable)262 public void setCheckable(boolean isCheckable) 263 { 264 if (isRadio()) 265 checkable_ = true; 266 else 267 checkable_ = isCheckable; 268 } 269 isChecked()270 public boolean isChecked() 271 { 272 return checkable_ && checked_; 273 } 274 setChecked(boolean checked)275 public void setChecked(boolean checked) 276 { 277 if (!isCheckable()) 278 return; 279 280 checked_ = checked; 281 if (Desktop.hasDesktopFrame()) 282 DesktopMenuCallback.setCommandChecked(id_, checked_); 283 } 284 setRadio(boolean isRadio)285 public void setRadio(boolean isRadio) 286 { 287 radio_ = isRadio; 288 if (radio_) 289 setCheckable(true); 290 } 291 isRadio()292 public boolean isRadio() 293 { 294 return radio_; 295 } 296 getMenuRole()297 public MenuitemRole getMenuRole() 298 { 299 if (isRadio()) 300 return Roles.getMenuitemradioRole(); 301 else if (isCheckable()) 302 return Roles.getMenuitemcheckboxRole(); 303 else 304 return Roles.getMenuitemRole(); 305 } 306 getWindowMode()307 public String getWindowMode() 308 { 309 return windowMode_; 310 } 311 setWindowMode(String mode)312 public void setWindowMode(String mode) 313 { 314 windowMode_ = mode; 315 } 316 isRebindable()317 public boolean isRebindable() 318 { 319 return rebindable_; 320 } 321 setRebindable(boolean rebindable)322 public void setRebindable(boolean rebindable) 323 { 324 rebindable_ = rebindable; 325 } 326 327 public enum Context 328 { 329 Workbench, Editor, R, Cpp, PackageDevelopment, RMarkdown, 330 Markdown, Sweave, Help, VCS, Packrat, Renv, RPresentation, 331 Addin, Viewer, History, Tutorial, Diagnostics, Import, Files; 332 333 @Override toString()334 public String toString() 335 { 336 if (this == Cpp) 337 return "C / C++"; 338 339 return StringUtil.prettyCamel(super.toString()); 340 } 341 } 342 getContext()343 public Context getContext() 344 { 345 return context_; 346 } 347 setContext(String context)348 public void setContext(String context) 349 { 350 String lower = context.toLowerCase(); 351 if (lower.equals("workbench")) 352 context_ = Context.Workbench; 353 else if (lower.equals("editor")) 354 context_ = Context.Editor; 355 else if (lower.equals("vcs")) 356 context_ = Context.VCS; 357 else if (lower.equals("r")) 358 context_ = Context.R; 359 else if (lower.equals("cpp")) 360 context_ = Context.Cpp; 361 else if (lower.equals("packagedevelopment")) 362 context_ = Context.PackageDevelopment; 363 else if (lower.equals("rmarkdown")) 364 context_ = Context.RMarkdown; 365 else if (lower.equals("sweave")) 366 context_ = Context.Markdown; 367 else if (lower.equals("markdown")) 368 context_ = Context.Sweave; 369 else if (lower.equals("help")) 370 context_ = Context.Help; 371 else if (lower.equals("packrat")) 372 context_ = Context.Packrat; 373 else if (lower.equals("renv")) 374 context_ = Context.Renv; 375 else if (lower.equals("presentation")) 376 context_ = Context.RPresentation; 377 else if (lower.equals("viewer")) 378 context_ = Context.Viewer; 379 else if (lower.equals("tutorial")) 380 context_ = Context.Tutorial; 381 else if (lower.equals("diagnostics")) 382 context_ = Context.Diagnostics; 383 else if (lower.equals("history")) 384 context_ = Context.History; 385 else if (lower.equals("import")) 386 context_ = Context.Import; 387 else if (lower.equals("files")) 388 context_ = Context.Files; 389 else 390 throw new Error("Invalid AppCommand context '" + context + "'"); 391 } 392 393 /** 394 * Hides the command and makes sure it never comes back. 395 */ remove()396 public void remove() 397 { 398 setVisible(false); 399 removed_ = true; 400 } 401 getDesc()402 public String getDesc() 403 { 404 return desc_; 405 } 406 getTooltip()407 public String getTooltip() 408 { 409 String desc = StringUtil.notNull(getDesc()); 410 String shortcut = getShortcutPrettyHtml(); 411 shortcut = StringUtil.isNullOrEmpty(shortcut) 412 ? "" 413 : "(" + DomUtils.htmlToText(shortcut) + ")"; 414 415 String result = (desc + " " + shortcut).trim(); 416 return result.length() == 0 ? null : result; 417 } 418 getId()419 public String getId() 420 { 421 return id_; 422 } 423 424 // Called by CommandBundleGenerator setId(String id)425 public void setId(String id) 426 { 427 id_ = id; 428 } 429 setDesc(String desc)430 public void setDesc(String desc) 431 { 432 desc_ = desc; 433 } 434 getLabel()435 public String getLabel() 436 { 437 return label_; 438 } 439 setLabel(String label)440 public void setLabel(String label) 441 { 442 label_ = label; 443 } 444 getButtonLabel()445 public String getButtonLabel() 446 { 447 if (buttonLabel_ != null) 448 return buttonLabel_; 449 return getLabel(); 450 } 451 setButtonLabel(String buttonLabel)452 public void setButtonLabel(String buttonLabel) 453 { 454 buttonLabel_ = buttonLabel; 455 } 456 getMenuLabel(boolean useMnemonics)457 public String getMenuLabel(boolean useMnemonics) 458 { 459 if (menuLabel_ != null) 460 { 461 return AppMenuItem.replaceMnemonics(menuLabel_, useMnemonics ? "&" : ""); 462 } 463 return getLabel(); 464 } 465 setMenuLabel(String menuLabel)466 public void setMenuLabel(String menuLabel) 467 { 468 menuLabel_ = menuLabel; 469 if (Desktop.hasDesktopFrame()) 470 DesktopMenuCallback.setCommandLabel(id_, menuLabel_); 471 } 472 473 @Override getImageResource()474 public ImageResource getImageResource() 475 { 476 return menuImageResource(isCheckable(), isChecked(), imageResource_); 477 } 478 menuImageResource(boolean isCheckable, boolean isChecked, ImageResource defaultImage)479 public static ImageResource menuImageResource(boolean isCheckable, boolean isChecked, ImageResource defaultImage) 480 { 481 if (isCheckable) 482 { 483 if (RStudioThemes.isFlat() && RStudioThemes.isEditorDark()) { 484 return isChecked ? 485 new ImageResource2x(ThemeResources.INSTANCE.menuCheckInverted2x()) : 486 null; 487 } 488 else { 489 return isChecked ? 490 new ImageResource2x(ThemeResources.INSTANCE.menuCheck2x()) : 491 null; 492 } 493 } 494 else 495 { 496 return defaultImage; 497 } 498 } 499 500 @Override addRenderedImage(Image image)501 public void addRenderedImage(Image image) 502 { 503 } 504 setImageResource(ImageResource imageResource)505 public void setImageResource(ImageResource imageResource) 506 { 507 imageResource_ = imageResource; 508 } 509 setRightImage(ImageResource image)510 public void setRightImage(ImageResource image) 511 { 512 setRightImage(image, null); 513 } 514 setRightImage(ImageResource image, String desc)515 public void setRightImage(ImageResource image, String desc) 516 { 517 rightImage_ = image; 518 rightImageDesc_ = desc; 519 } 520 addHandler(CommandHandler handler)521 public HandlerRegistration addHandler(CommandHandler handler) 522 { 523 return handlers_.addHandler(CommandEvent.TYPE, handler); 524 } 525 addEnabledChangedHandler( EnabledChangedHandler handler)526 public HandlerRegistration addEnabledChangedHandler( 527 EnabledChangedHandler handler) 528 { 529 return handlers_.addHandler(EnabledChangedEvent.TYPE, handler); 530 } 531 addVisibleChangedHandler( VisibleChangedHandler handler)532 public HandlerRegistration addVisibleChangedHandler( 533 VisibleChangedHandler handler) 534 { 535 return handlers_.addHandler(VisibleChangedEvent.TYPE, handler); 536 } 537 onClick(ClickEvent event)538 public void onClick(ClickEvent event) 539 { 540 execute(); 541 } 542 createToolbarButton()543 public ToolbarButton createToolbarButton() 544 { 545 return createToolbarButton(true); 546 } 547 createToolbarButton(boolean synced)548 public ToolbarButton createToolbarButton(boolean synced) 549 { 550 CommandToolbarButton button = new CommandToolbarButton(getButtonLabel(), 551 getDesc(), 552 this, 553 this, 554 synced); 555 if (getTooltip() != null) 556 button.setTitle(getTooltip()); 557 return button; 558 } 559 createMenuItem(boolean mainMenu)560 public MenuItem createMenuItem(boolean mainMenu) 561 { 562 return new AppMenuItem(this, mainMenu); 563 } 564 getMenuHTML(boolean mainMenu)565 public String getMenuHTML(boolean mainMenu) 566 { 567 String label = getMenuLabel(false); 568 String shortcut = shortcut_ != null ? shortcut_.toString(true) : ""; 569 570 return formatMenuLabel( 571 getImageResource(), label, shortcut, rightImage_, rightImageDesc_); 572 } 573 formatMenuLabel(ImageResource icon, String label, String shortcut)574 public static String formatMenuLabel(ImageResource icon, 575 String label, 576 String shortcut) 577 { 578 return formatMenuLabel(icon, label, false, shortcut); 579 } 580 formatMenuLabelWithStyle(ImageResource icon, String label, String shortcut, String styleName)581 public static String formatMenuLabelWithStyle(ImageResource icon, 582 String label, 583 String shortcut, 584 String styleName) 585 { 586 return formatMenuLabel(icon, 587 label, 588 false, 589 shortcut, 590 null, 591 null, 592 null, 593 styleName); 594 } 595 formatMenuLabel(ImageResource icon, String label, boolean html, String shortcut)596 public static String formatMenuLabel(ImageResource icon, 597 String label, 598 boolean html, 599 String shortcut) 600 { 601 return formatMenuLabel(icon, label, html, shortcut, null, null); 602 } 603 formatMenuLabel(ImageResource icon, String label, String shortcut, ImageResource rightImage, String rightImageDesc)604 public static String formatMenuLabel(ImageResource icon, 605 String label, 606 String shortcut, 607 ImageResource rightImage, 608 String rightImageDesc) 609 { 610 return formatMenuLabel(icon, label, false, shortcut, rightImage, rightImageDesc); 611 } 612 formatMenuLabel(ImageResource icon, String label, boolean html, String shortcut, ImageResource rightImage, String rightImageDesc)613 public static String formatMenuLabel(ImageResource icon, 614 String label, 615 boolean html, 616 String shortcut, 617 ImageResource rightImage, 618 String rightImageDesc) 619 { 620 return formatMenuLabel(icon, 621 label, 622 html, 623 shortcut, 624 null, 625 rightImage, 626 rightImageDesc, 627 null); 628 } 629 formatMenuLabel(ImageResource icon, String label, String shortcut, Integer iconOffsetY)630 public static String formatMenuLabel(ImageResource icon, 631 String label, 632 String shortcut, 633 Integer iconOffsetY) 634 { 635 return formatMenuLabel(icon, label, false, shortcut, iconOffsetY); 636 } 637 formatMenuLabel(ImageResource icon, String label, boolean html, String shortcut, Integer iconOffsetY)638 public static String formatMenuLabel(ImageResource icon, 639 String label, 640 boolean html, 641 String shortcut, 642 Integer iconOffsetY) 643 { 644 return formatMenuLabel(icon, label, html, shortcut, iconOffsetY, null, null, null); 645 } 646 formatMenuLabel(ImageResource icon, String label, boolean html, String shortcut, Integer iconOffsetY, ImageResource rightImage, String rightImageDesc, String styleName)647 public static String formatMenuLabel(ImageResource icon, 648 String label, 649 boolean html, 650 String shortcut, 651 Integer iconOffsetY, 652 ImageResource rightImage, 653 String rightImageDesc, 654 String styleName) 655 { 656 StringBuilder text = new StringBuilder(); 657 int topOffset = -2; 658 if (iconOffsetY != null) 659 topOffset += iconOffsetY; 660 text.append("<table role=\"presentation\""); 661 if (label != null) 662 { 663 text.append("id=\"" + ElementIds.idFromLabel(label) + "_command\" "); 664 } 665 666 if (styleName != null) 667 { 668 text.append("class='" + styleName + "' "); 669 } 670 671 text.append("border=0 cellpadding=0 cellspacing=0 width='100%'><tr>"); 672 673 text.append("<td width=\"25\" style=\"vertical-align: top\">" + 674 "<div style=\"width: 25px; margin-top: " + 675 topOffset + "px; margin-bottom: -10px\">"); 676 if (icon != null) 677 { 678 SafeHtml imageHtml = createMenuImageHtml(icon); 679 text.append(imageHtml.asString()); 680 } 681 else 682 { 683 text.append("<br/>"); 684 } 685 text.append("</div></td>"); 686 687 label = StringUtil.notNull(label); 688 if (!html) 689 label = DomUtils.textToHtml(label); 690 text.append("<td>" + label + "</td>"); 691 if (rightImage != null) 692 { 693 SafeHtml imageHtml = createRightImageHtml(rightImage, rightImageDesc); 694 text.append("<td align=right width=\"25\"><div style=\"width: 25px; float: right; margin-top: -7px; margin-bottom: -10px;\">" + imageHtml.asString() + "</div></td>"); 695 } 696 else if (shortcut != null) 697 { 698 text.append("<td align=right nowrap> " + shortcut + "</td>"); 699 } 700 text.append("</tr></table>"); 701 702 return text.toString(); 703 } 704 getShortcut()705 public KeyboardShortcut getShortcut() 706 { 707 return shortcut_; 708 } 709 getKeySequence()710 public KeySequence getKeySequence() 711 { 712 if (shortcut_ == null) 713 return new KeySequence(); 714 715 return shortcut_.getKeySequence(); 716 } 717 setShortcut(KeyboardShortcut shortcut)718 public void setShortcut(KeyboardShortcut shortcut) 719 { 720 shortcut_ = shortcut; 721 } 722 getShortcutRaw()723 public String getShortcutRaw() 724 { 725 return shortcut_ != null ? shortcut_.toString(false) : null; 726 } 727 getShortcutPrettyHtml()728 public String getShortcutPrettyHtml() 729 { 730 return shortcut_ != null ? shortcut_.toString(true) : null; 731 } 732 getExecutedFromShortcut()733 public boolean getExecutedFromShortcut() 734 { 735 return executedFromShortcut_; 736 } 737 disableNoHandlerAssertions()738 public static void disableNoHandlerAssertions() 739 { 740 enableNoHandlerAssertions_ = false; 741 } 742 summarize()743 public String summarize() 744 { 745 if (!StringUtil.isNullOrEmpty(getMenuLabel(false))) 746 return getMenuLabel(false); 747 else if (!StringUtil.isNullOrEmpty(getButtonLabel())) 748 return getButtonLabel(); 749 else if (!StringUtil.isNullOrEmpty(getDesc())) 750 return getDesc(); 751 else 752 return "(no description)"; 753 } 754 createRightImageHtml(ImageResource image, String desc)755 private static SafeHtml createRightImageHtml(ImageResource image, 756 String desc) 757 { 758 SafeHtmlBuilder sb = new SafeHtmlBuilder(); 759 sb.append(SafeHtmlUtil.createOpenTag("img", 760 "class", ThemeStyles.INSTANCE.menuRightImage(), 761 "title", StringUtil.notNull(desc), 762 "aria-label", StringUtil.notNull(desc), 763 "width", Integer.toString(image.getWidth()), 764 "height", Integer.toString(image.getHeight()), 765 "src", image.getSafeUri().asString(), 766 "alt", "")); 767 sb.appendHtmlConstant("</img>"); 768 return sb.toSafeHtml(); 769 } 770 createMenuImageHtml(ImageResource image)771 private static SafeHtml createMenuImageHtml(ImageResource image) 772 { 773 SafeHtmlBuilder sb = new SafeHtmlBuilder(); 774 sb.append(SafeHtmlUtil.createOpenTag("img", 775 "width", Integer.toString(image.getWidth()), 776 "height", Integer.toString(image.getHeight()), 777 "src", image.getSafeUri().asString(), 778 "alt", "")); 779 sb.appendHtmlConstant("</img>"); 780 return sb.toSafeHtml(); 781 } 782 783 private boolean enabled_ = true; 784 private boolean visible_ = true; 785 private boolean removed_ = false; 786 private boolean checkable_ = false; 787 private boolean radio_ = false; 788 private boolean checked_ = false; 789 private String windowMode_ = "any"; 790 private final HandlerManager handlers_ = new HandlerManager(this); 791 private boolean rebindable_ = true; 792 private Context context_ = Context.Workbench; 793 794 private String label_ = null; 795 private String buttonLabel_ = null; 796 private String menuLabel_ = null; 797 private String desc_; 798 private ImageResource imageResource_; 799 private KeyboardShortcut shortcut_; 800 private String id_; 801 private ImageResource rightImage_ = null; 802 private String rightImageDesc_ = null; 803 804 private boolean executedFromShortcut_ = false; 805 806 private static boolean enableNoHandlerAssertions_ = true; 807 808 public static final String WINDOW_MODE_BACKGROUND = "background"; 809 public static final String WINDOW_MODE_MAIN = "main"; 810 public static final String WINDOW_MODE_ANY = "any"; 811 } 812