1 /* 2 * Workbench.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.studio.client.workbench; 16 17 import com.google.gwt.core.client.GWT; 18 import com.google.gwt.core.client.Scheduler; 19 import com.google.gwt.user.client.Command; 20 import com.google.inject.Inject; 21 import com.google.inject.Provider; 22 23 import org.rstudio.core.client.BrowseCap; 24 import org.rstudio.core.client.Size; 25 import org.rstudio.core.client.StringUtil; 26 import org.rstudio.core.client.TimeBufferedCommand; 27 import org.rstudio.core.client.command.AppCommand; 28 import org.rstudio.core.client.command.CommandBinder; 29 import org.rstudio.core.client.command.Handler; 30 import org.rstudio.core.client.files.FileSystemItem; 31 import org.rstudio.core.client.files.filedialog.events.OpenFileDialogEvent; 32 import org.rstudio.core.client.widget.ModifyKeyboardShortcutsWidget; 33 import org.rstudio.core.client.widget.Operation; 34 import org.rstudio.core.client.widget.OperationWithInput; 35 import org.rstudio.core.client.widget.ProgressIndicator; 36 import org.rstudio.core.client.widget.ProgressOperationWithInput; 37 import org.rstudio.studio.client.RStudioGinjector; 38 import org.rstudio.studio.client.application.ApplicationVisibility; 39 import org.rstudio.studio.client.application.Desktop; 40 import org.rstudio.studio.client.application.events.DeferredInitCompletedEvent; 41 import org.rstudio.studio.client.application.events.EventBus; 42 import org.rstudio.studio.client.common.ConsoleDispatcher; 43 import org.rstudio.studio.client.common.FileDialogs; 44 import org.rstudio.studio.client.common.GlobalDisplay; 45 import org.rstudio.studio.client.common.GlobalDisplay.NewWindowOptions; 46 import org.rstudio.studio.client.common.GlobalProgressDelayer; 47 import org.rstudio.studio.client.common.SimpleRequestCallback; 48 import org.rstudio.studio.client.common.dependencies.DependencyManager; 49 import org.rstudio.studio.client.common.filetypes.FileTypeRegistry; 50 import org.rstudio.studio.client.common.rstudioapi.AskSecretManager; 51 import org.rstudio.studio.client.common.vcs.AskPassManager; 52 import org.rstudio.studio.client.common.vcs.ShowPublicKeyDialog; 53 import org.rstudio.studio.client.common.vcs.VCSConstants; 54 import org.rstudio.studio.client.htmlpreview.HTMLPreview; 55 import org.rstudio.studio.client.htmlpreview.events.ShowHTMLPreviewEvent; 56 import org.rstudio.studio.client.htmlpreview.events.ShowPageViewerEvent; 57 import org.rstudio.studio.client.htmlpreview.model.HTMLPreviewParams; 58 import org.rstudio.studio.client.pdfviewer.PDFViewer; 59 import org.rstudio.studio.client.plumber.PlumberAPI; 60 import org.rstudio.studio.client.projects.ProjectOpener; 61 import org.rstudio.studio.client.projects.model.ProjectTemplateRegistryProvider; 62 import org.rstudio.studio.client.rmarkdown.RmdOutput; 63 import org.rstudio.studio.client.rmarkdown.events.ShinyGadgetDialogEvent; 64 import org.rstudio.studio.client.server.Server; 65 import org.rstudio.studio.client.server.ServerError; 66 import org.rstudio.studio.client.server.ServerRequestCallback; 67 import org.rstudio.studio.client.server.VoidServerRequestCallback; 68 import org.rstudio.studio.client.server.remote.ExecuteUserCommandEvent; 69 import org.rstudio.studio.client.shiny.ShinyApplication; 70 import org.rstudio.studio.client.shiny.ui.ShinyGadgetDialog; 71 import org.rstudio.studio.client.workbench.commands.Commands; 72 import org.rstudio.studio.client.workbench.commands.ReportShortcutBindingEvent; 73 import org.rstudio.studio.client.workbench.events.*; 74 import org.rstudio.studio.client.workbench.events.ShowMainMenuEvent.Menu; 75 import org.rstudio.studio.client.workbench.model.*; 76 import org.rstudio.studio.client.workbench.prefs.model.UserPrefs; 77 import org.rstudio.studio.client.workbench.snippets.ui.EditSnippetsDialog; 78 import org.rstudio.studio.client.workbench.views.choosefile.ChooseFile; 79 import org.rstudio.studio.client.workbench.views.files.events.DirectoryNavigateEvent; 80 import org.rstudio.studio.client.workbench.views.source.SourceWindowManager; 81 import org.rstudio.studio.client.workbench.views.source.editors.profiler.ProfilerPresenter; 82 import org.rstudio.studio.client.workbench.views.terminal.events.ActivateNamedTerminalEvent; 83 import org.rstudio.studio.client.workbench.views.tutorial.TutorialPresenter.Tutorial; 84 import org.rstudio.studio.client.workbench.views.tutorial.events.TutorialCommandEvent; 85 import org.rstudio.studio.client.workbench.views.tutorial.events.TutorialLaunchEvent; 86 import org.rstudio.studio.client.workbench.views.vcs.git.model.GitState; 87 88 public class Workbench implements BusyEvent.Handler, 89 ShowErrorMessageEvent.Handler, 90 UserPromptEvent.Handler, 91 ShowWarningBarEvent.Handler, 92 BrowseUrlEvent.Handler, 93 QuotaStatusEvent.Handler, 94 WorkbenchLoadedEvent.Handler, 95 WorkbenchMetricsChangedEvent.Handler, 96 InstallRtoolsEvent.Handler, 97 ShinyGadgetDialogEvent.Handler, 98 ExecuteUserCommandEvent.Handler, 99 AdminNotificationEvent.Handler, 100 OpenFileDialogEvent.Handler, 101 ShowPageViewerEvent.Handler, 102 TutorialLaunchEvent.Handler, 103 DeferredInitCompletedEvent.Handler, 104 ReportShortcutBindingEvent.Handler 105 { 106 interface Binder extends CommandBinder<Commands, Workbench> {} 107 108 @Inject Workbench(WorkbenchMainView view, WorkbenchContext workbenchContext, GlobalDisplay globalDisplay, Commands commands, EventBus eventBus, Session session, Provider<UserPrefs> pPrefs, Server server, RemoteFileSystemContext fsContext, FileDialogs fileDialogs, FileTypeRegistry fileTypeRegistry, ConsoleDispatcher consoleDispatcher, WorkbenchNewSession newSession, ProjectOpener projectOpener, Provider<GitState> pGitState, SourceWindowManager sourceWindowManager, UserInterfaceHighlighter highlighter, ChooseFile chooseFile, AskPassManager askPass, PDFViewer pdfViewer, HTMLPreview htmlPreview, ProfilerPresenter prof, ShinyApplication sApp, PlumberAPI sAPI, DependencyManager dm, ApplicationVisibility av, RmdOutput rmdOutput, ProjectTemplateRegistryProvider provider, WorkbenchServerOperations serverOperations, AskSecretManager askSecret)109 public Workbench(WorkbenchMainView view, 110 WorkbenchContext workbenchContext, 111 GlobalDisplay globalDisplay, 112 Commands commands, 113 EventBus eventBus, 114 Session session, 115 Provider<UserPrefs> pPrefs, 116 Server server, 117 RemoteFileSystemContext fsContext, 118 FileDialogs fileDialogs, 119 FileTypeRegistry fileTypeRegistry, 120 ConsoleDispatcher consoleDispatcher, 121 WorkbenchNewSession newSession, 122 ProjectOpener projectOpener, 123 Provider<GitState> pGitState, 124 SourceWindowManager sourceWindowManager, 125 UserInterfaceHighlighter highlighter, // force gin to create 126 ChooseFile chooseFile, // force gin to create 127 AskPassManager askPass, // force gin to create 128 PDFViewer pdfViewer, // force gin to create 129 HTMLPreview htmlPreview, // force gin to create 130 ProfilerPresenter prof, // force gin to create 131 ShinyApplication sApp, // force gin to create 132 PlumberAPI sAPI, // force gin to create 133 DependencyManager dm, // force gin to create 134 ApplicationVisibility av, // force gin to create 135 RmdOutput rmdOutput, // force gin to create 136 ProjectTemplateRegistryProvider provider, // force gin to create 137 WorkbenchServerOperations serverOperations, // force gin to create 138 AskSecretManager askSecret) // force gin to create 139 { 140 view_ = view; 141 workbenchContext_ = workbenchContext; 142 projectOpener_ = projectOpener; 143 globalDisplay_ = globalDisplay; 144 commands_ = commands; 145 eventBus_ = eventBus; 146 session_ = session; 147 pPrefs_ = pPrefs; 148 server_ = server; 149 fsContext_ = fsContext; 150 fileDialogs_ = fileDialogs; 151 fileTypeRegistry_ = fileTypeRegistry; 152 consoleDispatcher_ = consoleDispatcher; 153 pGitState_ = pGitState; 154 newSession_ = newSession; 155 serverOperations_ = serverOperations; 156 sourceWindowManager_ = sourceWindowManager; 157 158 ((Binder)GWT.create(Binder.class)).bind(commands, this); 159 160 // edit 161 eventBus.addHandler(BusyEvent.TYPE, this); 162 eventBus.addHandler(ShowErrorMessageEvent.TYPE, this); 163 eventBus.addHandler(UserPromptEvent.TYPE, this); 164 eventBus.addHandler(ShowWarningBarEvent.TYPE, this); 165 eventBus.addHandler(BrowseUrlEvent.TYPE, this); 166 eventBus.addHandler(QuotaStatusEvent.TYPE, this); 167 eventBus.addHandler(WorkbenchLoadedEvent.TYPE, this); 168 eventBus.addHandler(WorkbenchMetricsChangedEvent.TYPE, this); 169 eventBus.addHandler(InstallRtoolsEvent.TYPE, this); 170 eventBus.addHandler(ShinyGadgetDialogEvent.TYPE, this); 171 eventBus.addHandler(ExecuteUserCommandEvent.TYPE, this); 172 eventBus.addHandler(AdminNotificationEvent.TYPE, this); 173 eventBus.addHandler(OpenFileDialogEvent.TYPE, this); 174 eventBus.addHandler(ShowPageViewerEvent.TYPE, this); 175 eventBus.addHandler(TutorialLaunchEvent.TYPE, this); 176 eventBus.addHandler(DeferredInitCompletedEvent.TYPE, this); 177 eventBus.addHandler(ReportShortcutBindingEvent.TYPE, this); 178 179 // We don't want to send setWorkbenchMetrics more than once per 1/2-second 180 metricsChangedCommand_ = new TimeBufferedCommand(500) 181 { 182 @Override 183 protected void performAction(boolean shouldSchedulePassive) 184 { 185 assert !shouldSchedulePassive; 186 187 server_.setWorkbenchMetrics(lastWorkbenchMetrics_, 188 new VoidServerRequestCallback()); 189 } 190 }; 191 } 192 getMainView()193 public WorkbenchMainView getMainView() 194 { 195 return view_; 196 } 197 onWorkbenchLoaded(WorkbenchLoadedEvent event)198 public void onWorkbenchLoaded(WorkbenchLoadedEvent event) 199 { 200 server_.initializeForMainWorkbench(); 201 202 FileSystemItem defaultDialogDir = 203 session_.getSessionInfo().getActiveProjectDir(); 204 if (defaultDialogDir != null) 205 workbenchContext_.setDefaultFileDialogDir(defaultDialogDir); 206 207 checkForInitMessages(); 208 checkForLicenseMessage(); 209 210 RStudioGinjector.INSTANCE.getFocusVisiblePolyfill().load(null); 211 212 if (Desktop.isDesktop() && 213 StringUtil.equals(session_.getSessionInfo().getVcsName(), VCSConstants.GIT_ID)) 214 { 215 pGitState_.get().addVcsRefreshHandler(vcsRefreshEvent -> 216 { 217 String title = workbenchContext_.createWindowTitle(); 218 if (title != null) 219 Desktop.getFrame().setWindowTitle(title); 220 }); 221 } 222 } 223 onTutorialLaunch(final TutorialLaunchEvent event)224 public void onTutorialLaunch(final TutorialLaunchEvent event) 225 { 226 commands_.activateTutorial().execute(); 227 228 Tutorial tutorial = event.getTutorial(); 229 Scheduler.get().scheduleDeferred(() -> { 230 231 TutorialCommandEvent commandEvent = new TutorialCommandEvent( 232 TutorialCommandEvent.TYPE_LAUNCH_DEFAULT_TUTORIAL, 233 tutorial.toJsObject()); 234 235 eventBus_.fireEvent(commandEvent); 236 237 }); 238 } 239 onDeferredInitCompleted(DeferredInitCompletedEvent ev)240 public void onDeferredInitCompleted(DeferredInitCompletedEvent ev) 241 { 242 checkForCrashHandlerPermission(); 243 } 244 onBusy(BusyEvent event)245 public void onBusy(BusyEvent event) 246 { 247 } 248 onShowErrorMessage(ShowErrorMessageEvent event)249 public void onShowErrorMessage(ShowErrorMessageEvent event) 250 { 251 ErrorMessage errorMessage = event.getErrorMessage(); 252 globalDisplay_.showErrorMessage(errorMessage.getTitle(), 253 errorMessage.getMessage()); 254 255 } 256 257 @Override onShowWarningBar(ShowWarningBarEvent event)258 public void onShowWarningBar(ShowWarningBarEvent event) 259 { 260 globalDisplay_.showWarningBar(event.isSevere(), event.getMessage()); 261 } 262 onBrowseUrl(BrowseUrlEvent event)263 public void onBrowseUrl(BrowseUrlEvent event) 264 { 265 BrowseUrlInfo urlInfo = event.getUrlInfo(); 266 NewWindowOptions newWindowOptions = new NewWindowOptions(); 267 newWindowOptions.setName(urlInfo.getWindow()); 268 globalDisplay_.openWindow(urlInfo.getUrl(), newWindowOptions); 269 } 270 onWorkbenchMetricsChanged(WorkbenchMetricsChangedEvent event)271 public void onWorkbenchMetricsChanged(WorkbenchMetricsChangedEvent event) 272 { 273 lastWorkbenchMetrics_ = event.getWorkbenchMetrics(); 274 metricsChangedCommand_.nudge(); 275 } 276 onQuotaStatus(QuotaStatusEvent event)277 public void onQuotaStatus(QuotaStatusEvent event) 278 { 279 QuotaStatus quotaStatus = event.getQuotaStatus(); 280 281 // always show warning if the user is over quota 282 if (quotaStatus.isOverQuota()) 283 { 284 long over = quotaStatus.getUsed() - quotaStatus.getQuota(); 285 StringBuilder msg = new StringBuilder(); 286 msg.append("You are "); 287 msg.append(StringUtil.formatFileSize(over)); 288 msg.append(" over your "); 289 msg.append(StringUtil.formatFileSize(quotaStatus.getQuota())); 290 msg.append(" file storage limit. Please remove files to "); 291 msg.append("continue working."); 292 globalDisplay_.showWarningBar(false, msg.toString()); 293 } 294 295 // show a warning if the user is near their quota (but no more 296 // than one time per instantiation of the application) 297 else if (quotaStatus.isNearQuota() && !nearQuotaWarningShown_) 298 { 299 StringBuilder msg = new StringBuilder(); 300 msg.append("You are nearly over your "); 301 msg.append(StringUtil.formatFileSize(quotaStatus.getQuota())); 302 msg.append(" file storage limit."); 303 globalDisplay_.showWarningBar(false, msg.toString()); 304 305 nearQuotaWarningShown_ = true; 306 } 307 } 308 309 @Override onShinyGadgetDialog(ShinyGadgetDialogEvent event)310 public void onShinyGadgetDialog(ShinyGadgetDialogEvent event) 311 { 312 new ShinyGadgetDialog(event.getCaption(), 313 event.getUrl(), 314 new Size(event.getPreferredWidth(), 315 event.getPreferredHeight())).showModal(); 316 } 317 318 @Handler onSetWorkingDir()319 public void onSetWorkingDir() 320 { 321 fileDialogs_.chooseFolder( 322 "Choose Working Directory", 323 fsContext_, 324 workbenchContext_.getCurrentWorkingDir(), 325 new ProgressOperationWithInput<FileSystemItem>() 326 { 327 public void execute(FileSystemItem input, 328 ProgressIndicator indicator) 329 { 330 if (input == null) 331 return; 332 333 // set console 334 consoleDispatcher_.executeSetWd(input, true); 335 336 // set files pane 337 eventBus_.fireEvent(new DirectoryNavigateEvent(input)); 338 339 indicator.onCompleted(); 340 } 341 }); 342 } 343 344 @Handler onSetWorkingDirToProjectDir()345 void onSetWorkingDirToProjectDir() 346 { 347 FileSystemItem projectDir = session_.getSessionInfo() 348 .getActiveProjectDir(); 349 if (projectDir != null) 350 { 351 consoleDispatcher_.executeSetWd(projectDir, false); 352 eventBus_.fireEvent(new DirectoryNavigateEvent(projectDir, true)); 353 } 354 } 355 356 @Handler onNewSession()357 void onNewSession() 358 { 359 newSession_.openNewSession(globalDisplay_, 360 workbenchContext_, 361 serverOperations_, 362 projectOpener_, 363 server_); 364 } 365 366 @Handler onSourceFile()367 public void onSourceFile() 368 { 369 fileDialogs_.openFile( 370 "Source File", 371 fsContext_, 372 workbenchContext_.getCurrentWorkingDir(), 373 new ProgressOperationWithInput<FileSystemItem>() 374 { 375 public void execute(FileSystemItem input, ProgressIndicator indicator) 376 { 377 if (input == null) 378 return; 379 380 indicator.onCompleted(); 381 382 consoleDispatcher_.executeSourceCommand( 383 input.getPath(), 384 fileTypeRegistry_.getTextTypeForFile(input), 385 pPrefs_.get().defaultEncoding().getValue(), 386 false, 387 false, 388 true, 389 false); 390 391 commands_.activateConsole().execute(); 392 } 393 }); 394 } 395 396 @Handler onVersionControlShowRsaKey()397 public void onVersionControlShowRsaKey() 398 { 399 final ProgressIndicator indicator = new GlobalProgressDelayer( 400 globalDisplay_, 500, "Reading RSA public key...").getIndicator(); 401 402 // compute path to public key 403 String sshDir = session_.getSessionInfo().getDefaultSSHKeyDir(); 404 final String keyPath = FileSystemItem.createDir(sshDir).completePath( 405 "id_rsa.pub"); 406 407 // read it 408 server_.gitSshPublicKey(keyPath, new ServerRequestCallback<String> () { 409 410 @Override 411 public void onResponseReceived(String publicKeyContents) 412 { 413 indicator.onCompleted(); 414 415 new ShowPublicKeyDialog("RSA Public Key", 416 publicKeyContents).showModal(); 417 } 418 419 @Override 420 public void onError(ServerError error) 421 { 422 String msg = "Error attempting to read key '" + keyPath + "' (" + 423 error.getUserMessage() + ")"; 424 indicator.onError(msg); 425 } 426 }); 427 } 428 429 @Handler onShowShellDialog()430 public void onShowShellDialog() 431 { 432 if (Desktop.isDesktop()) 433 { 434 server_.getTerminalOptions(new SimpleRequestCallback<TerminalOptions>() 435 { 436 @Override 437 public void onResponseReceived(TerminalOptions options) 438 { 439 Desktop.getFrame().openTerminal( 440 StringUtil.notNull(options.getTerminalPath()), 441 StringUtil.notNull(options.getWorkingDirectory()), 442 StringUtil.notNull(options.getExtraPathEntries()), 443 options.getShellType()); 444 } 445 }); 446 } 447 else 448 { 449 eventBus_.fireEvent(new ActivateNamedTerminalEvent()); 450 } 451 } 452 453 @Handler onBrowseAddins()454 public void onBrowseAddins() 455 { 456 BrowseAddinsDialog dialog = new BrowseAddinsDialog(new OperationWithInput<Command>() 457 { 458 @Override 459 public void execute(Command input) 460 { 461 if (input != null) 462 input.execute(); 463 } 464 }); 465 dialog.showModal(); 466 } 467 468 @Handler onModifyKeyboardShortcuts()469 public void onModifyKeyboardShortcuts() 470 { 471 new ModifyKeyboardShortcutsWidget().showModal(); 472 } 473 474 @Handler onEditCodeSnippets()475 public void onEditCodeSnippets() 476 { 477 new EditSnippetsDialog().showModal(); 478 } 479 480 @Handler onToggleFullScreen()481 public void onToggleFullScreen() 482 { 483 if (Desktop.hasDesktopFrame()) 484 Desktop.getFrame().toggleFullscreenMode(); 485 } 486 487 @Handler onShowFileMenu()488 public void onShowFileMenu() 489 { 490 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.File)); 491 } 492 493 @Handler onShowEditMenu()494 public void onShowEditMenu() 495 { 496 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Edit)); 497 } 498 499 @Handler onShowCodeMenu()500 public void onShowCodeMenu() 501 { 502 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Code)); 503 } 504 505 @Handler onShowViewMenu()506 public void onShowViewMenu() 507 { 508 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.View)); 509 } 510 511 @Handler onShowPlotsMenu()512 public void onShowPlotsMenu() 513 { 514 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Plots)); 515 } 516 517 @Handler onShowSessionMenu()518 public void onShowSessionMenu() 519 { 520 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Session)); 521 } 522 523 @Handler onShowBuildMenu()524 public void onShowBuildMenu() 525 { 526 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Build)); 527 } 528 529 @Handler onShowDebugMenu()530 public void onShowDebugMenu() 531 { 532 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Debug)); 533 } 534 535 @Handler onShowProfileMenu()536 public void onShowProfileMenu() 537 { 538 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Profile)); 539 } 540 541 @Handler onShowToolsMenu()542 public void onShowToolsMenu() 543 { 544 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Tools)); 545 } 546 547 @Handler onShowHelpMenu()548 public void onShowHelpMenu() 549 { 550 eventBus_.fireEvent(new ShowMainMenuEvent(Menu.Help)); 551 } 552 checkForInitMessages()553 private void checkForInitMessages() 554 { 555 if (!Desktop.isDesktop()) 556 { 557 server_.getInitMessages(new ServerRequestCallback<String>() { 558 @Override 559 public void onResponseReceived(String message) 560 { 561 if (message != null) 562 globalDisplay_.showWarningBar(false, message); 563 } 564 565 @Override 566 public void onError(ServerError error) 567 { 568 // ignore 569 } 570 }); 571 } 572 else 573 { 574 Desktop.getFrame().getInitMessages(message -> 575 { 576 if (!StringUtil.isNullOrEmpty(message)) 577 { 578 globalDisplay_.showLicenseWarningBar(false, message); 579 } 580 }); 581 } 582 } 583 checkForLicenseMessage()584 private void checkForLicenseMessage() 585 { 586 String licenseMessage = session_.getSessionInfo().getLicenseMessage(); 587 if (!StringUtil.isNullOrEmpty(licenseMessage)) 588 { 589 globalDisplay_.showLicenseWarningBar(false, licenseMessage); 590 } 591 } 592 checkForCrashHandlerPermission()593 private void checkForCrashHandlerPermission() 594 { 595 boolean shouldPrompt = session_.getSessionInfo().getPromptForCrashHandlerPermission(); 596 if (shouldPrompt) 597 { 598 String message = 599 "May we upload crash reports to RStudio automatically?\n\nCrash reports don't include " + 600 "any personal information, except for IP addresses which are used to determine how many users " + 601 "are affected by each crash.\n\nCrash reporting can be disabled at any time under the Global Options."; 602 603 globalDisplay_.showYesNoMessage(GlobalDisplay.MSG_QUESTION, 604 "Enable Automated Crash Reporting", 605 message, 606 false, 607 new Operation() { 608 @Override 609 public void execute() { 610 server_.setUserCrashHandlerPrompted(true, new SimpleRequestCallback<>()); 611 } 612 }, 613 new Operation() { 614 @Override 615 public void execute() { 616 server_.setUserCrashHandlerPrompted(false, new SimpleRequestCallback<>()); 617 } 618 }, 619 true); 620 } 621 } 622 onUserPrompt(UserPromptEvent event)623 public void onUserPrompt(UserPromptEvent event) 624 { 625 // is cancel supported? 626 UserPrompt userPrompt = event.getUserPrompt(); 627 628 // resolve labels 629 String yesLabel = userPrompt.getYesLabel(); 630 if (StringUtil.isNullOrEmpty(yesLabel)) 631 yesLabel = "Yes"; 632 String noLabel = userPrompt.getNoLabel(); 633 if (StringUtil.isNullOrEmpty(noLabel)) 634 noLabel = "No"; 635 636 // show dialog 637 globalDisplay_.showYesNoMessage( 638 userPrompt.getType(), 639 userPrompt.getCaption(), 640 userPrompt.getMessage(), 641 userPrompt.getIncludeCancel(), 642 userPromptResponse(UserPrompt.RESPONSE_YES), 643 userPromptResponse(UserPrompt.RESPONSE_NO), 644 userPrompt.getIncludeCancel() ? 645 userPromptResponse(UserPrompt.RESPONSE_CANCEL) : null, 646 yesLabel, 647 noLabel, 648 userPrompt.getYesIsDefault()); 649 } 650 userPromptResponse(final int response)651 private Operation userPromptResponse(final int response) 652 { 653 return new Operation() { 654 public void execute() 655 { 656 server_.userPromptCompleted(response, 657 new SimpleRequestCallback<>()); 658 659 } 660 }; 661 } 662 663 public void onAdminNotification(AdminNotificationEvent event) 664 { 665 AdminNotification notification = event.getAdminNotification(); 666 667 // show dialog 668 globalDisplay_.showMessage(notification.getType(), 669 "Admin Notification", 670 notification.getMessage(), 671 adminNotificationAcknowledged(notification.getId())); 672 } 673 674 @Override 675 public void onOpenFileDialog(OpenFileDialogEvent event) 676 { 677 final ProgressOperationWithInput<FileSystemItem> onSelected = 678 new ProgressOperationWithInput<FileSystemItem>() 679 { 680 @Override 681 public void execute(FileSystemItem input, 682 ProgressIndicator indicator) 683 { 684 indicator.onCompleted(); 685 686 server_.openFileDialogCompleted( 687 input == null ? "" : input.getPath(), 688 new VoidServerRequestCallback()); 689 } 690 }; 691 692 String caption = event.getCaption(); 693 String label = event.getLabel(); 694 int type = event.getType(); 695 FileSystemItem initialFilePath = event.getFile(); 696 String filter = event.getFilter(); 697 boolean selectExisting = event.selectExisting(); 698 699 if (type == OpenFileDialogEvent.TYPE_SELECT_FILE) 700 { 701 if (selectExisting) 702 { 703 fileDialogs_.openFile( 704 caption, 705 label, 706 fsContext_, 707 initialFilePath, 708 filter, 709 false, 710 false, 711 onSelected); 712 } 713 else 714 { 715 fileDialogs_.saveFile( 716 caption, 717 label, 718 fsContext_, 719 initialFilePath, 720 "", 721 false, 722 false, 723 onSelected); 724 } 725 } 726 else if (type == OpenFileDialogEvent.TYPE_SELECT_DIRECTORY) 727 { 728 fileDialogs_.chooseFolder( 729 caption, 730 label, 731 fsContext_, 732 initialFilePath, 733 false, 734 onSelected); 735 } 736 else 737 { 738 assert false: "unexpected file dialog type '" + type + "'"; 739 server_.openFileDialogCompleted(null, new VoidServerRequestCallback()); 740 } 741 } 742 743 private Operation adminNotificationAcknowledged(final String id) 744 { 745 return new Operation() { 746 public void execute() 747 { 748 server_.adminNotificationAcknowledged(id, new SimpleRequestCallback<>()); 749 } 750 }; 751 } 752 753 @Override 754 public void onInstallRtools(final InstallRtoolsEvent event) 755 { 756 if (BrowseCap.isWindowsDesktop()) 757 { 758 Desktop.getFrame().installRtools(StringUtil.notNull(event.getVersion()), 759 StringUtil.notNull(event.getInstallerPath())); 760 } 761 } 762 763 @Override 764 public void onShowPageViewer(ShowPageViewerEvent event) 765 { 766 // show the page viewer window 767 HTMLPreviewParams params = event.getParams(); 768 eventBus_.fireEvent(new ShowHTMLPreviewEvent(params)); 769 770 // server will now take care of sending the html_preview_completed event 771 } 772 773 @Override 774 public void onExecuteUserCommand(ExecuteUserCommandEvent event) 775 { 776 server_.executeUserCommand(event.getCommandName(), new VoidServerRequestCallback()); 777 } 778 779 @Override 780 public void onReportShortcutBinding(ReportShortcutBindingEvent event) 781 { 782 AppCommand command = commands_.getCommandById(event.getCommand()); 783 if (command == null) 784 globalDisplay_.showWarningBar(false, event.getCommand()); 785 else 786 globalDisplay_.showWarningBar(false, event.getCommand() + " : " + command.summarize()); 787 } 788 789 private final Server server_; 790 private final WorkbenchServerOperations serverOperations_; 791 private final EventBus eventBus_; 792 private final Session session_; 793 private final Provider<UserPrefs> pPrefs_; 794 private final WorkbenchMainView view_; 795 private final GlobalDisplay globalDisplay_; 796 private final Commands commands_; 797 private final RemoteFileSystemContext fsContext_; 798 private final FileDialogs fileDialogs_; 799 private final FileTypeRegistry fileTypeRegistry_; 800 private final WorkbenchContext workbenchContext_; 801 private final ProjectOpener projectOpener_; 802 private final ConsoleDispatcher consoleDispatcher_; 803 private final Provider<GitState> pGitState_; 804 private final TimeBufferedCommand metricsChangedCommand_; 805 private WorkbenchMetrics lastWorkbenchMetrics_; 806 private final WorkbenchNewSession newSession_; 807 private boolean nearQuotaWarningShown_ = false; 808 809 @SuppressWarnings("unused") private final SourceWindowManager sourceWindowManager_; 810 } 811