All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.extjs.gxt.ui.client.widget.ListView Maven / Gradle / Ivy

There is a newer version: 2.3.1-gwt22
Show newest version
/*
 * Sencha GXT 2.3.1 - Sencha for GWT
 * Copyright(c) 2007-2013, Sencha, Inc.
 * [email protected]
 * 
 * http://www.sencha.com/products/gxt/license/
 */
 package com.extjs.gxt.ui.client.widget;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.SelectionMode;
import com.extjs.gxt.ui.client.aria.FocusFrame;
import com.extjs.gxt.ui.client.core.CompositeElement;
import com.extjs.gxt.ui.client.core.DomQuery;
import com.extjs.gxt.ui.client.core.XDOM;
import com.extjs.gxt.ui.client.core.XTemplate;
import com.extjs.gxt.ui.client.data.BaseModel;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.ModelProcessor;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.ListViewEvent;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.StoreListener;
import com.extjs.gxt.ui.client.util.Util;
import com.extjs.gxt.ui.client.widget.tips.QuickTip;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;

/**
 * A mechanism for displaying data using custom layout templates. ListView uses
 * an {@link XTemplate} as its internal templating mechanism.
 * 
 * 

* In order to use these features, an {@link #setItemSelector(String)} must * be provided for the ListView to determine what nodes it will be working * with. * *

*
Events:
* *
Select : ListViewEvent(listView, event)
*
Fires when a template node is clicked.
*
    *
  • listView : this
  • *
  • event : the dom event
  • *
  • index : the index of the target node
  • *
*
* *
DoubleClick : ListViewEvent(listView, index, element, event)
*
Fires when a template node is double clicked.
*
    *
  • listView : this
  • *
  • index : the index of the target node
  • *
  • element : the target node
  • *
  • event : the dom event
  • *
*
* *
ContextMenu : ListViewEvent(listView, index, element, event)
*
Fires when a template node is right clicked.
*
    *
  • listView : this
  • *
  • index : the index of the target node
  • *
  • element : the target node
  • *
  • event : the dom event
  • *
*
*
* *
*
Inherited Events:
*
BoxComponent Move
*
BoxComponent Resize
*
Component Enable
*
Component Disable
*
Component BeforeHide
*
Component Hide
*
Component BeforeShow
*
Component Show
*
Component Attach
*
Component Detach
*
Component BeforeRender
*
Component Render
*
Component BrowserEvent
*
Component BeforeStateRestore
*
Component StateRestore
*
Component BeforeStateSave
*
Component SaveState
*
*/ public class ListView extends BoxComponent { protected int rowSelectorDepth = 5; protected ListStore store; private CompositeElement all; private String displayProperty = "text"; private boolean enableQuickTip = true; private String itemSelector = ".x-view-item"; private String loadingText; private ModelProcessor modelProcessor; private Element overElement; private String overStyle = "x-view-item-over"; private QuickTip quickTip; private boolean selectOnHover; private String selectStyle = "x-view-item-sel"; private ListViewSelectionModel sm; private StoreListener storeListener; private XTemplate template; /** * Creates a new view. */ public ListView() { initComponent(); setSelectionModel(new ListViewSelectionModel()); all = new CompositeElement(); baseStyle = "x-view"; disableTextSelection(true); } /** * Creates a new view. */ public ListView(ListStore store) { this(); setStore(store); } /** * Creates a new template list. * * @param template the template */ public ListView(ListStore store, XTemplate template) { this(store); this.template = template; } /** * Returns the matching element. * * @param element the element or any child element * @return the parent element */ public Element findElement(Element element) { return fly(element).findParentElement(itemSelector, rowSelectorDepth); } /** * Returns the element's index. * * @param element the element or any child element * @return the element index or -1 if no match */ public int findElementIndex(Element element) { Element elem = findElement(element); if (elem != null) { return indexOf(elem); } return -1; } /** * Returns the display property. * * @return the display property */ public String getDisplayProperty() { return displayProperty; } /** * Returns the element at the given index. * * @param index the index * @return the element */ public Element getElement(int index) { return all.getElement(index); } /** * Returns all of the child elements. * * @return the elements */ public List getElements() { return all.getElements(); } /** * Returns the number of models in the view. * * @return the count */ public int getItemCount() { return store == null ? 0 : store.getCount(); } /** * Returns the item selector. * * @return the selector */ public String getItemSelector() { return itemSelector; } /** * Returns the view's loading text. * * @return the loading text */ public String getLoadingText() { return loadingText; } /** * Returns the model processor. * * @return the model processor */ public ModelProcessor getModelProcessor() { return modelProcessor; } /** * Returns the over style. * * @return the over style */ public String getOverStyle() { return overStyle; } /** * Returns the view's quick tip instance. * * @return the quicktip instance or null if not enabled */ public QuickTip getQuickTip() { return quickTip; } /** * Returns the view's selection model. * * @return the selection model */ public ListViewSelectionModel getSelectionModel() { return sm; } /** * Returns true if select on hover is enabled. * * @return the select on hover state */ public boolean getSelectOnOver() { return selectOnHover; } /** * Returns the select style. * * @return the select style */ public String getSelectStyle() { return selectStyle; } /** * Returns the combo's store. * * @return the store */ public ListStore getStore() { return store; } /** * Returns the list's template. * * @return the template */ public XTemplate getTemplate() { return template; } /** * Returns the index of the element. * * @param element the element * @return the index */ public int indexOf(Element element) { if (element.getPropertyString("viewIndex") != null) { return element.getPropertyInt("viewIndex"); } return all.indexOf(element); } /** * Returns true if quicktips are enabled. * * @return true for enabled */ public boolean isEnableQuickTips() { return enableQuickTip; } /** * Moves the current selections down one level. */ public void moveSelectedDown() { List sel = getSelectionModel().getSelectedItems(); Collections.sort(sel, new Comparator() { public int compare(M o1, M o2) { return store.indexOf(o1) < store.indexOf(o2) ? 1 : 0; } }); for (M m : sel) { int idx = store.indexOf(m); if (idx < (store.getCount() - 1)) { store.remove(m); store.insert(m, idx + 1); } } getSelectionModel().select(sel, false); } /** * Moves the current selections up one level. */ public void moveSelectedUp() { List sel = getSelectionModel().getSelectedItems(); Collections.sort(sel, new Comparator() { public int compare(M o1, M o2) { return store.indexOf(o1) > store.indexOf(o2) ? 1 : 0; } }); for (M m : sel) { int idx = store.indexOf(m); if (idx > 0) { store.remove(m); store.insert(m, idx - 1); } } getSelectionModel().select(sel, false); } @SuppressWarnings({"unchecked", "rawtypes"}) public void onComponentEvent(ComponentEvent ce) { super.onComponentEvent(ce); ListViewEvent le = (ListViewEvent) ce; switch (ce.getEventTypeInt()) { case Event.ONMOUSEOVER: onMouseOver(le); break; case Event.ONMOUSEOUT: onMouseOut(le); break; case Event.ONMOUSEDOWN: onMouseDown(le); break; case Event.ONDBLCLICK: if (le.getIndex() != -1) { onDoubleClick(le); } break; case Event.ONCLICK: if (le.getIndex() != -1) { onClick(le); } break; case Event.ONFOCUS: onFocus(ce); break; } } /** * Refreshes the view by reloading the data from the store and re-rendering * the template. */ public void refresh() { if (!rendered) { return; } el().setInnerHtml(""); repaint(); List models = store == null ? new ArrayList() : store.getModels(); all.removeAll(); template.overwrite(getElement(), Util.getJsObjects(collectData(models, 0), template.getMaxDepth())); all = new CompositeElement(Util.toElementArray(el().select(itemSelector))); if (GXT.isAriaEnabled()) { for (int i = 0; i < all.getCount(); i++) { all.getElement(i).setId(XDOM.getUniqueId()); } } updateIndexes(0, -1); fireEvent(Events.Refresh); } /** * Refreshes an individual node's data from the store. * * @param index the items data index in the store */ public void refreshNode(int index) { onUpdate(store.getAt(index), index); } /** * Sets the display property. Applies when using the default template for each * item's text. * * @param displayProperty the display property */ public void setDisplayProperty(String displayProperty) { this.displayProperty = displayProperty; } /** * True to enable quicktips (defaults to true, pre-render). * * @param enableQuickTip true to enable quicktips */ public void setEnableQuickTips(boolean enableQuickTip) { assertPreRender(); this.enableQuickTip = enableQuickTip; } /** * This is a required setting. A simple CSS selector (e.g. div.some-class or * span:first-child) that will be used to determine what nodes this DataView * will be working with (defaults to 'x-view-item'). * * @param itemSelector the item selector */ public void setItemSelector(String itemSelector) { this.itemSelector = itemSelector; } /** * Sets the text loading text to be displayed during a load request. * * @param loadingText the loading text */ public void setLoadingText(String loadingText) { this.loadingText = loadingText; } /** * Sets the view's model processor. The model processor can be used to provide * "formatted" properties to the XTemplate used to render the view. * * @see ModelProcessor * @param modelProcessor */ public void setModelProcessor(ModelProcessor modelProcessor) { this.modelProcessor = modelProcessor; } /** * Sets the style name to apply on mouse over. * * @param overStyle the over style */ public void setOverStyle(String overStyle) { this.overStyle = overStyle; } /** * Sets the selection model. * * @param sm the selection model */ public void setSelectionModel(ListViewSelectionModel sm) { if (this.sm != null) { this.sm.bindList(null); } this.sm = sm; if (sm != null) { sm.bindList(this); } } /** * True to select the item when mousing over a element (defaults to false). * * @param selectOnHover true to select on mouse over */ public void setSelectOnOver(boolean selectOnHover) { this.selectOnHover = selectOnHover; } /** * The style to be applied to each selected item (defaults to * 'x-view-item-sel'). * * @param selectStyle the select style */ public void setSelectStyle(String selectStyle) { this.selectStyle = selectStyle; } /** * Sets the template fragment to be used for the text of each listview item. * *
   * <code>
   * listview.setSimpleTemplate("{abbr} {name}");
   * </code>
   * 
* * @param html the html used only for the text of each item in the list */ public void setSimpleTemplate(String html) { assertPreRender(); html = "
" + html + "
"; template = XTemplate.create(html); } /** * Changes the data store bound to this view and refreshes it. * * @param store the store to bind this view */ public void setStore(ListStore store) { if (this.store != null) { this.store.removeStoreListener(storeListener); } if (store != null) { store.addStoreListener(storeListener); } this.store = store; sm.bindList(this); if (store != null && isRendered()) { refresh(); } } /** * Sets the view's template. * * @param html the HTML fragment */ public void setTemplate(String html) { setTemplate(XTemplate.create(html)); } /** * Sets the view's template. * * @param template the template */ public void setTemplate(XTemplate template) { this.template = template; } @Override protected void afterRender() { super.afterRender(); refresh(); } protected List collectData(List models, int startIndex) { List list = new ArrayList(); for (int i = 0, len = models.size(); i < len; i++) { list.add(prepareData(models.get(i))); } return list; } @Override protected ComponentEvent createComponentEvent(Event event) { return new ListViewEvent(this, event); } protected void focusItem(int index) { Element elem = getElement(index); if (elem != null) { fly(elem).scrollIntoView(getElement(), false); } focus(); } protected void initComponent() { storeListener = new StoreListener() { @Override public void storeAdd(StoreEvent se) { onAdd(se.getModels(), se.getIndex()); } @Override public void storeBeforeDataChanged(StoreEvent se) { onBeforeLoad(); } @Override public void storeClear(StoreEvent se) { refresh(); } @Override public void storeDataChanged(StoreEvent se) { refresh(); } @Override public void storeFilter(StoreEvent se) { refresh(); } @Override public void storeRemove(StoreEvent se) { onRemove(se.getModel(), se.getIndex()); } @Override public void storeSort(StoreEvent se) { refresh(); } @Override public void storeUpdate(StoreEvent se) { int index = store.indexOf(se.getModel()); onUpdate(se.getModel(), index); } }; } protected void onAdd(List models, int index) { if (rendered) { if (all.getCount() == 0) { refresh(); return; } NodeList nodes = bufferRender(models); Element[] elements = Util.toElementArray(nodes); all.insert(elements, index); Element ref = index == 0 ? all.getElement(elements.length) : all.getElement(index - 1); for (int i = elements.length - 1; i >= 0; i--) { Node n = ref.getParentNode(); if (index == 0) { n.insertBefore(elements[i], n.getFirstChild()); } else { Node next = ref == null ? null : ref.getNextSibling(); if (next == null) { n.appendChild(elements[i]); } else { n.insertBefore(elements[i], next); } } if (GXT.isAriaEnabled()) { elements[i].setId(XDOM.getUniqueId()); } } updateIndexes(index, -1); } } protected void onBeforeLoad() { if (loadingText != null) { if (rendered) { el().setInnerHtml("
" + loadingText + "
"); } all.removeAll(); } } protected void onClick(ListViewEvent e) { } protected void onDoubleClick(ListViewEvent e) { fireEvent(Events.DoubleClick, e); } protected void onFocus(ComponentEvent ce) { FocusFrame.get().frame(this); } protected void onHighlightRow(int index, boolean highLight) { Element e = getElement(index); if (e != null) { fly(e).setStyleName("x-view-highlightrow", highLight); if (highLight && GXT.isAriaEnabled()) { setAriaState("aria-activedescendant", e.getId()); } } } protected void onMouseDown(ListViewEvent e) { if (e.getIndex() != -1) { fireEvent(Events.Select, e); } } protected void onMouseOut(ListViewEvent ce) { if (overElement != null) { if (!ce.within(overElement, true)) { fly(overElement).removeStyleName(overStyle); overElement = null; } } } protected void onMouseOver(ListViewEvent ce) { if (ce.getIndex() != -1) { if (selectOnHover) { sm.select(ce.getIndex(), false); } else { Element e = all.getElement(ce.getIndex()); if (e != null && e != overElement) { fly(e).addStyleName(overStyle); overElement = e; } } } } protected void onRemove(M data, int index) { if (all != null) { Element e = getElement(index); if (e != null) { fly(e).removeStyleName(overStyle); if (overElement == e) { overElement = null; } getSelectionModel().deselect(index); fly(e).removeFromParent(); all.remove(index); updateIndexes(index, -1); } } } @Override protected void onRender(Element target, int index) { super.onRender(target, index); setElement(DOM.createDiv(), target, index); el().setTabIndex(0); el().setElementAttribute("hideFocus", "true"); String aria = GXT.isAriaEnabled() ? " role='option' aria-selected='false' " : ""; if (template == null) { template = XTemplate.create("
{" + displayProperty + "}
"); } if (enableQuickTip) { quickTip = new QuickTip(this); } if (GXT.isAriaEnabled()) { setAriaRole("listbox"); SelectionMode mode = getSelectionModel().getSelectionMode(); setAriaState("aria-multiselectable", mode != SelectionMode.SINGLE ? "true" : "false"); setAriaRole("listbox"); } sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.FOCUSEVENTS | Event.ONKEYDOWN); } protected void onSelectChange(M model, boolean select) { if (rendered && all != null) { int index = store.indexOf(model); if (index != -1 && index < all.getCount()) { Element e = all.getElement(index); fly(e).setStyleName(selectStyle, select); fly(e).removeStyleName(overStyle); if (GXT.isAriaEnabled()) { e.setAttribute("aria-selected", "" + select); if (select) { setAriaState("aria-activedescendant", e.getId()); } } } } } @SuppressWarnings({"unchecked", "rawtypes"}) protected void onUpdate(M model, int index) { if (rendered) { Element original = all.getElement(index); if (original != null) { List list = Util.createList(model); Element node = bufferRender(list).getItem(0); all.replaceElement(original, node); original.getParentElement().replaceChild(node, original); } ListViewEvent evt = new ListViewEvent(this); evt.setModel(model); evt.setIndex(index); fireEvent(Events.RowUpdated, evt); } } protected M prepareData(M model) { if (modelProcessor != null) { boolean silent = false; if (model instanceof BaseModel) { silent = ((BaseModel) model).isSilent(); ((BaseModel) model).setSilent(true); } M m = modelProcessor.prepareData(model); if (model instanceof BaseModel) { ((BaseModel) model).setSilent(silent); } return m; } return model; } private NodeList bufferRender(List models) { Element div = DOM.createDiv(); template.overwrite(div, Util.getJsObjects(collectData(models, 0), template.getMaxDepth())); return DomQuery.select(itemSelector, div); } private void updateIndexes(int startIndex, int endIndex) { List elems = all.getElements(); endIndex = endIndex == -1 ? elems.size() - 1 : endIndex; for (int i = startIndex; i <= endIndex; i++) { elems.get(i).setPropertyInt("viewIndex", i); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy