Package com.sencha.gxt.widget.core.client.grid

Source Code of com.sencha.gxt.widget.core.client.grid.GroupingView$GroupingViewStyle

/**
* Sencha GXT 3.0.0 - Sencha for GWT
* Copyright(c) 2007-2012, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
package com.sencha.gxt.widget.core.client.grid;

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

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.Event;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.Point;
import com.sencha.gxt.data.shared.SortDir;
import com.sencha.gxt.data.shared.SortInfo;
import com.sencha.gxt.data.shared.SortInfoBean;
import com.sencha.gxt.data.shared.Store.StoreSortInfo;
import com.sencha.gxt.messages.client.DefaultMessages;
import com.sencha.gxt.widget.core.client.event.CollapseItemEvent;
import com.sencha.gxt.widget.core.client.event.CollapseItemEvent.CollapseItemHandler;
import com.sencha.gxt.widget.core.client.event.CollapseItemEvent.HasCollapseItemHandlers;
import com.sencha.gxt.widget.core.client.event.ExpandItemEvent;
import com.sencha.gxt.widget.core.client.event.ExpandItemEvent.ExpandItemHandler;
import com.sencha.gxt.widget.core.client.event.ExpandItemEvent.HasExpandItemHandlers;
import com.sencha.gxt.widget.core.client.menu.CheckMenuItem;
import com.sencha.gxt.widget.core.client.menu.Item;
import com.sencha.gxt.widget.core.client.menu.Menu;
import com.sencha.gxt.widget.core.client.menu.MenuItem;
import com.sencha.gxt.widget.core.client.menu.SeparatorMenuItem;

/**
* <code>GridView</code> that groups data based on a given grouping column.
*
* @param <M> the model type
*/
public class GroupingView<M> extends GridView<M> implements HasCollapseItemHandlers<List<M>>,
    HasExpandItemHandlers<List<M>> {

  public static class GroupingData<M> {
    private final Object value;
    private final int startRow;
    private final List<M> items = new ArrayList<M>();
    private boolean collapsed;

    public GroupingData(Object value, int startRow) {
      assert value != null;
      this.value = value;
      this.startRow = startRow;
    }

    @Override
    public boolean equals(Object other) {
      if (other instanceof GroupingData) {
        return ((GroupingData<?>) other).value.equals(value);
      }
      return false;
    }

    public List<M> getItems() {
      return items;
    }

    public int getStartRow() {
      return startRow;
    }

    public Object getValue() {
      return value;
    }

    @Override
    public int hashCode() {
      return value.hashCode();
    }

    public boolean isCollapsed() {
      return collapsed;
    }

    public void setCollapsed(boolean collapsed) {
      this.collapsed = collapsed;
    }

  }

  public interface GroupingViewAppearance {

    XElement findHead(XElement element);

    XElement getGroup(XElement head);

    boolean isCollapsed(XElement group);

    void onGroupExpand(XElement group, boolean expanded);

    SafeHtml renderGroupHeader(GroupingData<?> groupInfo);

    ImageResource groupByIcon();

    GroupingViewStyle style();
  }

  public interface GroupSummaryTemplate<M> {
    SafeHtml renderGroupSummary(GroupingData<M> groupInfo);
  }

  public interface GroupingViewStyle extends CssResource, GridDataTableStyles {

    String gridGroup();

    String gridGroupBody();

    String gridGroupCollapsed();

    String gridGroupHead();

    String gridGroupHeadInner();
  }

  protected ColumnConfig<M, ?> lastGroupField;
  protected ColumnConfig<M, ?> groupingColumn;

  protected boolean enableGrouping;

  private GroupSummaryTemplate<M> groupSummaryTemplate;
  private final GroupingViewAppearance groupAppearance;

  private boolean enableGroupingMenu = true;
  private boolean enableNoGroups = true;
  private boolean showGroupedColumn = true;
  private boolean startCollapsed;

  private boolean isUpdating = false;
  private StoreSortInfo<M> lastStoreSort;
  private SortInfo lastSort;
  private HandlerManager handlerManager = new HandlerManager(this);

  public GroupingView() {
    this(GWT.<GroupingViewAppearance> create(GroupingViewAppearance.class));
  }

  public GroupingView(GroupingViewAppearance groupAppearance) {
    this.groupAppearance = groupAppearance;
  }

  @Override
  public HandlerRegistration addCollapseHandler(CollapseItemHandler<List<M>> handler) {
    return handlerManager.addHandler(CollapseItemEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addExpandHandler(ExpandItemHandler<List<M>> handler) {
    return handlerManager.addHandler(ExpandItemEvent.getType(), handler);
  }

  /**
   * Collapses all groups.
   */
  public void collapseAllGroups() {
    toggleAllGroups(false);
  }

  /**
   * Expands all groups.
   */
  public void expandAllGroups() {
    toggleAllGroups(true);
  }

  @Override
  protected void afterRender() {
    ColumnConfig<M, ?> column = groupingColumn;

    // set groupingColumn to null to force regrouping only if grouping
    // hasn't already occurred
    if (lastStoreSort == null && lastSort == null && column != null) {
      groupingColumn = null;
    }
    groupBy(column);
    super.afterRender();
  }

  public void groupBy(ColumnConfig<M, ?> column) {
    if (grid == null) {
      // if still being configured, save the grouping column for later
      groupingColumn = column;
    }
    if (column != groupingColumn) {
      // remove the existing group, if any
      if (groupingColumn != null) {
        if (grid.getLoader() == null || !grid.getLoader().isRemoteSort()) {
          assert lastStoreSort != null && ds.getSortInfo().contains(lastStoreSort);
          // remove the lastStoreSort from the listStore
          ds.getSortInfo().remove(lastStoreSort);
        } else {
          assert lastSort != null;
          grid.getLoader().removeSortInfo(lastSort);
        }
      } else {// groupingColumn == null;
        assert lastStoreSort == null && lastSort == null;
      }

      // set the new one
      groupingColumn = column;
      if (column != null) {
        if (grid.getLoader() == null || !grid.getLoader().isRemoteSort()) {
          lastStoreSort = createStoreSortInfo(column, SortDir.ASC);
          ds.addSortInfo(0, lastStoreSort);// this triggers the sort
        } else {
          lastSort = new SortInfoBean(column.getValueProvider(), SortDir.ASC);
          grid.getLoader().addSortInfo(0, lastSort);
          grid.getLoader().load();
        }
      } else {// new column == null
        lastStoreSort = null;
        lastSort = null;
        // full redraw without groups
        refresh(false);
      }
    }
  }

  /**
   * Returns true if the user can turn off grouping.
   *
   * @return the enable no groups state
   */
  public boolean isEnabledNoGroups() {
    return enableNoGroups;
  }

  /**
   * Returns true if the grouping menu is enabled.
   *
   * @return the enable grouping state
   */
  public boolean isEnableGroupingMenu() {
    return enableGroupingMenu;
  }

  /**
   * Returns true if the group is expanded.
   *
   * @param group the group
   * @return true if expanded
   */
  public boolean isExpanded(Element group) {
    return groupAppearance.isCollapsed(group.<XElement> cast());
  }

  /**
   * Returns true if the grouped column is visible.
   *
   * @return the show grouped column
   */
  public boolean isShowGroupedColumn() {
    return showGroupedColumn;
  }

  /**
   * Returns true if start collapsed is enabled.
   *
   * @return the start collapsed state
   */
  public boolean isStartCollapsed() {
    return startCollapsed;
  }

  /**
   * True to enable the the grouping menu items in the header context menu
   * (defaults to true).
   *
   * @param enableGroupingMenu true to enable the grouping menu items
   */
  public void setEnableGroupingMenu(boolean enableGroupingMenu) {
    this.enableGroupingMenu = enableGroupingMenu;
  }

  /**
   * True to enable the no groups menu item in the header context menu (defaults
   * to true).
   *
   * @param enableNoGroups true to enable no groups menu item
   */
  public void setEnableNoGroups(boolean enableNoGroups) {
    this.enableNoGroups = enableNoGroups;
  }

  /**
   * Sets whether the grouped column is visible (defaults to true).
   *
   * @param showGroupedColumn true to show the grouped column
   */
  public void setShowGroupedColumn(boolean showGroupedColumn) {
    this.showGroupedColumn = showGroupedColumn;
  }

  /**
   * Sets whether the groups should start collapsed (defaults to false).
   *
   * @param startCollapsed true to start collapsed
   */
  public void setStartCollapsed(boolean startCollapsed) {
    this.startCollapsed = startCollapsed;
  }

  /**
   * Toggles all groups.
   *
   * @param expanded true to expand
   */
  public void toggleAllGroups(boolean expanded) {
    NodeList<Element> groups = getGroups();
    for (int i = 0; i < groups.getLength(); i++) {
      toggleGroup(groups.getItem(i), expanded);
    }
  }

  protected Menu createContextMenu(final int colIndex) {
    Menu menu = super.createContextMenu(colIndex);

    if (menu != null && enableGroupingMenu) {
      if (cm.isGroupable(colIndex)) {
        MenuItem groupBy = new MenuItem(DefaultMessages.getMessages().groupingView_groupByText());
        groupBy.setIcon(groupAppearance.groupByIcon());
        groupBy.addSelectionHandler(new SelectionHandler<Item>() {
          @Override
          public void onSelection(SelectionEvent<Item> event) {
            groupBy(cm.getColumn(colIndex));
          }
        });
        menu.add(new SeparatorMenuItem());
        menu.add(groupBy);
      }
      if (enableGrouping && enableNoGroups) {
        final CheckMenuItem showInGroups = new CheckMenuItem(
            DefaultMessages.getMessages().groupingView_showGroupsText());
        showInGroups.setChecked(true);
        showInGroups.addSelectionHandler(new SelectionHandler<Item>() {
          @Override
          public void onSelection(SelectionEvent<Item> event) {
            if (showInGroups.isChecked()) {
              groupBy(cm.getColumn(colIndex));
            } else {
              groupBy(null);
            }
          }
        });
        menu.add(showInGroups);
      }
    }

    return menu;
  }

  @Override
  protected SafeHtml doRender(List<ColumnData> cs, List<M> rows, int startRow) {
    enableGrouping = groupingColumn != null;

    if (!enableGrouping || isUpdating) {
      return super.doRender(cs, rows, startRow);
    }

    GroupingData<M> curGroup = null;

    List<GroupingData<M>> groups = new ArrayList<GroupingData<M>>();

    // iterate through each item, creating a new group as needed. Assumes the
    // list is sorted
    for (int j = 0; j < rows.size(); j++) {
      M model = rows.get(j);

      int rowIndex = (j + startRow);

      // the value for the group field
      Object gvalue = groupingColumn.getValueProvider().getValue(model);

      if (curGroup == null || !curGroup.getValue().equals(gvalue)) {
        curGroup = new GroupingData<M>(gvalue, rowIndex);
        curGroup.setCollapsed(startCollapsed);
        curGroup.getItems().add(model);
        assert !groups.contains(curGroup);
        groups.add(curGroup);

      } else {
        curGroup.getItems().add(model);
      }
    }

    // TODO support a grouping renderer, perhaps in the
    // for (GroupingData<M> group : groups) {
    // if (groupRenderer != null) {
    // String g = groupRenderer.render(group);
    // if (g == null || g.equals("")) {
    // g = "&nbsp;";
    // }
    // group.group = g;
    // }
    // }

    SafeHtmlBuilder buf = new SafeHtmlBuilder();

    String styles = "width:" + getTotalWidth() + "px;";
    SafeStyles tableStyles = SafeStylesUtils.fromTrustedString(styles);

    for (int i = 0, len = groups.size(); i < len; i++) {
      GroupingData<M> g = groups.get(i);
      SafeHtml renderedRows = tpls.table(getAppearance().styles().dataTable(), tableStyles,
          super.doRender(cs, g.getItems(), g.getStartRow()), renderHiddenHeaders(getColumnWidths()));
      renderGroup(buf, g, renderedRows);
    }

    return buf.toSafeHtml();
  }

  @Override
  protected void doSort(int colIndex, SortDir sortDir) {
    ColumnConfig<M, ?> column = cm.getColumn(colIndex);
    if (groupingColumn != null) {
      if (grid.getLoader() == null || !grid.getLoader().isRemoteSort()) {
        // first sort is lastStoreSort
        assert lastStoreSort != null;
        ds.getSortInfo().clear();

        StoreSortInfo<M> sortInfo = createStoreSortInfo(column, sortDir);

        if (sortDir == null && storeSortInfo != null && storeSortInfo.getValueProvider() == column.getValueProvider()) {
          sortInfo.setDirection(storeSortInfo.getDirection() == SortDir.ASC ? SortDir.DESC : SortDir.ASC);
        } else if (sortDir == null) {
          sortInfo.setDirection(SortDir.ASC);
        }

        ds.getSortInfo().add(0, lastStoreSort);
        ds.getSortInfo().add(1, sortInfo);

        if (GWT.isProdMode()) {
          ds.applySort(false);
        } else {
          try {
            // applySort will apply its sort when called, which might trigger an
            // exception if the column passed in's data isn't Comparable
            ds.applySort(false);
          } catch (ClassCastException ex) {
            GWT.log("Column can't be sorted " + column.getValueProvider().getPath() + " is not Comparable. ", ex);
            throw ex;
          }
        }
      } else {
        assert lastSort != null;
        ValueProvider<? super M, ?> vp = column.getValueProvider();
        grid.getLoader().clearSortInfo();
        grid.getLoader().addSortInfo(0, lastSort);
        grid.getLoader().addSortInfo(1, new SortInfoBean(vp, sortDir));
        grid.getLoader().load();
      }

    } else {
      super.doSort(colIndex, sortDir);
    }
  }

  protected List<GroupingData<M>> getGroupData() {
    List<GroupingData<M>> groups = new ArrayList<GroupingData<M>>();

    GroupingData<M> curGroup = null;
    for (int i = 0, len = ds.size(); i < len; i++) {
      M m = ds.get(i);
      Object gvalue = groupingColumn.getValueProvider().getValue(m);
      if (curGroup == null || !curGroup.getValue().equals(gvalue)) {
        curGroup = new GroupingData<M>(gvalue, i);
        curGroup.setCollapsed(true);
        curGroup.getItems().add(m);
        assert !groups.contains(curGroup);
        groups.add(curGroup);
      } else {
        curGroup.getItems().add(m);
      }
    }

    return groups;
  }

  protected NodeList<Element> getGroups() {
    if (!enableGrouping) {
      return JsArray.createArray().cast();
    }
    return dataTable.<XElement> cast().select("." + groupAppearance.style().gridGroup());
  }

  @Override
  protected NodeList<Element> getRows() {
    if (!enableGrouping || !hasRows()) {
      return super.getRows();
    }
    return dataTable.<XElement> cast().select("." + styles.row());
  }

  @Override
  protected void adjustColumnWidths(int[] columnWidths) {
    super.adjustColumnWidths(columnWidths);
  }

  @Override
  protected StoreSortInfo<M> getSortState() {
    if (groupingColumn != null) {
      if (ds.getSortInfo().size() > 1) {
        return ds.getSortInfo().get(1);
      }
    }
    return super.getSortState();
  }

  @Override
  protected void onAdd(List<M> models, int index) {
    if (enableGrouping) {
      Point ss = getScrollState();
      refresh(false);
      restoreScroll(ss);
    } else {
      super.onAdd(models, index);
    }
  }

  @Override
  protected void onMouseDown(Event ge) {
    super.onMouseDown(ge);
    XElement head = ge.getEventTarget().cast();
    head = groupAppearance.findHead(head);
    if (head != null) {
      ge.stopPropagation();
      XElement group = groupAppearance.getGroup(head);
      toggleGroup(group, groupAppearance.isCollapsed(group));
    }
  }

  @Override
  protected void onRemove(M m, int index, boolean isUpdate) {
    Element parentToRemove = null;
    if (enableGrouping) {
      Element row = getRow(index).cast();

      // TODO appearance this
      Element groupContainer = row.getParentElement().cast();
      if (groupContainer.getChildCount() == 1) {
        parentToRemove = groupContainer.getParentElement().cast();
      }
    }
    super.onRemove(m, index, isUpdate);
    if (parentToRemove != null) {
      parentToRemove.removeFromParent();
    }
  }

  @Override
  protected void refreshRow(int row) {
    isUpdating = true;
    super.refreshRow(row);
    isUpdating = false;
  }

  protected void renderGroup(SafeHtmlBuilder buf, GroupingData<M> g, SafeHtml renderedRows) {
    String groupClass = groupAppearance.style().gridGroup();
    String headClass = groupAppearance.style().gridGroupHead();
    buf.append(tpls.tr(groupClass,
        tpls.tdWrap(cm.getColumnCount(), headClass + " " + styles.cell(), styles.cellInner(), renderGroupHeader(g))));
    buf.append(tpls.tr("", tpls.tdWrap(cm.getColumnCount(), "", "", renderedRows)));
  }

  protected SafeHtml renderGroupHeader(GroupingData<M> groupInfo) {
    return groupAppearance.renderGroupHeader(groupInfo);
  }

  protected SafeHtml renderGroupSummary(GroupingData<M> groupInfo) {
    if (groupSummaryTemplate != null) {
      return groupSummaryTemplate.renderGroupSummary(groupInfo);
    } else {
      return null;
    }
  }

  @Override
  protected SafeHtml renderRows(int startRow, int endRow) {
    boolean eg = groupingColumn != null;
    if (!showGroupedColumn) {
      int colIndex = cm.indexOf(groupingColumn);
      if (!eg && lastGroupField != null) {
        dataTableBody.removeChildren();
        cm.setHidden(cm.indexOf(lastGroupField), false);
        cm.getColumn(cm.indexOf(lastGroupField)).setHideable(true);
        lastGroupField = groupingColumn;
      } else if (eg && (lastGroupField == null || lastGroupField == groupingColumn)) {
        lastGroupField = groupingColumn;
        cm.setHidden(colIndex, true);
        cm.getColumn(colIndex).setHideable(false);
      } else if (eg && lastGroupField != null && !groupingColumn.equals(lastGroupField)) {
        dataTableBody.removeChildren();
        int oldIndex = cm.indexOf(lastGroupField);
        cm.setHidden(oldIndex, false);
        cm.getColumn(oldIndex).setHideable(true);
        lastGroupField = groupingColumn;
        cm.setHidden(colIndex, true);
        cm.getColumn(colIndex).setHideable(false);
      }
    }
    return super.renderRows(startRow, endRow);
  }

  protected void toggleGroup(Element group, boolean expanded) {
    assert group != null;
    groupAppearance.onGroupExpand(group.<XElement> cast(), expanded);
    calculateVBar(false);
    if (expanded) {
      handlerManager.fireEvent(new ExpandItemEvent<List<M>>(null));
    } else {
      handlerManager.fireEvent(new CollapseItemEvent<List<M>>(null));
    }
  }
}
TOP

Related Classes of com.sencha.gxt.widget.core.client.grid.GroupingView$GroupingViewStyle

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.