/*
* EnvironmentObjectGrid.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.view;
import java.util.ArrayList;
import java.util.List;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.theme.res.ThemeStyles;
import org.rstudio.studio.client.workbench.views.environment.view.RObjectEntry.Categories;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.ClickableTextCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.builder.shared.TableCellBuilder;
import com.google.gwt.dom.builder.shared.TableRowBuilder;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.user.cellview.client.AbstractCellTable;
import com.google.gwt.user.cellview.client.AbstractCellTableBuilder;
import com.google.gwt.user.cellview.client.AbstractHeaderOrFooterBuilder;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.view.client.DefaultSelectionEventManager;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionModel;
public class EnvironmentObjectGrid extends EnvironmentObjectDisplay
{
public interface Style extends CssResource
{
String objectGridColumn();
String objectGridHeader();
String checkColumn();
String objectGrid();
String valueColumn();
String dataFrameValueCol();
}
public interface Resources extends ClientBundle
{
@Source("EnvironmentObjectGrid.css")
Style style();
}
public EnvironmentObjectGrid(EnvironmentObjectDisplay.Host host,
EnvironmentObjectsObserver observer,
String environmentName)
{
super(host, observer, environmentName);
style_ = ((Resources)GWT.create(Resources.class)).style();
style_.ensureInjected();
selection_ = new MultiSelectionModel<RObjectEntry>(
RObjectEntry.KEY_PROVIDER);
createColumns();
setTableBuilder(new EnvironmentObjectGridBuilder(this));
setHeaderBuilder(new GridHeaderBuilder(this, false));
setSkipRowHoverCheck(true);
setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED);
setSelectionModel(selection_,
DefaultSelectionEventManager.<RObjectEntry>createCheckboxManager(0));
addStyleName(style_.objectGrid());
}
// Returns the objects that should be considered selected.
// - If one or more objects are manually selected, that set of objects is
// returned.
// - If no objects are manually selected but the list is filtered, the
// the objects that match the filter are returned.
// - If no selection or filter is present, an empty list is returned
// (generally this causes operations to act on the whole list)
@Override
public List<String> getSelectedObjects()
{
boolean hasFilter = !host_.getFilterText().isEmpty();
ArrayList<String> selectedObjectNames = new ArrayList<String>();
ArrayList<String> filteredObjectNames = new ArrayList<String>();
List<RObjectEntry> objects = getVisibleItems();
for (RObjectEntry object: objects)
{
if (object.visible)
{
if (hasFilter)
{
filteredObjectNames.add(object.rObject.getName());
}
if (selection_.isSelected(object))
{
selectedObjectNames.add(object.rObject.getName());
}
}
}
return selectedObjectNames.size() == 0 ? filteredObjectNames :
selectedObjectNames;
}
@Override
public void clearSelection()
{
setSelectAll(false);
redrawHeaders();
}
@Override
public void setEnvironmentName(String environmentName)
{
// When the environment changes, we need to redraw the headers to
// (possibly) adjust for the presence or absence of the selection
// column.
super.setEnvironmentName(environmentName);
if (columns_.size() > 0)
{
columns_.get(0).setWidth(selectionEnabled() ? 20 : 25);
}
setColumnWidths();
}
// Private methods ---------------------------------------------------------
private void createColumns()
{
checkColumn_ = new Column<RObjectEntry, Boolean>(
new CheckboxCell(false, false))
{
@Override
public Boolean getValue(RObjectEntry value)
{
return selection_.isSelected(value);
}
};
addColumn(checkColumn_);
checkHeader_ = new Header<Boolean>(new CheckboxCell())
{
@Override
public Boolean getValue()
{
return selectAll_;
}
};
checkHeader_.setUpdater(new ValueUpdater<Boolean>()
{
@Override
public void update(Boolean value)
{
if (selectAll_ != value)
{
setSelectAll(value);
}
}
});
columns_.add(new ObjectGridColumn(
new ClickableTextCell(filterRenderer_), "Name",
selectionEnabled() ? 20 : 25,
ObjectGridColumn.COLUMN_NAME, host_)
{
@Override
public String getValue(RObjectEntry object)
{
return object.rObject.getName();
}
});
columns_.add(new ObjectGridColumn(
new ClickableTextCell(), "Type", 15,
ObjectGridColumn.COLUMN_TYPE, host_)
{
@Override
public String getValue(RObjectEntry object)
{
return object.rObject.getType();
}
});
columns_.add(new ObjectGridColumn(
new ClickableTextCell(), "Length", 10,
ObjectGridColumn.COLUMN_LENGTH, host_)
{
@Override
public String getValue(RObjectEntry object)
{
return (new Integer(object.rObject.getLength())).toString();
}
});
columns_.add(new ObjectGridColumn(
new ClickableTextCell(), "Size", 12,
ObjectGridColumn.COLUMN_SIZE, host_)
{
@Override
public String getValue(RObjectEntry object)
{
return StringUtil.formatFileSize(object.rObject.getSize());
}
});
columns_.add(new ObjectGridColumn(
new ClickableTextCell(filterRenderer_),
"Value", 38,
ObjectGridColumn.COLUMN_VALUE, host_)
{
@Override
public String getValue(RObjectEntry object)
{
return object.getDisplayValue();
}
});
for (ObjectGridColumn column: columns_)
{
if (column.getType() == ObjectGridColumn.COLUMN_VALUE)
{
attachClickToInvoke(column);
}
addColumn(column);
}
setColumnWidths();
}
private void setSelectAll(boolean selected)
{
List<RObjectEntry> objects = getVisibleItems();
for (RObjectEntry object: objects)
{
if (object.visible)
{
selection_.setSelected(object, selected);
}
}
selectAll_ = selected;
}
private class GridHeaderBuilder
extends AbstractHeaderOrFooterBuilder<RObjectEntry>
{
public GridHeaderBuilder(AbstractCellTable<RObjectEntry> table,
boolean isFooter)
{
super(table, isFooter);
setSortIconStartOfLine(false);
}
@Override
protected boolean buildHeaderOrFooterImpl()
{
// Render the "select all" checkbox header cell
TableRowBuilder row = startRow();
if (selectionEnabled())
{
TableCellBuilder selectAll = row.startTH();
selectAll.className(style_.objectGridHeader() + " " +
style_.checkColumn());
renderHeader(selectAll, new Cell.Context(0, 0, null), checkHeader_);
selectAll.end();
}
// Render a header for each column
for (int i = 0; i < columns_.size(); i++)
{
ObjectGridColumn col = columns_.get(i);
TableCellBuilder cell = row.startTH();
cell.className(style_.objectGridHeader());
Cell.Context context = new Cell.Context(0, i, null);
renderSortableHeader(cell, context, col.getHeader(),
i == host_.getSortColumn(),
host_.getAscendingSort());
cell.endTH();
}
row.end();
return true;
}
}
private void setColumnWidths()
{
int start = 0;
if (selectionEnabled())
{
setColumnWidth(start++, "5%");
}
else
{
// Clear the width of the last column (it's going to go away entirely
// if we're dropping the selection column).
clearColumnWidth(columns_.size());
}
for (int i = 0; i < columns_.size(); i++)
{
setColumnWidth(
start + i,
new Integer(columns_.get(i).getWidth()).toString() + "%");
}
}
// builds individual rows of the object table
private class EnvironmentObjectGridBuilder
extends AbstractCellTableBuilder<RObjectEntry>
{
public EnvironmentObjectGridBuilder(
AbstractCellTable<RObjectEntry> cellTable)
{
super(cellTable);
}
@Override
protected void buildRowImpl(RObjectEntry rowValue, int absRowIndex)
{
if (!rowValue.visible)
return;
TableRowBuilder row = startRow();
if (selectionEnabled())
{
TableCellBuilder check = row.startTD();
check.className(style_.checkColumn());
check.style().width(5, Unit.PCT);
renderCell(check, createContext(0), checkColumn_, rowValue);
check.endTD();
}
for (int i = 0; i < columns_.size(); i++)
{
ObjectGridColumn col = columns_.get(i);
TableCellBuilder td = row.startTD();
String className = style_.objectGridColumn();
if (col.getType() == ObjectGridColumn.COLUMN_VALUE)
{
className += " " + style_.valueColumn();
if (host_.enableClickableObjects() &&
rowValue.getCategory() == Categories.Data)
{
className += " " + style_.dataFrameValueCol() +
" " +
ThemeStyles.INSTANCE.environmentDataFrameCol();
}
if (rowValue.isPromise())
{
className += " " + environmentStyle_.unevaluatedPromise();
}
td.title(rowValue.getDisplayValue());
}
if (col.getType() == ObjectGridColumn.COLUMN_NAME)
{
td.title(rowValue.rObject.getName());
}
td.className(className);
renderCell(td, createContext(i+1), col, rowValue);
td.endTD();
}
row.end();
}
}
private Column<RObjectEntry, Boolean> checkColumn_;
private Header<Boolean> checkHeader_;
private ArrayList<ObjectGridColumn> columns_ =
new ArrayList<ObjectGridColumn>();
private Style style_;
private SelectionModel<RObjectEntry> selection_;
private boolean selectAll_ = false;
}