/* * ShowAddinsDialog.java * * Copyright (C) 2009-15 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; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.cellview.client.ColumnSortEvent; import com.google.gwt.user.cellview.client.DataGrid; import com.google.gwt.user.cellview.client.TextColumn; import com.google.gwt.user.cellview.client.TextHeader; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.ProvidesKey; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SingleSelectionModel; import com.google.inject.Inject; import org.rstudio.core.client.ListUtil; import org.rstudio.core.client.StringUtil; import org.rstudio.core.client.ListUtil.FilterPredicate; import org.rstudio.core.client.js.JsUtil; import org.rstudio.core.client.theme.RStudioDataGridResources; import org.rstudio.core.client.theme.RStudioDataGridStyle; import org.rstudio.core.client.widget.FilterWidget; import org.rstudio.core.client.widget.ModalDialog; import org.rstudio.core.client.widget.ModifyKeyboardShortcutsWidget; import org.rstudio.core.client.widget.OperationWithInput; import org.rstudio.core.client.widget.ThemedButton; import org.rstudio.studio.client.RStudioGinjector; import org.rstudio.studio.client.common.HelpLink; import org.rstudio.studio.client.workbench.addins.Addins.AddinExecutor; import org.rstudio.studio.client.workbench.addins.Addins.RAddin; import org.rstudio.studio.client.workbench.addins.Addins.RAddins; import org.rstudio.studio.client.workbench.addins.AddinsCommandManager; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class BrowseAddinsDialog extends ModalDialog<Command> { public BrowseAddinsDialog(OperationWithInput<Command> operation) { super("Addins", operation); RStudioGinjector.INSTANCE.injectMembers(this); setOkButtonCaption("Execute"); filterWidget_ = new FilterWidget() { @Override public void filter(String query) { BrowseAddinsDialog.this.filter(query); } }; filterWidget_.getElement().getStyle().setFloat(Style.Float.LEFT); helpLink_ = new HelpLink("Using RStudio Addins", "rstudio_addins", false); helpLink_.getElement().getStyle().setFloat(Style.Float.RIGHT); keyProvider_ = new ProvidesKey<RAddin>() { @Override public Object getKey(RAddin addin) { return addin.hashCode(); } }; table_ = new DataGrid<RAddin>(1000, RES, keyProvider_); table_.setWidth("500px"); table_.setHeight("400px"); selectionModel_ = new SingleSelectionModel<RAddin>(); selectionModel_.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { @Override public void onSelectionChange(SelectionChangeEvent event) { selection_ = selectionModel_.getSelectedObject(); } }); table_.setSelectionModel(selectionModel_); table_.setEmptyTableWidget(emptyTableLabel("Loading addins...")); addColumns(); dataProvider_ = new ListDataProvider<RAddin>(); dataProvider_.addDataDisplay(table_); originalData_ = new ArrayList<RAddin>(); // sync to current addins addins_ = addinsCommandManager_.getRAddins(); List<RAddin> data = new ArrayList<RAddin>(); for (String key : JsUtil.asIterable(addins_.keys())) data.add(addins_.get(key)); dataProvider_.setList(data); originalData_ = data; table_.setEmptyTableWidget(emptyTableLabel("No addins available")); addLeftWidget(new ThemedButton("Keyboard Shortcuts...", new ClickHandler() { @Override public void onClick(ClickEvent event) { ModifyKeyboardShortcutsWidget widget = new ModifyKeyboardShortcutsWidget("addin"); widget.showModal(); } })); FlowPanel headerPanel = new FlowPanel(); headerPanel.add(filterWidget_); headerPanel.add(helpLink_); container_ = new VerticalPanel(); container_.add(headerPanel); container_.add(new VerticalSeparator("4px")); container_.add(table_); } private static class VerticalSeparator extends Composite { public VerticalSeparator(String size) { FlowPanel panel = new FlowPanel(); panel.setHeight(size); initWidget(panel); } } @Inject private void initialize(AddinsCommandManager addinsCommandManager) { addinsCommandManager_ = addinsCommandManager; } private void addColumns() { // Package ---- pkgColumn_ = new TextColumn<RAddin>() { @Override public String getValue(RAddin addin) { return addin.getPackage(); } }; pkgColumn_.setSortable(true); table_.addColumn(pkgColumn_, new TextHeader("Package")); table_.setColumnWidth(pkgColumn_, "120px"); // Name ---- nameColumn_ = new TextColumn<RAddin>() { @Override public String getValue(RAddin addin) { return StringUtil.truncate(addin.getName(), 120, "..."); } }; nameColumn_.setSortable(true); table_.addColumn(nameColumn_, new TextHeader("Name")); table_.setColumnWidth(nameColumn_, "120px"); // Description ---- descColumn_ = new TextColumn<RAddin>() { @Override public String getValue(RAddin addin) { return addin.getDescription(); } }; descColumn_.setSortable(true); table_.addColumn(descColumn_, new TextHeader("Description")); table_.addColumnSortHandler(new ColumnSortEvent.Handler() { @Override public void onColumnSort(ColumnSortEvent event) { int index = -1; if (event.getColumn().equals(pkgColumn_)) index = 0; else if (event.getColumn().equals(nameColumn_)) index = 1; else if (event.getColumn().equals(descColumn_)) index = 2; if (index == -1) return; sort(index, event.isSortAscending()); } }); } private void sort(final int index, final boolean forward) { Collections.sort(dataProvider_.getList(), new Comparator<RAddin>() { @Override public int compare(RAddin o1, RAddin o2) { String f1 = ""; String f2 = ""; if (index == 0) { f1 = o1.getPackage(); f2 = o2.getPackage(); } else if (index == 1) { f1 = o1.getName(); f2 = o2.getName(); } else if (index == 2) { f1 = o1.getDescription(); f2 = o2.getDescription(); } return forward ? f1.compareTo(f2) : f2.compareTo(f1); } }); } private void filter(String query) { final String[] splat = query.toLowerCase().split("\\s+"); List<RAddin> data = ListUtil.filter(originalData_, new FilterPredicate<RAddin>() { @Override public boolean test(RAddin object) { for (String el : splat) { boolean match = object.getName().toLowerCase().contains(el) || object.getPackage().toLowerCase().contains(el); if (!match) return false; } return true; } }); dataProvider_.setList(data); } @Override protected Command collectInput() { if (selection_ == null) { return new Command() { @Override public void execute() { } }; } return new ExecuteAddinCommand( addins_.get(selection_.getId()), new AddinExecutor()); } private static class ExecuteAddinCommand implements Command { public ExecuteAddinCommand(RAddin addin, AddinExecutor executor) { addin_ = addin; executor_ = executor; } @Override public void execute() { executor_.execute(addin_); } private final RAddin addin_; private final AddinExecutor executor_; } @Override protected Widget createMainWidget() { return container_; } private Label emptyTableLabel(String caption) { Label label = new Label(caption); label.getElement().getStyle().setMarginTop(20, Unit.PX); label.getElement().getStyle().setColor("#888"); return label; } // Private members ---- private final VerticalPanel container_; private final FilterWidget filterWidget_; private final HelpLink helpLink_; private final DataGrid<RAddin> table_; private TextColumn<RAddin> pkgColumn_; private TextColumn<RAddin> nameColumn_; private TextColumn<RAddin> descColumn_; private final ProvidesKey<RAddin> keyProvider_; private final ListDataProvider<RAddin> dataProvider_; private final SingleSelectionModel<RAddin> selectionModel_; private List<RAddin> originalData_; private RAddins addins_; private RAddin selection_; // Injected ---- private AddinsCommandManager addinsCommandManager_; // Resources, etc ---- public interface Resources extends RStudioDataGridResources { @Source({RStudioDataGridStyle.RSTUDIO_DEFAULT_CSS}) Styles dataGridStyle(); } public interface Styles extends RStudioDataGridStyle { } private static final Resources RES = GWT.create(Resources.class); static { RES.dataGridStyle().ensureInjected(); } }