Package org.gwtlib.client.table.ui

Source Code of org.gwtlib.client.table.ui.Table$Style

/*
* Copyright 2008 Sander Berents
*
* Licensed 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.gwtlib.client.table.ui;

import java.util.ArrayList;
import java.util.List;

import org.gwtlib.client.table.ColumnLayout;
import org.gwtlib.client.table.ContentProvider;
import org.gwtlib.client.table.Row;
import org.gwtlib.client.table.Rows;
import org.gwtlib.client.table.RowsCache;
import org.gwtlib.client.ui.Messages;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;

/**
* Table.
*
* CSS Style Rules:
* <ul>
* <li>.gwtlib-Table { the table itself }</li>
* <li>.gwtlib-Table-empty { no data message }</li>
* <li>.gwtlib-Table-error { error message }</li>
* <li>.gwtlib-Header { the table header cells }</li>
* <li>.gwtlib-Header-sortable { the table header cell for a sortable column }</li>
* <li>.gwtlib-Column-ascending { the table header cell for a ascending sorted column }</li>
* <li>.gwtlib-Column-descending { the table header cell for a descending sorted column }</li>
* <li>.gwtlib-Row { a row }</li>
* <li>.gwtlib-Row-even { an even row }</li>
* <li>.gwtlib-Row-odd { an odd row }</li>
* <li>.gwtlib-Row-empty { a row without data (when there is less data than table rows) }</li>
* <li>.gwtlib-Row-selected { a row that has the <code>Row.State.SELECT</code> state set }</li>
* </ul>
*
* @author Sander Berents
*/
public class Table extends AbstractComposite implements SourcesTableEvents {
  private static final String STYLE               = "gwtlib-Table";
  private static final String STYLE_HEADER_ROW    = "gwtlib-Header-Row";
  private static final String STYLE_HEADER        = "gwtlib-Header";
  private static final String STYLE_SORTABLE      = "gwtlib-Header-sortable";
  private static final String STYLE_ASCENDING     = "gwtlib-Column-ascending";
  private static final String STYLE_DESCENDING    = "gwtlib-Column-descending";
  private static final String STYLE_ROW           = "gwtlib-Row";
  private static final String STYLE_ROW_EVEN      = "gwtlib-Row-even";
  private static final String STYLE_ROW_ODD       = "gwtlib-Row-odd";
  private static final String STYLE_ROW_EMPTY     = "gwtlib-Row-empty";
  private static final String STYLE_ROW_SELECT    = "gwtlib-Row-selected";
  private static final String STYLE_COLUMN_SELECT = "gwtlib-Column-selected";
  private static final String STYLE_CELL          = "gwtlib-Cell";
  private static final String STYLE_CELL_SELECT   = "gwtlib-Cell-selected";
  private static final String STYLE_NO_DATA       = "gwtlib-Table-empty";
  private static final String STYLE_ERROR         = "gwtlib-Table-error";

  public interface Style {
    public static final int SINGLE_SELECT = 1 << 0;
    public static final int MULTI_SELECT  = 1 << 1;
  };

  protected FlexTable _panel;
  protected FlexTable _table;
  protected ScrollPanel _scroll;
  protected ColumnLayout _layout;
  protected ContentProvider _provider;
  protected RowsCache _cache;
  protected int _begin = 0;
  protected int _size = 10;
  protected int _minsize = _size;
  protected List<TableListener> _listeners;
  protected Messages _messages = (Messages)GWT.create(Messages.class);

  public Table(ColumnLayout layout) {
    this(layout, true);
  }

  @Deprecated
  public Table(Messages messages, ColumnLayout layout) {
    this(layout, true);
    _messages = messages;
  }

  protected Table(ColumnLayout layout, boolean initWidget) {
    super(Style.SINGLE_SELECT);
    _layout = layout;
    _cache = new RowsCache();
    _listeners = new ArrayList<TableListener>();

    _table = new FlexTable();
    _table.setCellSpacing(0);
    _table.setCellSpacing(0);
    _table.setSize("100%", "auto");
    _scroll = new ScrollPanel(_table);

    _panel = new FlexTable();
    _panel.setCellSpacing(0);
    _panel.setCellPadding(0);
    _panel.setWidget(0, 0, _scroll);
    _panel.getRowFormatter().setVerticalAlign(0, HasVerticalAlignment.ALIGN_TOP);
    _panel.getFlexCellFormatter().addStyleName(0, 0, "scroll-cell");

    if(initWidget) initWidget(_panel);
    _table.setStylePrimaryName(STYLE);

    initOptimalSize();

    Window.addResizeHandler(new ResizeHandler() {
      @Override
      public void onResize(ResizeEvent event) {
        initOptimalSize();
      }
    });

    _table.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        HTMLTable.Cell cell = _table.getCellForEvent(event);
        if(cell != null) {
          int row = cell.getRowIndex();
          int col = cell.getCellIndex();
 
          GWT.log("onCellClicked " + row + "," + col, null);
          fireCellClickedEvent(row, col);
          int c = toActualColumnPos(col);
          Column column = _layout.getColumn(c);
          if(row == 0) {
            if(column.isSortable()) {
              sort(c, column.getSortDirection() != Column.Sort.ASCENDING);
            }
          } else {
            Row r = _cache.getRow(_begin + row - 1);
            if(r != null) {
              fireCellClickedEvent(r, column);
              fireRowClickedEvent(r);
            }
          }
        }
      }
    });
  }

  protected void initOptimalSize() {
    _scroll.setVisible(false);
    DeferredCommand.addCommand(new Command() {
      public void execute() {
        Element e = _panel.getCellFormatter().getElement(0, 0);
        int w = DOM.getElementPropertyInt(e, "offsetWidth");
        int h = DOM.getElementPropertyInt(e, "offsetHeight");
        GWT.log("Initial table size is " + w + "," + h, null);
        if(w == 0 || h == 0) {
          _scroll.setVisible(true); // Skip all this if it is used inside a TabPanel
        } else {
          w -= 2; if(w < 0) w = 0;
          h -= 2; if(h < 0) h = 0;
          _scroll.setSize("" + w + "px", "" + h + "px");
          _scroll.setVisible(true);
          w = DOM.getElementPropertyInt(e, "offsetWidth");
          h = DOM.getElementPropertyInt(e, "offsetHeight");
          GWT.log("Now table size is " + w + "," + h, null);
        }
      }
    });
  }

  /**
   * Sets the content provider. This is required.
   * @param provider
   */
  public void setContentProvider(ContentProvider provider) {
    _provider = provider;
  }

  /**
   * Sets the number of rows to display in the table and redraws it.
   * @param size
   */
  public void setSize(int size) {
    clear();
    _size = size;
    update();
  }

  /**
   * Sets the minimal number of rows to display in the table.
   * @param size
   */
  public void setMinimumSize(int size) {
    _minsize = size;
  }

  /**
   * Sets the top row offset and redraws the table.
   * @param pos Zero based position.
   */
  public void setPosition(int pos) {
    _begin = pos;
    update();
  }

  public void refreshRowState() {
    Rows rows = getRows(_begin);
    int r = 1;
    for(int i = 0; i < rows.size(); ++i, ++r) {
      Row row = rows.getRow(i);
      _table.getRowFormatter().setStyleName(r, STYLE_ROW);
      _table.getRowFormatter().addStyleName(r, i % 2 == 0 ? STYLE_ROW_EVEN : STYLE_ROW_ODD);
      if(row.hasState(Row.State.SELECT)) _table.getRowFormatter().addStyleName(r, STYLE_ROW_SELECT);
      for(int j = 0, c = 0; j < _layout.getTotalColumnCount(); ++j) {
        Column column = _layout.getColumn(j);
        if(column.isVisible()) {
          _table.getCellFormatter().removeStyleName(r, c, STYLE_COLUMN_SELECT);
          _table.getCellFormatter().removeStyleName(r, c, STYLE_CELL_SELECT);
          if(column.hasState(Column.State.SELECT)) {
            _table.getCellFormatter().addStyleName(r, c, STYLE_COLUMN_SELECT);
            if(row.hasState(Row.State.SELECT)) _table.getCellFormatter().addStyleName(r, c, STYLE_CELL_SELECT);
          }
          c++;
        }
      }
    }
    int nclear = Math.min(_size - rows.size(), _table.getRowCount() - rows.size() - 1);
    while(nclear-- > 0) {
      for(int j = 0, c = 0; j < _layout.getTotalColumnCount(); ++j) {
        Column column = _layout.getColumn(j);
        if(column.isVisible()) {
          _table.getCellFormatter().removeStyleName(r, c, STYLE_COLUMN_SELECT);
          _table.getCellFormatter().removeStyleName(r, c, STYLE_CELL_SELECT);
          _table.getRowFormatter().setStyleName(r, STYLE_ROW);
          _table.getRowFormatter().addStyleName(r, STYLE_ROW_EMPTY);
          _table.clearCell(r, c++);
        }
      }
      ++r;
    }
  }

  /**
   * Redraws the table.
   */
  public void update() {
    fetch(_begin, _begin + _size);
  }

  /**
   * Clears the row cache, resets the current position to zero, fetches
   * up to <code>size</code> rows of data and redraws the table.
   */
  public void reset() {
    _begin = 0;
    _cache.clear();
    update();
  }

  /**
   * Clears the row cache, resets the current position to zero.
   */
  public void clear() {
    _begin = 0;
    _cache.clear();
    while(_table.getRowCount() > 0) _table.removeRow(0);
    renderHeader();
    render(0);
  }

  protected void fetch(int begin, int end) {
    int sortId = -1;
    boolean ascending = false;
    int sortColumn = _layout.getSortColumn();
    if(sortColumn >= 0) {
      sortId = _layout.getColumn(sortColumn).getId();
      ascending = _layout.getColumn(sortColumn).getSortDirection() == Column.Sort.ASCENDING;
    }
    Rows rows = _cache.getRows(begin, end, sortId, ascending);
    if(rows.size() == end - begin) {
      onSuccess(rows);
    } else {
      fireLoadEvent();
      _provider.load(begin, end, sortId, ascending);
    }
  }

  public void show(int column, boolean visible) {
    Column col = _layout.getColumn(column);
    if(fireShowColumnEvent(col, visible)) {
      col.setVisible(visible);
    }
  }

  public void sort(int column, boolean ascending) {
    Column col = _layout.getColumn(column);
    if(fireSortColumnEvent(col, ascending)) {
      int sortColumn = _layout.getSortColumn();
      if(sortColumn != -1) {
        setColumnSortStyle(toVisibleColumnPos(sortColumn), Column.Sort.NONE);
        _layout.getColumn(sortColumn).setSortDirection(Column.Sort.NONE);
      }
      int dir = ascending ? Column.Sort.ASCENDING : Column.Sort.DESCENDING;
      _layout.getColumn(column).setSortDirection(dir);
      _begin = 0;
      fetch(_begin, _size);
    }
  }

  /**
   * Returns the column layout information.
   * @return ColumnLayout object.
   */
  public ColumnLayout getColumnLayout() {
    return _layout;
  }

  /**
   * Returns currently displayed rows.
   * @return Rows object.
   */
  public Rows getRows() {
    return getRows(_begin);
  }

  protected void renderHeader() {
    _table.getRowFormatter().setStylePrimaryName(0, STYLE_HEADER_ROW);
    for(int i = 0, c = 0; i < _layout.getTotalColumnCount(); ++i) {
      Column column = _layout.getColumn(i);
      if(column.isVisible()) {
        _table.setWidget(0, c, column);
        _table.getCellFormatter().setStylePrimaryName(0, c, STYLE_HEADER);
        if(column.isSortable()) _table.getCellFormatter().addStyleName(0, c, STYLE_SORTABLE);
        setColumnSortStyle(c, column.getSortDirection());
        c++;
      }
    }
  }

  protected void render(int pos) {
    Rows rows = getRows(pos);
    GWT.log("Rendering " + rows.size() + " rows", null);
    int r = 1;
    for(int i = 0; i < rows.size(); ++i, ++r) {
      final Row row = rows.getRow(i);
      for(int j = 0, c = 0; j < _layout.getTotalColumnCount(); ++j) {
        final Column column = _layout.getColumn(j);
        if(column.isVisible()) {
          final Widget widget = column.getRenderer().render(row, column, row.getValue(j));
          if(widget instanceof HasClickHandlers) {
            ((HasClickHandlers)widget).addClickHandler(new ClickHandler() {
              @Override
              public void onClick(ClickEvent event) {
                fireClickEvent(row, column, widget);       
              }
            });
          }
          if(widget instanceof HasChangeHandlers) {
            ((HasChangeHandlers)widget).addChangeHandler(new ChangeHandler() {
              @Override
              public void onChange(ChangeEvent event) {
                fireChangeEvent(row, column, widget);

              }
            });
          }
          fireRenderCellEvent(row, column, widget);
          _table.setWidget(r, c, widget);
          _table.getFlexCellFormatter().addStyleName(r, c, STYLE_CELL);
          c++;
        }
      }
    }
    for(; r <= Math.min(_minsize, _size); ++r) {
      for(int j = 0, c = 0; j < _layout.getTotalColumnCount(); ++j) {
        final Column column = _layout.getColumn(j);
        if(column.isVisible()) _table.setWidget(r, c++, new Label());
      }     
    }
    resetEmpty();
    refreshRowState();
  }

  protected void renderEmpty(String message, String style) {
    int r = 1;
    while(_table.getRowCount() > 1) _table.removeRow(1);
    _table.setWidget(r, 0, new Label(message));
    FlexTable.FlexCellFormatter formatter = (FlexTable.FlexCellFormatter)_table.getCellFormatter();
    formatter.setColSpan(r, 0, _layout.getVisibleColumnCount());
    formatter.setRowSpan(r, 0, _size);
    formatter.setStylePrimaryName(r, 0, style);
    _table.getRowFormatter().setStyleName(r, STYLE_ROW);
    _table.getRowFormatter().addStyleName(r, STYLE_ROW_EMPTY);
  }

  protected void resetEmpty() {
    int r = 1;
    FlexTable.FlexCellFormatter formatter = (FlexTable.FlexCellFormatter)_table.getCellFormatter();
    if(_table.getRowCount() > 1) {
      formatter.removeStyleName(r, 0, STYLE_NO_DATA);
      formatter.removeStyleName(r, 0, STYLE_ERROR);
      formatter.setColSpan(r, 0, 1);
      formatter.setRowSpan(r, 0, 1);
    }
  }

  protected Rows getRows(int begin) {
    int sortId = -1;
    boolean ascending = false;
    int sortColumn = _layout.getSortColumn();
    if(sortColumn >= 0) {
      sortId = _layout.getColumn(sortColumn).getId();
      ascending = _layout.getColumn(sortColumn).getSortDirection() == Column.Sort.ASCENDING;
    }
    Rows rows = _cache.getRows(begin, begin + _size, sortId, ascending);
    return rows;
  }

  /**
   * Should be called by the handler of <code>ContentProvider</code> requests to
   * pass the loaded rows to the table. Usually this will be called by an asynchronous
   * callback <code>AsyncCallback.onSuccess</code> implementation.
   * @param rows
   */
  public void onSuccess(Rows rows) {
    while(_table.getRowCount() > 0) _table.removeRow(0);
    _cache.merge(rows);
    fireLoadedEvent(true);
    fireRenderEvent();
    renderHeader();
    if(rows.size() == 0) {
      renderEmpty(_messages.none(), STYLE_NO_DATA);
    } else {
      render(rows.getBegin());
    }
    _begin = rows.getBegin();
    fireRenderedEvent();
    initOptimalSize();
  }

  /**
   * Should be called by the handler of <code>ContentProvider</code> requests when
   * a failure occurs. Usually this will be called by an asynchronous callback
   * <code>AsyncCallback.onFailure</code> implementation.
   * @param caught
   */
  public void onFailure(Throwable caught) {
    while(_table.getRowCount() > 0) _table.removeRow(0);
    _cache.clear();
    fireLoadedEvent(false);
    fireRenderEvent();
    renderHeader();
    render(_begin);
    renderEmpty(_messages.error(caught == null ? null : caught.getMessage()), STYLE_ERROR);
    fireRenderedEvent();
    initOptimalSize();
  }

  public void addTableListener(TableListener listener) {
    _listeners.add(listener);
  }

  public void removeTableListener(TableListener listener) {
    _listeners.remove(listener);
  }

  protected void fireCellClickedEvent(int row, int column) {
    for(TableListener listener : _listeners) {
      listener.onCellClicked(this, row, column);
    }
  }

  protected void fireCellClickedEvent(Row row, Column column) {
    for(TableListener listener : _listeners) {
      listener.onCellClicked(this, row, column);
    }
  }

  protected void fireRowClickedEvent(Row row) {
    for(TableListener listener : _listeners) {
      listener.onRowClicked(this, row);
    }
  }

  protected void fireClickEvent(Row row, Column column, Widget widget) {
    for(TableListener listener : _listeners) {
      listener.onClick(this, row, column, widget);
    }
  }

  protected void fireChangeEvent(Row row, Column column, Widget widget) {
    for(TableListener listener : _listeners) {
      listener.onChange(this, row, column, widget);
    }
  }

  protected boolean fireShowColumnEvent(Column column, boolean visible) {
    for(TableListener listener : _listeners) {
      if(!listener.onShowColumn(this, column, visible)) return false;
    }
    return true;
  }

  protected boolean fireSortColumnEvent(Column column, boolean ascending) {
    for(TableListener listener : _listeners) {
      if(!listener.onSortColumn(this, column, ascending)) return false;
    }
    return true;
  }

  protected void fireLoadEvent() {
    for(TableListener listener : _listeners) {
      listener.onLoad(this);
    }
  }

  protected void fireLoadedEvent(boolean success) {
    for(TableListener listener : _listeners) {
      listener.onLoaded(this, success);
    }
  }

  protected void fireRenderEvent() {
    for(TableListener listener : _listeners) {
      listener.onRender(this);
    }
  }

  protected void fireRenderedEvent() {
    for(TableListener listener : _listeners) {
      listener.onRendered(this);
    }
  }

  protected void fireRenderCellEvent(Row row, Column column, Widget widget) {
    for(TableListener listener : _listeners) {
      listener.onRenderCell(this, row, column, widget);
    }
  }

  /**
   * Converts visible column to actual column.
   * @param col
   * @return
   */
  private int toActualColumnPos(int col) {
    int pos = -1;
    while(col >= 0) {
      if(_layout.getColumn(++pos).isVisible()) --col;
    }
    return pos;
  }

  /**
   * Converts visible column to actual column.
   * @param col
   * @return
   */
  private int toVisibleColumnPos(int col) {
    int pos = -1;
    while(col >= 0) {
      if(_layout.getColumn(col--).isVisible()) ++pos;
    }
    return pos;
  }

  public void setColumnSortStyle(int col, int dir) {
    int row = 0;
    FlexTable.FlexCellFormatter formatter = _table.getFlexCellFormatter();
    switch(dir) {
      case Column.Sort.ASCENDING:
        formatter.removeStyleName(row, col, STYLE_DESCENDING);
        formatter.addStyleName(row, col, STYLE_ASCENDING);
        break;
      case Column.Sort.DESCENDING:
        formatter.removeStyleName(row, col, STYLE_ASCENDING);
        formatter.addStyleName(row, col, STYLE_DESCENDING);
        break;
      default:
        formatter.removeStyleName(row, col, STYLE_ASCENDING);
        formatter.removeStyleName(row, col, STYLE_DESCENDING);
        break;
    }
  }
}
TOP

Related Classes of org.gwtlib.client.table.ui.Table$Style

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.