/*
* EnvironmentPane.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.environment;
import java.util.ArrayList;
import java.util.List;
import org.rstudio.core.client.DebugFilePosition;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.widget.Operation;
import org.rstudio.core.client.widget.SearchWidget;
import org.rstudio.core.client.widget.SecondaryToolbar;
import org.rstudio.core.client.widget.Toolbar;
import org.rstudio.core.client.widget.ToolbarButton;
import org.rstudio.core.client.widget.ToolbarPopupMenu;
import org.rstudio.studio.client.application.events.EventBus;
import org.rstudio.studio.client.common.GlobalDisplay;
import org.rstudio.studio.client.common.ImageMenuItem;
import org.rstudio.studio.client.common.icons.StandardIcons;
import org.rstudio.studio.client.server.ServerError;
import org.rstudio.studio.client.server.ServerRequestCallback;
import org.rstudio.studio.client.server.Void;
import org.rstudio.studio.client.workbench.commands.Commands;
import org.rstudio.studio.client.workbench.model.Session;
import org.rstudio.studio.client.workbench.prefs.model.UIPrefs;
import org.rstudio.studio.client.workbench.ui.WorkbenchPane;
import org.rstudio.studio.client.workbench.views.console.events.SendToConsoleEvent;
import org.rstudio.studio.client.workbench.views.environment.model.CallFrame;
import org.rstudio.studio.client.workbench.views.environment.model.EnvironmentContextData;
import org.rstudio.studio.client.workbench.views.environment.model.EnvironmentFrame;
import org.rstudio.studio.client.workbench.views.environment.model.EnvironmentServerOperations;
import org.rstudio.studio.client.workbench.views.environment.model.ObjectContents;
import org.rstudio.studio.client.workbench.views.environment.model.RObject;
import org.rstudio.studio.client.workbench.views.environment.view.EnvironmentObjects;
import org.rstudio.studio.client.workbench.views.environment.view.EnvironmentObjectsObserver;
import org.rstudio.studio.client.workbench.views.environment.view.EnvironmentResources;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
public class EnvironmentPane extends WorkbenchPane
implements EnvironmentPresenter.Display,
EnvironmentObjectsObserver
{
@Inject
public EnvironmentPane(Commands commands,
EventBus eventBus,
GlobalDisplay globalDisplay,
EnvironmentServerOperations serverOperations,
Session session,
UIPrefs prefs)
{
super("Environment");
commands_ = commands;
eventBus_ = eventBus;
server_ = serverOperations;
globalDisplay_ = globalDisplay;
prefs_ = prefs;
expandedObjects_ = new ArrayList<String>();
scrollPosition_ = 0;
isClientStateDirty_ = false;
environments_ = null;
EnvironmentContextData environmentState =
session.getSessionInfo().getEnvironmentState();
environmentName_ = environmentState.environmentName();
environmentIsLocal_ = environmentState.environmentIsLocal();
EnvironmentPaneResources.INSTANCE.environmentPaneStyle().ensureInjected();
ensureWidget();
}
// WorkbenchPane overrides -------------------------------------------------
@Override
protected Toolbar createMainToolbar()
{
Toolbar toolbar = new Toolbar();
toolbar.addLeftWidget(commands_.loadWorkspace().createToolbarButton());
toolbar.addLeftWidget(commands_.saveWorkspace().createToolbarButton());
toolbar.addLeftSeparator();
toolbar.addLeftWidget(createImportMenu());
toolbar.addLeftSeparator();
toolbar.addLeftWidget(commands_.clearWorkspace().createToolbarButton());
toolbar.addLeftSeparator();
toolbar.addLeftWidget(commands_.refreshEnvironment().createToolbarButton());
ToolbarPopupMenu menu = new ToolbarPopupMenu();
menu.addItem(createViewMenuItem(EnvironmentObjects.OBJECT_LIST_VIEW));
menu.addItem(createViewMenuItem(EnvironmentObjects.OBJECT_GRID_VIEW));
viewButton_ = new ToolbarButton(
nameOfViewType(EnvironmentObjects.OBJECT_LIST_VIEW),
imageOfViewType(EnvironmentObjects.OBJECT_LIST_VIEW),
menu);
toolbar.addRightWidget(viewButton_);
return toolbar;
}
@Override
protected SecondaryToolbar createSecondaryToolbar()
{
SecondaryToolbar toolbar = new SecondaryToolbar();
environmentMenu_ = new EnvironmentPopupMenu();
environmentButton_ = new ToolbarButton(
friendlyEnvironmentName(),
imageOfEnvironment(environmentName_, environmentIsLocal_),
environmentMenu_);
toolbar.addLeftWidget(environmentButton_);
SearchWidget searchWidget = new SearchWidget(new SuggestOracle() {
@Override
public void requestSuggestions(Request request, Callback callback)
{
// no suggestions
callback.onSuggestionsReady(
request,
new Response(new ArrayList<Suggestion>()));
}
});
searchWidget.addValueChangeHandler(new ValueChangeHandler<String>() {
@Override
public void onValueChange(ValueChangeEvent<String> event)
{
objects_.setFilterText(event.getValue());
}
});
searchWidget.getElement().getStyle().setMarginTop(1, Unit.PX);
toolbar.addRightWidget(searchWidget);
return toolbar;
}
@Override
protected Widget createMainWidget()
{
objects_ = new EnvironmentObjects(this);
return objects_;
}
// EnviromentPresenter.Display implementation ------------------------------
@Override
public void addObject(RObject object)
{
objects_.addObject(object);
}
@Override
public void addObjects(JsArray<RObject> objects)
{
objects_.addObjects(objects);
}
@Override
public void removeObject(String objectName)
{
objects_.removeObject(objectName);
}
@Override
public void setContextDepth(int contextDepth)
{
objects_.setContextDepth(contextDepth);
// if the environment we're about to show is nested, turn off the toolbar
// commands that act on the global environment
Boolean commandsEnabled = contextDepth == 0;
commands_.loadWorkspace().setEnabled(commandsEnabled);
commands_.saveWorkspace().setEnabled(commandsEnabled);
commands_.importDatasetFromFile().setEnabled(commandsEnabled);
commands_.importDatasetFromURL().setEnabled(commandsEnabled);
dataImportButton_.setEnabled(commandsEnabled);
}
@Override
public void clearObjects()
{
objects_.clearObjects();
expandedObjects_.clear();
scrollPosition_ = 0;
isClientStateDirty_ = true;
}
@Override
public void setEnvironmentName(String environmentName, boolean local)
{
environmentName_ = environmentName;
environmentButton_.setText(friendlyEnvironmentName());
environmentButton_.setLeftImage(imageOfEnvironment(environmentName,
local));
objects_.setEnvironmentName(friendlyEnvironmentName());
if (environmentName.equals("R_GlobalEnv"))
commands_.clearWorkspace().setEnabled(true);
else
commands_.clearWorkspace().setEnabled(false);
}
@Override
public void setCallFrames(JsArray<CallFrame> frameList, boolean autoSize)
{
objects_.setCallFrames(frameList, autoSize);
}
@Override
public int getScrollPosition()
{
return scrollPosition_;
}
@Override
public void setScrollPosition(int scrollPosition)
{
objects_.setScrollPosition(scrollPosition);
}
@Override
public void setExpandedObjects(JsArrayString objects)
{
objects_.setExpandedObjects(objects);
expandedObjects_.clear();
for (int idx = 0; idx < objects.length(); idx++)
{
expandedObjects_.add(objects.get(idx));
}
}
@Override
public String[] getExpandedObjects()
{
return expandedObjects_.toArray(new String[0]);
}
@Override
public List<String> getSelectedObjects()
{
return objects_.getSelectedObjects();
}
@Override
public void clearSelection()
{
objects_.clearSelection();
}
@Override
public void changeContextDepth(int newDepth)
{
server_.setContextDepth(newDepth, new ServerRequestCallback<Void>()
{
@Override
public void onError(ServerError error)
{
globalDisplay_.showErrorMessage("Error opening call frame", error.getUserMessage());
}
});
}
public boolean clientStateDirty()
{
return isClientStateDirty_;
}
public void setClientStateClean()
{
isClientStateDirty_ = false;
}
@Override
public void resize()
{
objects_.onResize();
}
@Override
public void setBrowserRange(DebugFilePosition range)
{
objects_.updateLineNumber(range.getLine());
}
@Override
public void setObjectDisplayType(int type)
{
viewButton_.setText(nameOfViewType(type));
viewButton_.setLeftImage(imageOfViewType(type));
objects_.setObjectDisplay(type);
}
@Override
public int getObjectDisplayType()
{
return objects_.getObjectDisplay();
}
@Override
public int getSortColumn()
{
return objects_.getSortColumn();
}
@Override
public boolean getAscendingSort()
{
return objects_.getAscendingSort();
}
@Override
public void setSort(int sortColumn, boolean sortAscending)
{
objects_.setSort(sortColumn, sortAscending);
}
@Override
public void setViewDirty()
{
isClientStateDirty_ = true;
}
// EnviromentObjects.Observer implementation -------------------------------
public void setPersistedScrollPosition(int scrollPosition)
{
scrollPosition_ = scrollPosition;
isClientStateDirty_ = true;
}
public void setObjectExpanded(String objectName)
{
expandedObjects_.add(objectName);
isClientStateDirty_ = true;
}
public void setObjectCollapsed(String objectName)
{
expandedObjects_.remove(objectName);
isClientStateDirty_ = true;
}
public void viewObject(String objectName)
{
executeFunctionForObject("View", objectName);
}
@Override
public boolean getShowInternalFunctions()
{
return prefs_.showInternalFunctionsInTraceback().getValue();
}
@Override
public void setShowInternalFunctions(boolean show)
{
prefs_.showInternalFunctionsInTraceback().setProjectValue(show);
}
public void fillObjectContents(final RObject object,
final Operation onCompleted)
{
server_.getObjectContents(object.getName(),
new ServerRequestCallback<ObjectContents>()
{
@Override
public void onResponseReceived(ObjectContents contents)
{
object.setDeferredContents(contents.getContents());
onCompleted.execute();
}
@Override
public void onError(ServerError error)
{
onCompleted.execute();
}
});
}
// Private methods ---------------------------------------------------------
private void executeFunctionForObject(String function, String objectName)
{
String editCode =
function + "(" + StringUtil.toRSymbolName(objectName) + ")";
SendToConsoleEvent event = new SendToConsoleEvent(editCode, true);
eventBus_.fireEvent(event);
}
private Widget createImportMenu()
{
ToolbarPopupMenu menu = new ToolbarPopupMenu();
menu.addItem(commands_.importDatasetFromFile().createMenuItem(false));
menu.addItem(commands_.importDatasetFromURL().createMenuItem(false));
dataImportButton_ = new ToolbarButton(
"Import Dataset",
StandardIcons.INSTANCE.import_dataset(),
menu);
return dataImportButton_;
}
private String friendlyEnvironmentName()
{
return friendlyNameOfEnvironment(environmentName_);
}
private String friendlyNameOfEnvironment(String name)
{
if (name.equals("R_GlobalEnv"))
return GLOBAL_ENVIRONMENT_NAME;
else if (name.equals("base"))
return "package:base";
else
return name;
}
private ImageResource imageOfEnvironment(String name, boolean local)
{
if (name.endsWith("()"))
return EnvironmentResources.INSTANCE.functionEnvironment();
else if (name.equals("R_GlobalEnv"))
return EnvironmentResources.INSTANCE.globalEnvironment();
else if (name.startsWith("package:") ||
name.equals("base") ||
local)
return EnvironmentResources.INSTANCE.packageEnvironment();
else
return EnvironmentResources.INSTANCE.attachedEnvironment();
}
private void setEnvironments(JsArray<EnvironmentFrame> environments)
{
environments_ = environments;
rebuildEnvironmentMenu();
}
private void rebuildEnvironmentMenu()
{
environmentMenu_.clearItems();
if (environments_ == null)
{
return;
}
for (int i = 0; i < environments_.length(); i++)
{
final EnvironmentFrame frame = environments_.get(i);
ImageResource img = imageOfEnvironment(frame.getName(),
frame.isLocal());
environmentMenu_.addItem(ImageMenuItem.create(img,
friendlyNameOfEnvironment(frame.getName()),
new Scheduler.ScheduledCommand()
{
@Override
public void execute()
{
loadEnvironmentFrame(frame);
}
}, 2));
}
}
// Called to load a new environment into the environment pane.
private void loadEnvironmentFrame(final EnvironmentFrame frame)
{
ServerRequestCallback<Void> callback = new ServerRequestCallback<Void>()
{
@Override
public void onResponseReceived(Void v)
{
setEnvironmentName(frame.getName(), frame.isLocal());
}
@Override
public void onError(ServerError error)
{
}
};
// If the frame's an active call frame, set it by its index
if (frame.getFrame() > 0)
server_.setEnvironmentFrame(frame.getFrame(), callback);
// Otherwise, set it by its name
else
server_.setEnvironment(frame.getName(), callback);
}
private String nameOfViewType(int type)
{
if (type == EnvironmentObjects.OBJECT_LIST_VIEW)
return "List";
else if (type == EnvironmentObjects.OBJECT_GRID_VIEW)
return "Grid";
return "";
}
private ImageResource imageOfViewType(int type)
{
if (type == EnvironmentObjects.OBJECT_LIST_VIEW)
return EnvironmentResources.INSTANCE.objectListView();
else if (type == EnvironmentObjects.OBJECT_GRID_VIEW)
return EnvironmentResources.INSTANCE.objectGridView();
return null;
}
private MenuItem createViewMenuItem(final int type)
{
return ImageMenuItem.create(
imageOfViewType(type),
nameOfViewType(type),
new Scheduler.ScheduledCommand()
{
@Override
public void execute()
{
setObjectDisplayType(type);
}
}, 2);
}
// An extension of the toolbar popup menu that gets environment names from
// the server when the menu is invoked.
private class EnvironmentPopupMenu extends ToolbarPopupMenu
{
@Override
public void getDynamicPopupMenu
(final ToolbarPopupMenu.DynamicPopupMenuCallback callback)
{
server_.getEnvironmentNames(
new ServerRequestCallback<JsArray<EnvironmentFrame>>()
{
@Override
public void onResponseReceived(JsArray<EnvironmentFrame> response)
{
setEnvironments(response);
callback.onPopupMenu(environmentMenu_);
}
@Override
public void onError(ServerError error)
{
// Just live with a stale list.
callback.onPopupMenu(environmentMenu_);
}
});
}
}
public static final String GLOBAL_ENVIRONMENT_NAME = "Global Environment";
private final Commands commands_;
private final EventBus eventBus_;
private final GlobalDisplay globalDisplay_;
private final EnvironmentServerOperations server_;
private final UIPrefs prefs_;
private ToolbarButton dataImportButton_;
private ToolbarPopupMenu environmentMenu_;
private ToolbarButton environmentButton_;
private ToolbarButton viewButton_;
private EnvironmentObjects objects_;
private ArrayList<String> expandedObjects_;
private int scrollPosition_;
private boolean isClientStateDirty_;
private JsArray<EnvironmentFrame> environments_;
private String environmentName_;
private boolean environmentIsLocal_;
}