/* * WorkbenchScreen.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.studio.client.workbench.ui; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Document; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.SelectionHandler; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.*; import com.google.inject.Inject; import com.google.inject.Provider; import org.rstudio.core.client.BrowseCap; import org.rstudio.core.client.SerializedCommand; import org.rstudio.core.client.SerializedCommandQueue; import org.rstudio.core.client.Size; import org.rstudio.core.client.TimeBufferedCommand; import org.rstudio.core.client.command.CommandBinder; import org.rstudio.core.client.command.Handler; import org.rstudio.core.client.events.WindowStateChangeEvent; import org.rstudio.core.client.layout.WindowState; import org.rstudio.core.client.theme.ModuleTabLayoutPanel; import org.rstudio.core.client.widget.FontSizer; import org.rstudio.core.client.widget.Toolbar; import org.rstudio.studio.client.application.Desktop; import org.rstudio.studio.client.application.events.ChangeFontSizeEvent; import org.rstudio.studio.client.application.events.ChangeFontSizeHandler; import org.rstudio.studio.client.application.events.EventBus; import org.rstudio.studio.client.application.ui.appended.ApplicationEndedPopupPanel; import org.rstudio.studio.client.common.GlobalDisplay; import org.rstudio.studio.client.workbench.FileMRUList; import org.rstudio.studio.client.workbench.WorkbenchMainView; import org.rstudio.studio.client.workbench.commands.Commands; import org.rstudio.studio.client.workbench.events.*; import org.rstudio.studio.client.workbench.model.Session; import org.rstudio.studio.client.workbench.model.WorkbenchMetrics; import org.rstudio.studio.client.workbench.ui.PaneManager.Tab; import org.rstudio.studio.client.workbench.views.edit.Edit; import org.rstudio.studio.client.workbench.views.edit.Edit.Shim; import org.rstudio.studio.client.workbench.views.edit.events.ShowEditorEvent; import org.rstudio.studio.client.workbench.views.help.events.ActivateHelpEvent; import org.rstudio.studio.client.workbench.views.plots.PlotsTab; import org.rstudio.studio.client.workbench.views.source.events.LastSourceDocClosedEvent; import org.rstudio.studio.client.workbench.views.source.events.LastSourceDocClosedHandler; public class WorkbenchScreen extends Composite implements WorkbenchMainView, SelectionHandler<Integer>, ActivatePaneHandler, RequiresResize { interface MyCommandBinder extends CommandBinder<Commands, WorkbenchScreen>{} static MyCommandBinder commandBinder = GWT.create(MyCommandBinder.class); @Inject public WorkbenchScreen(GlobalDisplay globalDisplay, EventBus eventBus, Session session, Provider<PaneManager> pPaneManager, final Edit.Shim edit, Commands commands, final Provider<FileMRUList> mruList, FontSizeManager fontSizeManager, OptionsLoader.Shim optionsLoader) { globalDisplay_ = globalDisplay; eventBus_ = eventBus; session_ = session; edit_ = edit; optionsLoader_ = optionsLoader; if (!BrowseCap.isMacintoshDesktop() || BrowseCap.isCocoaDesktop()) commands.macPreferences().remove(); if (!Desktop.isDesktop() || !Desktop.getFrame().supportsFullscreenMode()) { commands.toggleFullScreen().remove(); } eventBus_.addHandler(ActivatePaneEvent.TYPE, this); eventBus_.addHandler(ShowEditorEvent.TYPE, edit); eventBus_.addHandler(ChangeFontSizeEvent.TYPE, new ChangeFontSizeHandler() { public void onChangeFontSize(ChangeFontSizeEvent event) { FontSizer.setNormalFontSize(Document.get(), event.getFontSize()); Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { // Causes the console width to be remeasured doOnPaneSizesChanged(); } }); } }); FontSizer.setNormalFontSize(Document.get(), fontSizeManager.getSize()); paneManager_ = pPaneManager.get(); tabsPanel_ = paneManager_.getPanel(); tabsPanel_.setSize("100%", "100%"); tabsPanel_.addStyleDependentName("Workbench"); // Prevent doOnPaneSizesChanged() from being called more than once // every N milliseconds. Note that the act of sending the client metrics // to the server also has its own buffer, so this one only needs to // minimize the expense of doing the calculation, not of sending data to // the server. paneSizesChangedCommand_ = new TimeBufferedCommand(200) { @Override protected void performAction(boolean shouldSchedulePassive) { assert !shouldSchedulePassive; doOnPaneSizesChanged(); } }; eventBus.addHandler(SessionInitEvent.TYPE, new SessionInitHandler() { public void onSessionInit(SessionInitEvent sie) { prefetch(); mruList.get(); } }); eventBus.addHandler(LastSourceDocClosedEvent.TYPE, new LastSourceDocClosedHandler() { public void onLastSourceDocClosed(LastSourceDocClosedEvent event) { paneManager_.getSourceLogicalWindow().onWindowStateChange( new WindowStateChangeEvent(WindowState.HIDE)); } }); ((PlotsTab) paneManager_.getTab(Tab.Plots)).addResizeHandler(new ResizeHandler() { public void onResize(ResizeEvent event) { onPaneSizesChanged(); } }); tabsPanel_.addSplitterResizedHandler(new SplitterResizedHandler() { public void onSplitterResized(SplitterResizedEvent event) { onPaneSizesChanged(); } }); // init widget initWidget(tabsPanel_); commandBinder.bind(commands, this); } private void prefetch() { final SerializedCommandQueue prefetchQueue = new SerializedCommandQueue(); Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { onPaneSizesChanged(); } }); Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { for (final WorkbenchTab tab : paneManager_.getAllTabs()) prefetchQueue.addCommand(new SerializedCommand() { public void onExecute(Command continuation) { tab.prefetch(continuation); } }); prefetchQueue.addCommand(new SerializedCommand() { public void onExecute(Command continuation) { ApplicationEndedPopupPanel.prefetch(continuation); } }); prefetchQueue.addCommand(new SerializedCommand() { public void onExecute(Command continuation) { edit_.forceLoad(true, continuation); } }); prefetchQueue.addCommand(new SerializedCommand() { public void onExecute(Command continuation) { optionsLoader_.forceLoad(true, continuation); } }); } }); } public void onResize() { tabsPanel_.onResize(); onPaneSizesChanged(); } @Override protected void onLoad() { super.onLoad(); eventBus_.fireEvent(new WorkbenchLoadedEvent()); } private void onPaneSizesChanged() { paneSizesChangedCommand_.nudge(); } private void doOnPaneSizesChanged() { // console width int consoleWidth = paneManager_.getConsole().getCharacterWidth(); // if the console is hidden then just use its last value. if there // has never been a valid value then then lastMetrics_ console width // will be 0, which the server will know to ignore if (paneManager_.getConsole().getOffsetWidth() <= 0) consoleWidth = lastMetrics_.getConsoleWidth(); // plots size (don't allow negative metrics) WorkbenchTabPanel plotPanel = paneManager_.getOwnerTabPanel(Tab.Plots); Size deckPanelSize = new Size( plotPanel.getOffsetWidth(), plotPanel.getOffsetHeight() - ModuleTabLayoutPanel.BAR_HEIGHT); Size plotsSize = new Size( Math.max(deckPanelSize.width, 0), Math.max(deckPanelSize.height - Toolbar.DEFAULT_HEIGHT, 0)); double devicePixelRatio = 1.0; if (BrowseCap.isMacintoshDesktop()) devicePixelRatio = BrowseCap.devicePixelRatio(); WorkbenchMetrics metrics = WorkbenchMetrics.create(consoleWidth, plotsSize.width, plotsSize.height, devicePixelRatio); // make sure we don't send very similar metrics values twice (it is // an expensive operation since it involves at least 2 http requests) if (!metrics.closeEnoughToPrevious(lastMetrics_)) { lastMetrics_ = metrics; eventBus_.fireEvent(new WorkbenchMetricsChangedEvent(metrics)); } session_.persistClientState(); } public void onSelection(SelectionEvent<Integer> integerSelectionEvent) { eventBus_.fireEvent(new PushClientStateEvent()); } @Override public void onActivatePane(ActivatePaneEvent event) { paneManager_.activateTab(event.getPane()); } private void fireEventDelayed(final GwtEvent<?> event, int delayMs) { new Timer() { @Override public void run() { eventBus_.fireEvent(event); } }.schedule(delayMs); } @Handler void onActivateEnvironment() { paneManager_.activateTab(Tab.Environment); } @Handler void onActivateHistory() { paneManager_.activateTab(Tab.History); } @Handler void onActivateFiles() { paneManager_.activateTab(Tab.Files); } @Handler void onActivatePlots() { paneManager_.activateTab(Tab.Plots); } @Handler void onActivatePackages() { paneManager_.activateTab(Tab.Packages); } @Handler void onActivateHelp() { paneManager_.activateTab(Tab.Help); fireEventDelayed(new ActivateHelpEvent(), 200); } @Handler void onActivateVcs() { paneManager_.activateTab(Tab.VCS); } @Handler void onActivateBuild() { paneManager_.activateTab(Tab.Build); } @Handler void onActivatePresentation() { paneManager_.activateTab(Tab.Presentation); } @Handler void onActivateViewer() { paneManager_.activateTab(Tab.Viewer); } @Handler void onActivateConnections() { paneManager_.activateTab(Tab.Connections); } @Handler void onLayoutZoomEnvironment() { paneManager_.zoomTab(Tab.Environment); } @Handler void onLayoutZoomHistory() { paneManager_.zoomTab(Tab.History); } @Handler void onLayoutZoomFiles() { paneManager_.zoomTab(Tab.Files); } @Handler void onLayoutZoomPlots() { paneManager_.zoomTab(Tab.Plots); } @Handler void onLayoutZoomPackages() { paneManager_.zoomTab(Tab.Packages); } @Handler void onLayoutZoomHelp() { paneManager_.zoomTab(Tab.Help); fireEventDelayed(new ActivateHelpEvent(), 200); } @Handler void onLayoutZoomVcs() { paneManager_.zoomTab(Tab.VCS); } @Handler void onLayoutZoomBuild() { paneManager_.zoomTab(Tab.Build); } @Handler void onLayoutZoomViewer() { paneManager_.zoomTab(Tab.Viewer); } @Handler void onLayoutZoomConnections() { paneManager_.zoomTab(Tab.Connections); } @Handler void onMacPreferences() { onShowOptions(); } @Handler void onShowOptions() { optionsLoader_.showOptions(); } @Handler void onVersionControlHelp() { globalDisplay_.openRStudioLink("using_version_control"); } public Widget asWidget() { return this; } private TimeBufferedCommand paneSizesChangedCommand_; private WorkbenchMetrics lastMetrics_ = WorkbenchMetrics.create(0,0,0,1.0); private final GlobalDisplay globalDisplay_; private final EventBus eventBus_; private final Session session_; private final Shim edit_; private final org.rstudio.studio.client.workbench.ui.OptionsLoader.Shim optionsLoader_; private final MainSplitPanel tabsPanel_ ; private PaneManager paneManager_; }