/*
* DirectoryContentsWidget.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.core.client.files.filedialog;
import com.google.gwt.dom.client.TableElement;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import org.rstudio.core.client.Point;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.dom.DomUtils;
import org.rstudio.core.client.events.HasSelectionCommitHandlers;
import org.rstudio.core.client.events.SelectionCommitEvent;
import org.rstudio.core.client.events.SelectionCommitHandler;
import org.rstudio.core.client.files.FileSystemContext;
import org.rstudio.core.client.files.FileSystemItem;
import org.rstudio.core.client.widget.DoubleClickState;
import org.rstudio.core.client.widget.ScrollPanelWithClick;
import org.rstudio.core.client.widget.SimplePanelWithProgress;
import org.rstudio.studio.client.common.filetypes.FileIconResources;
import java.util.HashMap;
public class DirectoryContentsWidget
extends Composite
implements HasSelectionHandlers<FileSystemItem>,
HasSelectionCommitHandlers<FileSystemItem>,
HasFocusHandlers, HasBlurHandlers
{
private static class FlexTableEx extends FlexTable
{
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler)
{
return addDomHandler(handler, MouseDownEvent.getType());
}
public HandlerRegistration addKeyDownHandler(KeyDownHandler handler)
{
return addDomHandler(handler, KeyDownEvent.getType());
}
public HandlerRegistration addFocusHandler(FocusHandler handler)
{
return addDomHandler(handler, FocusEvent.getType());
}
public HandlerRegistration addBlurHandler(BlurHandler handler)
{
return addDomHandler(handler, BlurEvent.getType());
}
}
public DirectoryContentsWidget(FileSystemContext context)
{
context_ = context;
table_ = new FlexTableEx();
table_.getElement().setTabIndex(0);
table_.setCellSpacing(0);
table_.setCellPadding(2);
table_.setSize("100%", "100%");
scrollPanel_ = new ScrollPanelWithClick(table_);
scrollPanel_.setSize("100%", "100%");
progressPanel_ = new SimplePanelWithProgress();
progressPanel_.setWidget(null);
initWidget(progressPanel_);
setStylePrimaryName(styles_.contents());
hookMouseEvents();
}
private void hookMouseEvents()
{
table_.addClickHandler(new ClickHandler()
{
public void onClick(ClickEvent event)
{
event.preventDefault();
event.stopPropagation();
// IE10: focusing the the table element causes the scroll panel to
// snap it to the top, scrolling the event target offscreen. Set
// and restore the position after focus.
if (DomUtils.getActiveElement() != table_.getElement())
{
int h = scrollPanel_.getHorizontalScrollPosition();
int v = scrollPanel_.getVerticalScrollPosition();
focusImpl_.focus(table_.getElement());
scrollPanel_.setHorizontalScrollPosition(h);
scrollPanel_.setVerticalScrollPosition(v);
}
HTMLTable.Cell cell = table_.getCellForEvent(event);
if (cell != null)
{
setSelectedRow(cell.getRowIndex());
if (doubleClick_.checkForDoubleClick(event.getNativeEvent()))
{
SelectionCommitEvent.fire(DirectoryContentsWidget.this,
getSelectedItem());
}
}
else
setSelectedRow(null);
}
});
table_.addMouseDownHandler(new MouseDownHandler()
{
public void onMouseDown(MouseDownEvent event)
{
event.preventDefault();
}
});
table_.addKeyDownHandler(new KeyDownHandler()
{
public void onKeyDown(KeyDownEvent event)
{
switch (event.getNativeKeyCode())
{
case KeyCodes.KEY_DOWN:
moveBy(event, 1);
break;
case KeyCodes.KEY_UP:
moveBy(event, -1);
break;
case KeyCodes.KEY_PAGEDOWN:
moveBy(event, 12);
break;
case KeyCodes.KEY_PAGEUP:
moveBy(event, -12);
break;
case KeyCodes.KEY_HOME:
event.preventDefault();
event.stopPropagation();
if (table_.getRowCount() > 0)
setSelectedRow(0);
break;
case KeyCodes.KEY_END:
event.preventDefault();
event.stopPropagation();
if (table_.getRowCount() > 0)
setSelectedRow(table_.getRowCount() - 1);
break;
case KeyCodes.KEY_ENTER:
event.preventDefault();
event.stopPropagation();
SelectionCommitEvent.fire(DirectoryContentsWidget.this,
getSelectedItem());
break;
}
}
private void moveBy(KeyDownEvent event, int offset)
{
event.stopPropagation();
event.preventDefault();
moveSelection(offset);
}
});
scrollPanel_.addClickHandler(new ClickHandler()
{
public void onClick(ClickEvent event)
{
event.preventDefault();
event.stopPropagation();
setSelectedRow(null);
}
});
scrollPanel_.addMouseDownHandler(new MouseDownHandler()
{
public void onMouseDown(MouseDownEvent event)
{
event.preventDefault();
}
});
}
private void moveSelection(int offset)
{
if (selectedRow_ == null)
{
if (table_.getRowCount() > 0)
setSelectedRow(0);
return;
}
int row = selectedRow_.intValue() + offset;
row = Math.max(0, Math.min(table_.getRowCount()-1, row));
setSelectedRow(row);
}
public void setSelectedRow(Integer row)
{
if (selectedRow_ != null)
{
table_.getRowFormatter().removeStyleName(
selectedRow_.intValue(),
"gwt-MenuItem-selected");
selectedRow_ = null;
selectedValue_ = null;
}
if (row != null
&& row.intValue() >= 0
&& row.intValue() < table_.getRowCount())
{
selectedRow_ = row.intValue();
table_.getRowFormatter().addStyleName(
selectedRow_,
"gwt-MenuItem-selected");
selectedValue_ = table_.getText(row.intValue(), COL_NAME);
TableRowElement rowEl = ((TableElement)table_.getElement().cast())
.getRows().getItem(selectedRow_);
int horizScroll = scrollPanel_.getHorizontalScrollPosition();
rowEl.scrollIntoView();
scrollPanel_.setHorizontalScrollPosition(horizScroll);
}
SelectionEvent.fire(DirectoryContentsWidget.this,
getSelectedItem());
}
public String getSelectedValue()
{
return selectedValue_;
}
public FileSystemItem getSelectedItem()
{
return items_.get(getSelectedValue());
}
public void showProgress(boolean show)
{
if (show)
progressPanel_.showProgress(300);
else
progressPanel_.setWidget(scrollPanel_);
}
public void clearContents()
{
table_.removeAllRows();
items_.clear();
selectedRow_ = null;
selectedValue_ = null;
scrollPanel_.scrollToTop();
scrollPanel_.scrollToLeft();
}
public void setContents(FileSystemItem[] contents,
FileSystemItem parentDirectory)
{
clearContents();
if (parentDirectory != null)
addItem(parentDirectory,
"..",
FileIconResources.INSTANCE.iconUpFolder());
for (FileSystemItem fsi : contents)
addItem(fsi, null, null);
showProgress(false);
}
private int addItem(FileSystemItem item,
String customName,
ImageResource customIcon)
{
if (customName == null)
customName = item.getName();
if (customIcon == null)
customIcon = context_.getIcon(item);
items_.put(customName, item);
int newRow = table_.insertRow(table_.getRowCount());
table_.setWidget(
newRow,
COL_ICON,
new Image(customIcon));
table_.setText(newRow, COL_NAME, customName);
table_.getCellFormatter().setStylePrimaryName(newRow,
COL_ICON,
styles_.columnIcon());
table_.getCellFormatter().setStylePrimaryName(newRow,
COL_NAME,
styles_.columnName());
if (!item.isDirectory())
{
table_.setText(newRow,
COL_SIZE,
StringUtil.formatFileSize(item.getLength()));
table_.setText(newRow,
COL_TIMESTAMP,
StringUtil.formatDate(item.getLastModified()));
table_.getCellFormatter().setStylePrimaryName(newRow,
COL_SIZE,
styles_.columnSize());
table_.getCellFormatter().setStylePrimaryName(newRow,
COL_TIMESTAMP,
styles_.columnDate());
}
else
{
((FlexTable.FlexCellFormatter)table_.getCellFormatter()).setColSpan(
newRow, COL_NAME, 3);
}
return newRow;
}
public Point getScrollPosition()
{
return new Point(scrollPanel_.getHorizontalScrollPosition(),
scrollPanel_.getVerticalScrollPosition());
}
public void setScrollPosition(Point p)
{
scrollPanel_.setVerticalScrollPosition(p.getY());
scrollPanel_.setHorizontalScrollPosition(p.getX());
}
public HandlerRegistration addSelectionHandler(
SelectionHandler<FileSystemItem> handler)
{
return addHandler(handler, SelectionEvent.getType());
}
public HandlerRegistration addSelectionCommitHandler(
SelectionCommitHandler<FileSystemItem> handler)
{
return addHandler(handler, SelectionCommitEvent.getType());
}
public void addDirectory(FileSystemItem directory)
{
int rowNum = addItem(directory, null, null);
TableElement table = (TableElement) table_.getElement().cast();
TableRowElement row = table.getRows().getItem(rowNum);
row.scrollIntoView();
scrollPanel_.setHorizontalScrollPosition(0);
setSelectedRow(rowNum);
}
public HandlerRegistration addFocusHandler(FocusHandler handler)
{
return table_.addFocusHandler(handler);
}
public HandlerRegistration addBlurHandler(BlurHandler handler)
{
return table_.addBlurHandler(handler);
}
public void setFocus(boolean focused)
{
if (focused)
focusImpl_.focus(table_.getElement());
else
focusImpl_.blur(table_.getElement());
}
private HashMap<String, FileSystemItem> items_ =
new HashMap<String, FileSystemItem>();
private final DoubleClickState doubleClick_ = new DoubleClickState();
private Integer selectedRow_;
private String selectedValue_;
private final FlexTableEx table_;
private final ScrollPanelWithClick scrollPanel_;
private final SimplePanelWithProgress progressPanel_;
private static final int COL_ICON = 0;
private static final int COL_NAME = 1;
private static final int COL_SIZE = 2;
private static final int COL_TIMESTAMP = 3;
private final FileDialogStyles styles_ = FileDialogResources.INSTANCE.styles();
private final FocusImpl focusImpl_ = FocusImpl.getFocusImplForPanel();
private final FileSystemContext context_;
}