/* * Copyright 2014 Daniel Kurka * * 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 com.googlecode.mgwt.ui.client.widget.list.celllist; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Element; 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.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.uibinder.client.UiFactory; import com.googlecode.mgwt.ui.client.widget.list.celllist.CellListAppearance.CellListCss; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A grouping cell list renders a number of groups with their children * * @author Daniel Kurka * * @param <G> the type of the model for the header * @param <T> the type if the model for the content */ public class GroupingCellList<G, T> extends CellList<T> implements HasSelectionHandlers<T> { public static final GroupingCellListAppearance DEFAULT_APPEARANCE = GWT.create(GroupingCellListAppearance.class); private final Cell<G> header; private final Map<Integer, Integer> map = new HashMap<Integer, Integer>(); private final Map<Integer, T> modelMap = new HashMap<Integer, T>(); private GroupingCellListAppearance groupAppearance; private HeaderTemplate headerTemplate; /** * The cellgroup interface * * @author Daniel Kurka * * @param <G> the type of the model for the header * @param <T> the type if the model for the content */ public interface CellGroup<G, T> { /** * get the name of the group * * @return the name of the group */ public String getKey(); /** * get the group header model * * @return the group header model */ public G getGroup(); /** * get the group content models * * @return the group content models */ public List<T> getMember(); } /** * The standard implementation of {@link GroupingCellList.CellGroup} * * @author Daniel Kurka * * @param <G> the type of the model for the header * @param <T> the type if the model for the content */ public static class StandardCellGroup<G, T> implements CellGroup<G, T> { protected final String key; protected final G group; protected final List<T> member; /** * Construct a {@link GroupingCellList.StandardCellGroup} * * @param key the key to use * @param group the group model to use * @param member the list of content models for this group */ public StandardCellGroup(String key, G group, List<T> member) { this.key = key; this.group = group; this.member = member; } @Override public String getKey() { return key; } @Override public G getGroup() { return group; } @Override public List<T> getMember() { return member; } } public interface HeaderTemplate { SafeHtml li(SafeHtml cellContents, String classes); } /** * Construct a cell list with a given cell for content and for the header * * @param cell the cell for content * @param header the cell for the headers */ public GroupingCellList(Cell<T> cell, Cell<G> header) { this(cell, header, DEFAULT_APPEARANCE); } /** * Construct a cell list with a given cell for content, for the header and a given css * * @param cell the cell for content * @param header the cell for the headers * @param css the css to use */ public GroupingCellList(Cell<T> cell, Cell<G> header, GroupingCellListAppearance groupAppearance) { super(cell, groupAppearance); this.groupAppearance = groupAppearance; this.header = header; headerTemplate = this.groupAppearance.getHeaderTemplate(); } /** * render a given set of models * * @param groups the model to render */ public void renderGroup(List<CellGroup<G, T>> groups) { SafeHtmlBuilder sb = new SafeHtmlBuilder(); int count = 0; map.clear(); modelMap.clear(); int groupCounter = 0; int renderedGroups = 0; for (CellGroup<G, T> cellGroup : groups) { if (cellGroup.getMember().isEmpty()) { groupCounter++; continue; } // render header of group SafeHtmlBuilder headerBuilder = new SafeHtmlBuilder(); header.render(headerBuilder, cellGroup.getGroup()); sb.append(headerTemplate.li(headerBuilder.toSafeHtml(), this.groupAppearance.css().listHeadElement())); // render members of group List<T> models = cellGroup.getMember(); for (T model : models) { SafeHtmlBuilder cellBuilder = new SafeHtmlBuilder(); String clazz = this.groupAppearance.css().entry() + " "; if (cell.canBeSelected(model)) { clazz += this.groupAppearance.css().canbeSelected() + " "; } cell.render(cellBuilder, model); sb.append(entryTemplate.li(count, clazz, cellBuilder.toSafeHtml())); modelMap.put(count, model); count++; } map.put(renderedGroups, groupCounter); renderedGroups++; groupCounter++; } final String html = sb.toSafeHtml().asString(); getElement().setInnerHTML(html); if (count > 0) { String innerHTML = getElement().getInnerHTML(); if ("".equals(innerHTML.trim())) { fixBug(html); } } } /** * get the css selector for header elements * * @return the css selector for header elements */ public String getHeaderSelector() { return "li." + this.groupAppearance.css().listHeadElement(); } /** * get the css used by this {@link GroupingCellList} * * @return the css used by this {@link GroupingCellList} */ public CellListCss getListCss() { return this.groupAppearance.css(); } /** * get the mapping of index to content * * @return the mapping of index to content */ public Map<Integer, Integer> getMapping() { return map; } /** * render a header and return the value as html * * @param group the header to render * @return the string value */ public String renderGroupHeader(G group) { SafeHtmlBuilder headerBuilder = new SafeHtmlBuilder(); header.render(headerBuilder, group); return headerBuilder.toSafeHtml().asString(); } @Override protected void fireSelectionAtIndex(int index, Element targetElement) { T t = modelMap.get(index); if (t != null) { SelectionEvent.fire(this, t); } } @Override @UiFactory public CellListAppearance getAppearance() { return super.getAppearance(); } @Override public HandlerRegistration addSelectionHandler(SelectionHandler<T> handler) { return addHandler(handler, SelectionEvent.getType()); } }