/* * InstallPackageDialog.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.views.packages.ui; import org.rstudio.core.client.Debug; import org.rstudio.core.client.StringUtil; import org.rstudio.core.client.files.FileSystemContext; import org.rstudio.core.client.files.FileSystemItem; import org.rstudio.core.client.widget.CanFocus; import org.rstudio.core.client.widget.CaptionWithHelp; import org.rstudio.core.client.widget.FocusHelper; import org.rstudio.core.client.widget.ModalDialog; import org.rstudio.core.client.widget.MultipleItemSuggestTextBox; import org.rstudio.core.client.widget.OperationWithInput; import org.rstudio.core.client.widget.ProgressIndicator; import org.rstudio.core.client.widget.ProgressOperationWithInput; import org.rstudio.core.client.widget.TextBoxWithButton; import org.rstudio.studio.client.RStudioGinjector; import org.rstudio.studio.client.common.FileDialogs; import org.rstudio.studio.client.common.GlobalDisplay; import org.rstudio.studio.client.server.ServerError; import org.rstudio.studio.client.server.ServerRequestCallback; import org.rstudio.studio.client.workbench.views.packages.model.PackageInstallContext; import org.rstudio.studio.client.workbench.views.packages.model.PackageInstallOptions; import org.rstudio.studio.client.workbench.views.packages.model.PackageInstallRequest; import org.rstudio.studio.client.workbench.views.packages.model.PackagesServerOperations; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.MultiWordSuggestOracle; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.SuggestBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; public class InstallPackageDialog extends ModalDialog<PackageInstallRequest> { public InstallPackageDialog( PackageInstallContext installContext, PackageInstallOptions defaultInstallOptions, PackagesServerOperations server, GlobalDisplay globalDisplay, OperationWithInput<PackageInstallRequest> operation) { super("Install Packages", operation); installContext_ = installContext; defaultInstallOptions_ = defaultInstallOptions; server_ = server; globalDisplay_ = globalDisplay; setOkButtonCaption("Install"); } @Override protected PackageInstallRequest collectInput() { // package install options String libraryPath = installContext_.getWriteableLibraryPaths().get( libraryListBox_.getSelectedIndex()); boolean installDependencies = installDependenciesCheckBox_.getValue(); PackageInstallOptions options = PackageInstallOptions.create( installFromRepository(), libraryPath, installDependencies); if (installFromRepository()) { return new PackageInstallRequest(packagesTextBox_.getItems(), options); } else { return new PackageInstallRequest(archiveFilePath_, options); } } @Override protected boolean validate(PackageInstallRequest request) { // check for package name if (installFromRepository() && (request.getPackages().size() == 0) || !installFromRepository() && request.getLocalPackage() == null) { globalDisplay_.showErrorMessage( "No Package Selected", "You must specify the package to install.", getPackageInputWidget()); return false; } else { return true; } } @Override protected Widget createMainWidget() { // vertical panel VerticalPanel mainPanel = new VerticalPanel(); mainPanel.setSpacing(2); mainPanel.setStylePrimaryName(RESOURCES.styles().mainWidget()); // source type reposCaption_ = new CaptionWithHelp("Install from:", "Configuring Repositories", "configuring_repositories"); reposCaption_.setIncludeVersionInfo(false); reposCaption_.setWidth("100%"); mainPanel.add(reposCaption_); packageSourceListBox_ = new ListBox(); packageSourceListBox_.setStylePrimaryName( RESOURCES.styles().packageSourceListBox()); packageSourceListBox_.addStyleName(RESOURCES.styles().extraBottomPad()); JsArrayString repos = installContext_.selectedRepositoryNames(); if (repos.length() == 1) { packageSourceListBox_.addItem("Repository (" + repos.get(0) + ")"); } else { StringBuilder reposItem = new StringBuilder(); reposItem.append("Repository ("); for (int i=0; i<repos.length(); i++) { if (i != 0) reposItem.append(", "); reposItem.append(repos.get(i)); } reposItem.append(")"); packageSourceListBox_.addItem(reposItem.toString()); } packageSourceListBox_.addItem("Package Archive File (" + installContext_.packageArchiveExtension() + ")"); mainPanel.add(packageSourceListBox_); // source panel container sourcePanel_ = new SimplePanel(); sourcePanel_.setStylePrimaryName(RESOURCES.styles().packageSourcePanel()); // repos source panel reposSourcePanel_ = new FlowPanel(); Label packagesLabel = new Label( "Packages (separate multiple with space or comma):"); packagesLabel.setStylePrimaryName(RESOURCES.styles().packagesLabel()); reposSourcePanel_.add(packagesLabel); packagesTextBox_ = new MultipleItemSuggestTextBox(); packagesSuggestBox_ = new SuggestBox(new PackageOracle(), packagesTextBox_); packagesSuggestBox_.setWidth("100%"); packagesSuggestBox_.setLimit(20); packagesSuggestBox_.addStyleName(RESOURCES.styles().extraBottomPad()); reposSourcePanel_.add(packagesSuggestBox_); sourcePanel_.setWidget(reposSourcePanel_); mainPanel.add(sourcePanel_); // archive source panel packageArchiveFile_ = new TextBoxWithButton( "Package archive:", "Browse...", browseForArchiveClickHandler_); // create check box here because manageUIState accesses it installDependenciesCheckBox_ = new CheckBox(); if (defaultInstallOptions_.getInstallFromRepository()) packageSourceListBox_.setSelectedIndex(0); else packageSourceListBox_.setSelectedIndex(1); manageUIState(); packageSourceListBox_.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { manageUIState(); if (!installFromRepository()) packageArchiveFile_.click(); } }); mainPanel.add(new Label("Install to Library:")); // library list box libraryListBox_ = new ListBox(); libraryListBox_.setWidth("100%"); libraryListBox_.addStyleName(RESOURCES.styles().extraBottomPad()); JsArrayString libPaths = installContext_.getWriteableLibraryPaths(); int selectedIndex = 0; for (int i=0; i<libPaths.length(); i++) { String libPath = libPaths.get(i); if (!installContext_.isDevModeOn()) { if (defaultInstallOptions_.getLibraryPath().equals(libPath)) selectedIndex = i; } if (libPath.equals(installContext_.getDefaultLibraryPath())) libPath = libPath + " [Default]"; libraryListBox_.addItem(libPath); } libraryListBox_.setSelectedIndex(selectedIndex); mainPanel.add(libraryListBox_); // install dependencies check box installDependenciesCheckBox_.addStyleName(RESOURCES.styles().installDependenciesCheckBox()); installDependenciesCheckBox_.setText("Install dependencies"); installDependenciesCheckBox_.setValue( defaultInstallOptions_.getInstallDependencies()); mainPanel.add(installDependenciesCheckBox_); mainPanel.add(new HTML("<br/>")); return mainPanel; } @Override protected void onDialogShown() { if (installFromRepository()) FocusHelper.setFocusDeferred(packagesSuggestBox_); else FocusHelper.setFocusDeferred(packageSourceListBox_); } private void manageUIState() { if (installFromRepository()) { reposCaption_.setHelpVisible(true); sourcePanel_.setWidget(reposSourcePanel_); FocusHelper.setFocusDeferred(packagesSuggestBox_); installDependenciesCheckBox_.setVisible(true); } else { reposCaption_.setHelpVisible(false); sourcePanel_.setWidget(packageArchiveFile_); FocusHelper.setFocusDeferred(packageArchiveFile_); installDependenciesCheckBox_.setVisible(false); } } private boolean installFromRepository() { return packageSourceListBox_.getSelectedIndex() == 0; } private CanFocus getPackageInputWidget() { if (installFromRepository()) return new CanFocus() { @Override public void focus() { packagesSuggestBox_.setFocus(true); }}; else return packageArchiveFile_; } private ClickHandler browseForArchiveClickHandler_ = new ClickHandler() { @Override public void onClick(ClickEvent event) { fileDialogs_.openFile( "Select Package Archive", fileSystemContext_, defaultArchiveDir_, new ProgressOperationWithInput<FileSystemItem>() { public void execute(FileSystemItem input, ProgressIndicator indicator) { indicator.onCompleted(); if (input == null) return; // update default archive dir defaultArchiveDir_ = input.getParentPath(); // set archive file path archiveFilePath_ = input; // update UI packageArchiveFile_.setText( StringUtil.shortPathName(input, "gwt-TextBox", 280)); } }); } }; private class PackageOracle extends MultiWordSuggestOracle { PackageOracle() { // no separators (strict prefix match) super(""); server_.availablePackages(null, new ServerRequestCallback<JsArrayString>() { @Override public void onResponseReceived(JsArrayString packages) { for (int i=0; i<packages.length(); i++) add(packages.get(i)); } @Override public void onError(ServerError error) { Debug.log("Error querying for packages: " + error.getUserMessage()); } }); } } static interface Styles extends CssResource { String mainWidget(); String packageSourcePanel(); String configureRepositoriesImage(); String configureRepositoriesLink(); String packagesLabel(); String extraBottomPad(); String installDependenciesCheckBox(); String packageSourceListBox(); String packageFileTextBox(); String packageFileBrowseButton(); } static interface Resources extends ClientBundle { @Source("InstallPackageDialog.css") Styles styles(); } static Resources RESOURCES = (Resources)GWT.create(Resources.class) ; public static void ensureStylesInjected() { RESOURCES.styles().ensureInjected(); } private final PackageInstallContext installContext_; private final PackageInstallOptions defaultInstallOptions_; private final PackagesServerOperations server_; private final GlobalDisplay globalDisplay_; private CaptionWithHelp reposCaption_; private ListBox packageSourceListBox_; private SimplePanel sourcePanel_; private FlowPanel reposSourcePanel_; private TextBoxWithButton packageArchiveFile_ = null; private MultipleItemSuggestTextBox packagesTextBox_ = null; private SuggestBox packagesSuggestBox_ = null; private ListBox libraryListBox_ = null; private CheckBox installDependenciesCheckBox_ = null; FileSystemItem archiveFilePath_ = null; private static FileSystemItem defaultArchiveDir_ = FileSystemItem.home(); private final FileSystemContext fileSystemContext_ = RStudioGinjector.INSTANCE.getRemoteFileSystemContext(); private final FileDialogs fileDialogs_ = RStudioGinjector.INSTANCE.getFileDialogs(); }