/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.trader.client.ui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* A composite widget combines a data table, a table header, a scrollbar,
* and a caption and a {@link HelpLink help anchor}.
* <br>
* Each row of the data table displays an instance of type T. How the instance
* is displayed in controlled by a set of {@link GridCellRenderer renderers}
* attached to each column.
* <br>
* The styles used
* <LI>table-caption : for the caption
* <LI>column-header : for the column headers
* <LI>row-odd : for odd numbered rows
* <LI>row-even : for even numbered rows
*
*
* @author Pinaki Poddar
*
* @param <T> the type of data being displayed.
*/
public class ScrollableTable<T> extends Composite {
private FlexTable _main;
private Grid _caption;
private FlexTable _header;
private ScrollPanel _scroll;
private List<GridCellRenderer<T>> _renderers;
private List<T> _rows;
private boolean _stripeRows;
private static final String STYLE_CAPTION = "table-caption";
private static final String STYLE_HEADER = "column-header";
private static final String STYLE_MAIN = "table";
private static final String ROW_EVEN = "row-even";
private static final String ROW_ODD = "row-odd";
/**
* Create a scrollable table with the given caption and given pixel dimension.
* The table will not be backed by a data storage model.
*
* @param caption of the table
* @param w width in pixel
* @param h height in pixel
*/
public ScrollableTable(String caption, int w, int h) {
this(caption, w+"px", h+"px", false);
}
/**
* Create a scrollable table with the given caption and given pixel dimension.
* The table will not be backed by a data storage model.
*
* @param caption of the table
* @param w width in pixel
* @param h height in pixel
* @param updatable whether the table data will be backed by a storage such
* that row can be updated rather than always inserted
*/
public ScrollableTable(String caption, int w, int h, boolean updatable) {
this(caption, w+"px", h+"px", updatable);
}
/**
* Create a scrollable table with the given caption and given dimension.
*
* @param caption of the table
* @param w width in given unit
* @param h height in given unit
* @param updatable whether the table data will be backed by a storage such
* that row can be updated rather than always inserted
*/
public ScrollableTable(String caption, String w, String h, boolean updatable) {
super();
_renderers = new ArrayList<GridCellRenderer<T>>();
VerticalPanel vert = new VerticalPanel();
vert.setSpacing(0);
_caption = new Grid(1,2);
_caption.setWidth("100%");
setCaption(caption);
vert.add(_caption);
_header = new FlexTable();
_header.addStyleName(STYLE_HEADER);
_header.setWidth("100%");
_main = new FlexTable();
_main.addStyleName(STYLE_MAIN);
_main.setWidth("100%");
_main.setBorderWidth(0);
_scroll = new ScrollPanel();
_scroll.setSize(w, h);
_scroll.setAlwaysShowScrollBars(true);
_scroll.add(_main);
vert.add(_header);
vert.add(_scroll);
if (updatable) {
_rows = new ArrayList<T>();
}
initWidget(vert);
}
@SuppressWarnings("unchecked")
public List<T> getModel() {
return _rows == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(_rows);
}
public void setCaption(String str) {
HTML caption = new HTML(str);
caption.addStyleName(STYLE_CAPTION);
_caption.setWidget(0, 0, caption);
}
public void setSize(int w, int h) {
int dh = 0;
if (_caption != null) {
dh += _caption.getOffsetHeight();
}
dh += _header.getOffsetHeight();
_scroll.setPixelSize(w, h-dh);
}
public void setRenderer(int column, GridCellRenderer<T> r) {
if (column > _renderers.size()) {
for (int i = _renderers.size(); i <= column; i++) {
_renderers.add(null);
}
_renderers.add(r);
} else if (column == _renderers.size()) {
_renderers.add(r);
} else {
_renderers.set(column, r);
}
}
public void setColumnHeader(int column, String txt, String width) {
HTML header = new HTML(txt);
header.addStyleName(STYLE_HEADER);
_header.setWidget(0, column, header);
_main.getColumnFormatter().setWidth(column, width);
}
public void setStripeRows(boolean stripe) {
_stripeRows = stripe;
}
public boolean isStripeRows() {
return _stripeRows;
}
public void insert(T data) {
setRow(_main.getRowCount(), data, null);
}
public void remove(T data) {
int i = findRow(data);
if (i != -1) {
_main.removeRow(i);
_rows.remove(i);
}
}
/**
* Update entire row.
*/
public void update(T data, Integer[] columns) {
int i = findRow(data);
if (i != -1) {
setRow(i, data, columns);
} else {
insert(data);
}
}
public void updateCell(int row, int column, Widget widget, boolean animate) {
if (animate) {
FadeEffect fadeOut = new FadeEffect(_main.getWidget(row, column), false);
fadeOut.scheduleRepeating(10);
DOM.setElementAttribute(widget.getElement(), "opacity", "0.0");
_main.setWidget(row, column, widget);
FadeEffect fadeIn = new FadeEffect(_main.getWidget(row, column), true);
fadeIn.scheduleRepeating(10);
} else {
_main.setWidget(row, column, widget);
}
}
public int getRowCount() {
return _main.getRowCount();
}
/**
* Sets the cells of an existing row.
* Calls each renderer.
* @param row
* @param data
*/
private void setRow(int row, T data, Integer[] columns) {
if (_rows != null) {
if (row < 0 || row >= _rows.size())
_rows.add(data);
else
_rows.set(row, data);
}
for (int i = 0; i < _renderers.size(); i++) {
GridCellRenderer<T> r = _renderers.get(i);
if (r == null)
continue;
if (containsColumn(columns, i)) {
Widget widget = r.render(data);
if (widget != null)
_main.setWidget(row, i, widget);
}
}
if (isStripeRows()) {
_main.getRowFormatter().setStylePrimaryName(row, row%2 == 0? ROW_EVEN : ROW_ODD);
}
_scroll.scrollToBottom();
}
public int findRow(T data) {
if (_rows == null || data == null)
return -1;
return _rows.indexOf(data);
}
private boolean containsColumn(Integer[] columns, int i) {
if (columns == null)
return true;
for (int j = 0; j < columns.length; j++) {
if (columns[j] == i)
return true;
}
return false;
}
public T get(int i) {
if (_rows == null || i < 0 || i >= _rows.size())
return null;
return _rows.get(i);
}
public void scrollToBottom() {
_scroll.scrollToBottom();
}
public void addHelp(final String url) {
if (_caption == null) {
return;
}
_caption.getColumnFormatter().setWidth(0, "95%");
HelpLink help = new HelpLink(url);
_caption.setWidget(0, 1, help);
_caption.getCellFormatter().setHorizontalAlignment(0, 1, HasHorizontalAlignment.ALIGN_RIGHT);
}
}