import com.itmill.toolkit.terminal.gwt.client.ui.IPanel;
import com.itmill.toolkit.terminal.gwt.client.ui.IPasswordField;
import com.itmill.toolkit.terminal.gwt.client.ui.IPopupCalendar;
+import com.itmill.toolkit.terminal.gwt.client.ui.IScrollTable;
import com.itmill.toolkit.terminal.gwt.client.ui.ISelect;
import com.itmill.toolkit.terminal.gwt.client.ui.ITablePaging;
import com.itmill.toolkit.terminal.gwt.client.ui.ITabsheet;
import com.itmill.toolkit.terminal.gwt.client.ui.IUnknownComponent;
import com.itmill.toolkit.terminal.gwt.client.ui.IVerticalLayout;
import com.itmill.toolkit.terminal.gwt.client.ui.IWindow;
-import com.itmill.toolkit.terminal.gwt.client.ui.scrolltable.IScrollTable;
public class DefaultWidgetFactory implements WidgetFactory {
--- /dev/null
+package com.itmill.toolkit.terminal.gwt.client.ui;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+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.Event;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.ScrollListener;
+import com.google.gwt.user.client.ui.ScrollPanel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.Client;
+import com.itmill.toolkit.terminal.gwt.client.Paintable;
+import com.itmill.toolkit.terminal.gwt.client.UIDL;
+import com.itmill.toolkit.terminal.gwt.client.ui.IScrollTable.IScrollTableBody.IScrollTableRow;
+
+public class IScrollTable extends Composite implements Paintable, ITable, ScrollListener {
+
+ /**
+ * multiple of pagelenght which component will
+ * cache when requesting more rows
+ */
+ private static final double CACHE_RATE = 2;
+ /**
+ * fraction of pageLenght which can be scrolled without
+ * making new request
+ */
+ private static final double CACHE_REACT_RATE = 1;
+
+ private int firstRowInViewPort = 0;
+ private int pageLength = 15;
+
+ private boolean rowHeaders = false;
+
+ private String[] columnOrder;
+
+ private Client client;
+ private String id;
+
+ private boolean immediate;
+
+ private int selectMode = ITable.SELECT_MODE_NONE;
+
+ private Vector selectedRowKeys = new Vector();
+
+ private boolean initializedAndAttached = false;
+
+ private TableHead tHead = new TableHead();
+
+ private ScrollPanel bodyContainer = new ScrollPanel();
+
+ private ScrollPanel headerContainer = new ScrollPanel();
+
+ private boolean colWidthsInitialized = false;
+ private int totalRows;
+ private HashMap columnWidths = new HashMap();
+
+ private RowRequestHandler rowRequestHandler;
+ private IScrollTableBody tBody;
+ private int width = -1;
+ private int height = -1;
+ private int firstvisible = 0;
+ private boolean sortAscending;
+ private String sortColumn;
+ private boolean columnReordering;
+
+ public IScrollTable() {
+ headerContainer.setStyleName("iscrolltable-header");
+ headerContainer.add(tHead);
+ DOM.setStyleAttribute(headerContainer.getElement(), "overflow", "hidden");
+
+ bodyContainer.addScrollListener(this);
+
+ VerticalPanel panel = new VerticalPanel();
+ panel.add(headerContainer);
+ panel.add(bodyContainer);
+
+ rowRequestHandler = new RowRequestHandler();
+
+ initWidget(panel);
+ }
+
+ public void updateFromUIDL(UIDL uidl, Client client) {
+ if (client.updateComponent(this, uidl, true))
+ return;
+
+ this.client = client;
+ this.id = uidl.getStringAttribute("id");
+ this.immediate = uidl.getBooleanAttribute("immediate");
+ this.totalRows = uidl.getIntAttribute("totalrows");
+ this.pageLength = uidl.getIntAttribute("pagelength");
+ if(pageLength == 0)
+ pageLength = totalRows;
+ this.firstvisible = uidl.hasVariable("firstvisible") ? uidl.getIntVariable("firstvisible") : 0;
+ if(uidl.hasAttribute("rowheaders"))
+ rowHeaders = true;
+ if(uidl.hasAttribute("width"))
+ width = uidl.getIntAttribute("width");
+ if(uidl.hasAttribute("height"))
+ width = uidl.getIntAttribute("height");
+
+ if(uidl.hasVariable("sortascending")) {
+ this.sortAscending = uidl.getBooleanVariable("sortascending");
+ this.sortColumn = uidl.getStringVariable("sortcolumn");
+ }
+
+ Set selectedKeys = uidl.getStringArrayVariableAsSet("selected");
+ selectedRowKeys.clear();
+ for(Iterator it = selectedKeys.iterator();it.hasNext();)
+ selectedRowKeys.add((String) it.next());
+
+
+ if(uidl.hasAttribute("selectmode")) {
+ if(uidl.getStringAttribute("selectmode").equals("multi"))
+ selectMode = ITable.SELECT_MODE_MULTI;
+ else
+ selectMode = ITable.SELECT_MODE_SINGLE;
+ }
+
+ if(uidl.hasVariable("columnorder")) {
+ this.columnReordering = true;
+ this.columnOrder = uidl.getStringArrayVariable("columnorder");
+ }
+
+ UIDL columnInfo = null;
+ UIDL rowData = null;
+ for(Iterator it = uidl.getChildIterator(); it.hasNext();) {
+ UIDL c = (UIDL) it.next();
+ if(c.getTag().equals("cols"))
+ columnInfo = c;
+ else if(c.getTag().equals("rows"))
+ rowData = c;
+ else if(c.getTag().equals("actions"))
+ updateActionMap(c);
+ else if(c.getTag().equals("visiblecolumns"))
+ ;
+ }
+ updateHeader(columnInfo);
+
+ if(initializedAndAttached) {
+ updateBody(rowData, uidl.getIntAttribute("firstrow"),uidl.getIntAttribute("rows"));
+ } else {
+ getTBody().renderInitialRows(rowData,
+ uidl.getIntAttribute("firstrow"),
+ uidl.getIntAttribute("rows"),
+ totalRows);
+ bodyContainer.add(tBody);
+ initializedAndAttached = true;
+ }
+ }
+
+ private IScrollTableBody getTBody() {
+ if(tBody == null || totalRows != tBody.getTotalRows()) {
+ if(tBody != null)
+ tBody.removeFromParent();
+ tBody = new IScrollTableBody();
+ }
+ return tBody;
+ }
+
+ private void updateActionMap(UIDL c) {
+ // TODO Auto-generated method stub
+
+ }
+
+ private void updateHeader(UIDL uidl) {
+ if(uidl == null)
+ return;
+ int columnCount = uidl.getChidlCount();
+ int colIndex = 0;
+ if(rowHeaders) {
+ columnCount++;
+ HeaderCell c = (HeaderCell) tHead.getHeaderCell(0);
+ if(c == null) {
+ tHead.setHeaderCell(0, new HeaderCell("0", ""));
+ }
+ colIndex++;
+ }
+
+ for(Iterator it = uidl.getChildIterator();it.hasNext();) {
+ UIDL col = (UIDL) it.next();
+ String cid = col.getStringAttribute("cid");
+ HeaderCell c = (HeaderCell) tHead.getHeaderCell(colIndex);
+ if(c != null && c.getColKey().equals(cid)) {
+ c.setText(col.getStringAttribute("caption"));
+ } else {
+ c = new HeaderCell(cid, col.getStringAttribute("caption"));
+ tHead.setHeaderCell(colIndex, c);
+ }
+ if(col.hasAttribute("sortable")) {
+ c.setSortable(true);
+ if(cid.equals(sortColumn))
+ c.setSorted(true);
+ else
+ c.setSorted(false);
+ }
+ colIndex++;
+ }
+ }
+
+ /**
+ * @param uidl which contains row data
+ * @param firstRow first row in data set
+ * @param reqRows amount of rows in data set
+ */
+ private void updateBody(UIDL uidl, int firstRow, int reqRows) {
+ if(uidl == null || reqRows < 1)
+ return;
+
+ tBody.renderRows(uidl, firstRow, reqRows);
+
+ int optimalFirstRow = (int) (firstRowInViewPort - pageLength*CACHE_RATE);
+ while(tBody.getFirstRendered() < optimalFirstRow) {
+// client.console.log("removing row from start");
+ tBody.unlinkRow(true);
+ }
+ int optimalLastRow = (int) (firstRowInViewPort + pageLength + pageLength*CACHE_RATE);
+ while(tBody.getLastRendered() > optimalLastRow) {
+// client.console.log("removing row from the end");
+ tBody.unlinkRow(false);
+ }
+
+ }
+
+
+ private int getColIndexByKey(String colKey) {
+ // return 0 if asked for rowHeaders
+ if("0".equals(colKey))
+ return 0;
+ int index = -1;
+ for (int i = 0; i < columnOrder.length; i++) {
+ if(columnOrder[i].equals(colKey)) {
+ index = i;
+ break;
+ }
+ }
+ if(rowHeaders)
+ index++;
+ return index;
+ }
+
+ private String getColKeyByIndex(int index) {
+ return tHead.getHeaderCell(index).getColKey();
+ }
+
+ private void setColWidth(int colIndex, int w) {
+ HeaderCell cell = tHead.getHeaderCell(colIndex);
+ cell.setWidth(w);
+ tBody.setColWidth(colIndex, w);
+ String cid = cell.getColKey();;
+ columnWidths.put(cid,new Integer(w));
+ }
+
+ private int getColWidth(String colKey) {
+ return ( (Integer) this.columnWidths.get(colKey)).intValue();
+ }
+
+ private IScrollTableRow getRenderedRowByKey(String key) {
+ Iterator it = tBody.iterator();
+ IScrollTableRow r = null;
+ while(it.hasNext()) {
+ r = (IScrollTableRow) it.next();
+ if(r.getKey().equals(key))
+ return r;
+ }
+ return null;
+ }
+
+ private int getRenderedRowCount() {
+ return tBody.getLastRendered()-tBody.getFirstRendered();
+ }
+
+ private void reOrderColumn(String columnKey, int newIndex) {
+
+ int oldIndex = getColIndexByKey(columnKey);
+
+ // Change header order
+ tHead.moveCell(oldIndex, newIndex);
+
+ // Change body order
+ tBody.moveCol(oldIndex, newIndex);
+
+ // build new columnOrder and update it to server
+
+ String[] newOrder = new String[columnOrder.length];
+
+ Iterator hCells = tHead.iterator();
+
+ if(rowHeaders)
+ hCells.next();
+ int index = 0;
+ while(hCells.hasNext()) {
+ newOrder[index++] = ((HeaderCell) hCells.next()).getColKey();
+ }
+ columnOrder = newOrder;
+ client.updateVariable(id, "columnorder", newOrder, false);
+
+ }
+
+ /**
+ * This method has logick which rows needs to be requested from
+ * server when user scrolls
+ *
+ */
+ public void onScroll(Widget widget, int scrollLeft, int scrollTop) {
+ if(!initializedAndAttached)
+ return;
+
+ rowRequestHandler.cancel();
+
+ // fix headers horizontal scrolling
+ headerContainer.setHorizontalScrollPosition(scrollLeft);
+
+ firstRowInViewPort = (int) Math.ceil( scrollTop / (double) tBody.getRowHeight() );
+ client.console.log("At scrolltop: " + scrollTop + " At row " + firstRowInViewPort);
+
+ int postLimit = (int) (firstRowInViewPort + pageLength + pageLength*CACHE_REACT_RATE);
+ if(postLimit > totalRows -1 )
+ postLimit = totalRows - 1;
+ int preLimit = (int) (firstRowInViewPort - pageLength*CACHE_REACT_RATE);
+ if(preLimit < 0)
+ preLimit = 0;
+ int lastRendered = tBody.getLastRendered();
+ int firstRendered = tBody.getFirstRendered();
+ if( postLimit <= lastRendered && preLimit >= firstRendered ) {
+ client.updateVariable(this.id, "firstvisible", firstRowInViewPort, false);
+ return; // scrolled withing "non-react area"
+ }
+
+ if(firstRowInViewPort - pageLength*CACHE_RATE > lastRendered ||
+ firstRowInViewPort + pageLength + pageLength*CACHE_RATE < firstRendered ) {
+ // need a totally new set
+ client.console.log("Table: need a totally new set");
+ rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE));
+ rowRequestHandler.setReqRows((int) (2*CACHE_RATE*pageLength + pageLength));
+ rowRequestHandler.deferRowFetch();
+ return;
+ }
+ if(preLimit < firstRendered ) {
+ // need some rows to the beginning of the rendered area
+ client.console.log("Table: need some rows to the beginning of the rendered area");
+ rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE));
+ rowRequestHandler.setReqRows(firstRendered - rowRequestHandler.getReqFirstRow());
+ rowRequestHandler.deferRowFetch();
+
+ return;
+ }
+ if(postLimit > lastRendered) {
+ // need some rows to the end of the rendered area
+ client.console.log("need some rows to the end of the rendered area");
+ rowRequestHandler.setReqFirstRow(lastRendered + 1);
+ rowRequestHandler.setReqRows((int) ((firstRowInViewPort + pageLength + pageLength*CACHE_RATE) - lastRendered));
+ rowRequestHandler.deferRowFetch();
+ }
+
+ }
+
+
+
+ protected void onAttach() {
+
+ super.onAttach();
+ int bodyWidth = tBody.getOffsetWidth();
+
+ // sync column widths
+ initColumnWidths();
+
+ if(height < 0) {
+ bodyContainer.setHeight((tBody.getRowHeight()*pageLength) + "px");
+ } else {
+ bodyContainer.setHeight(height + "px");
+ }
+
+ if(width < 0) {
+ bodyWidth = tBody.getOffsetWidth();
+ bodyContainer.setWidth((tBody.getOffsetWidth() + getScrollBarWidth() ) + "px");
+ headerContainer.setWidth((tBody.getOffsetWidth()) + "px");
+ } else {
+ bodyContainer.setWidth(width + "px");
+ headerContainer.setWidth(width + "px");
+ }
+
+ tHead.disableBrowserIntelligence();
+
+ if(firstvisible > 0)
+ bodyContainer.setScrollPosition(firstvisible*tBody.getRowHeight());
+
+ DeferredCommand.add(new Command() {
+ public void execute() {
+ if(totalRows - 1 > tBody.getLastRendered()) {
+ // fetch cache rows
+ rowRequestHandler.setReqFirstRow(tBody.getLastRendered()+1);
+ rowRequestHandler.setReqRows((int) (pageLength*CACHE_RATE));
+ rowRequestHandler.deferRowFetch();
+ }
+ }
+ });
+
+
+ }
+
+ /**
+ * Run when receices its initial content. Syncs headers and bodys
+ * "natural widths and saves the values.
+ */
+ private void initColumnWidths() {
+ Iterator headCells = tHead.iterator();
+ int i = 0;
+ while(headCells.hasNext()) {
+ Element hCell = ((HeaderCell) headCells.next()).getElement();
+ int hw = DOM.getIntAttribute(hCell, "offsetWidth");
+ int cw = tBody.getColWidth(i);
+ int w = (hw > cw ? hw : cw) + IScrollTableBody.CELL_EXTRA_WIDTH;
+ setColWidth(i , w);
+ i++;
+ }
+ }
+
+ private int getScrollBarWidth() {
+ // TODO Auto-generated method stub
+ return 30;
+ }
+
+ private class RowRequestHandler extends Timer {
+
+ private int reqFirstRow = 0;
+ private int reqRows = 0;
+
+ public void deferRowFetch() {
+ if(reqRows > 0 && reqFirstRow < totalRows)
+ schedule(250);
+ }
+
+ public void setReqFirstRow(int reqFirstRow) {
+ if(reqFirstRow < 0)
+ reqFirstRow = 0;
+ else if(reqFirstRow >= totalRows)
+ reqFirstRow = totalRows - 1;
+ this.reqFirstRow = reqFirstRow;
+ }
+
+ public void setReqRows(int reqRows) {
+ this.reqRows = reqRows;
+ }
+
+ public void run() {
+ client.console.log("Getting " + reqRows + " rows from " + reqFirstRow);
+ client.updateVariable(id, "firstvisible", firstRowInViewPort, false);
+ client.updateVariable(id, "reqfirstrow", reqFirstRow, false);
+ client.updateVariable(id, "reqrows", reqRows, true);
+ }
+
+ public int getReqFirstRow() {
+ return reqFirstRow;
+ }
+
+ public int getReqRows() {
+ return reqRows;
+ }
+
+ }
+
+ public class HeaderCell extends Widget {
+
+ private static final int DRAG_WIDGET_WIDTH = 2;
+
+ private static final int MINIMUM_COL_WIDTH = 20;
+
+ Element td = DOM.createTD();
+
+ Element captionContainer = DOM.createDiv();
+
+ Element colResizeWidget = DOM.createDiv();
+
+ Element floatingCopyOfHeaderCell;
+
+ private boolean sortable = false;
+ private String cid;
+ private boolean dragging;
+
+ private int dragStartX;
+ private int colIndex;
+ private int originalWidth;
+
+ private boolean isResizing;
+
+ private int headerX;
+
+ private boolean moved;
+
+ private int closestSlot;
+
+
+ private HeaderCell(){};
+
+ public void setSortable(boolean b) {
+ if(b == sortable)
+ return;
+ sortable = b;
+ }
+
+ public HeaderCell(String colId, String headerText) {
+ this.cid = colId;
+
+ DOM.setStyleAttribute(colResizeWidget,"display", "block");
+ DOM.setStyleAttribute(colResizeWidget, "width", DRAG_WIDGET_WIDTH +"px");
+ DOM.setStyleAttribute(colResizeWidget,"height", "20px");
+ DOM.setStyleAttribute(colResizeWidget,"cssFloat", "right");
+ DOM.setStyleAttribute(colResizeWidget, "styleFloat", "right");
+ DOM.setStyleAttribute(colResizeWidget,"background", "brown");
+ DOM.setStyleAttribute(colResizeWidget,"cursor", "e-resize");
+ DOM.sinkEvents(colResizeWidget,Event.MOUSEEVENTS);
+
+ setText(headerText);
+
+ DOM.appendChild(td, colResizeWidget);
+
+
+ DOM.setStyleAttribute(captionContainer, "cssFloat", "right");
+ DOM.setStyleAttribute(captionContainer, "styleFloat", "right");
+ DOM.setStyleAttribute(captionContainer, "overflow", "hidden");
+ DOM.setStyleAttribute(captionContainer, "white-space", "nowrap");
+ DOM.setStyleAttribute(captionContainer, "display", "inline");
+ DOM.sinkEvents(captionContainer, Event.MOUSEEVENTS);
+
+ DOM.appendChild(td, captionContainer);
+
+ setElement(td);
+ }
+
+ public void setWidth(int w) {
+ DOM.setStyleAttribute(captionContainer, "width", (w - DRAG_WIDGET_WIDTH - 4) + "px");
+ setWidth(w + "px");
+ }
+
+ public void setText(String headerText) {
+ DOM.setInnerHTML(captionContainer, headerText);
+ }
+ public String getColKey() {
+ return cid;
+ }
+
+ private void setSorted(boolean sorted) {
+ if(sorted) {
+ if(sortAscending)
+ this.setStyleName("headerCellAsc");
+ else
+ this.setStyleName("headerCellDesc");
+ } else {
+ this.setStyleName("headerCell");
+ }
+ }
+
+ /**
+ * Handle column reordering.
+ */
+ public void onBrowserEvent(Event event) {
+
+ Element target = DOM.eventGetTarget(event);
+
+ if(isResizing || DOM.compare(target, colResizeWidget)) {
+ onResizeEvent(event);
+ } else {
+ handleCaptionEvent(event);
+ }
+
+
+ super.onBrowserEvent(event);
+ }
+
+
+ private void createFloatingCopy() {
+ floatingCopyOfHeaderCell = DOM.createDiv();
+ DOM.setInnerHTML(floatingCopyOfHeaderCell, DOM.getInnerHTML(td));
+ floatingCopyOfHeaderCell = DOM.getChild(floatingCopyOfHeaderCell, 1);
+ // TODO isolate non-standard css attribute (filter)
+ // TODO move styles to css file
+ DOM.setStyleAttribute(floatingCopyOfHeaderCell, "position", "absolute");
+ DOM.setStyleAttribute(floatingCopyOfHeaderCell, "background", "#000000");
+ DOM.setStyleAttribute(floatingCopyOfHeaderCell, "color", "#ffffff");
+ DOM.setStyleAttribute(floatingCopyOfHeaderCell, "opacity", "0.5");
+ DOM.setStyleAttribute(floatingCopyOfHeaderCell, "filter", "alpha(opacity=100)");
+ updateFloatingCopysPosition(DOM.getAbsoluteLeft(td), DOM.getAbsoluteTop(td));
+ DOM.appendChild(IScrollTable.this.getElement(), floatingCopyOfHeaderCell);
+ }
+
+ private void updateFloatingCopysPosition(int x, int y) {
+ x -= DOM.getIntAttribute(floatingCopyOfHeaderCell, "offsetWidth")/2;
+ DOM.setStyleAttribute(floatingCopyOfHeaderCell, "left", x + "px");
+ if(y > 0)
+ DOM.setStyleAttribute(floatingCopyOfHeaderCell, "top", (y + 7) + "px");
+ }
+
+ private void hideFloatingCopy() {
+ DOM.removeChild(IScrollTable.this.getElement(), floatingCopyOfHeaderCell);
+ floatingCopyOfHeaderCell = null;
+ }
+
+ private void handleCaptionEvent(Event event) {
+ switch (DOM.eventGetType(event)) {
+ case Event.ONMOUSEDOWN:
+ dragging = true;
+ moved = false;
+ colIndex = getColIndexByKey(cid);
+ DOM.setCapture(getElement());
+
+ this.headerX = tHead.getAbsoluteLeft();
+
+ DOM.eventPreventDefault(event);
+ break;
+ case Event.ONMOUSEUP:
+ dragging = false;
+ DOM.releaseCapture(getElement());
+
+ if(!moved) {
+ // mouse event was a click to header -> sort column
+ if(sortable) {
+ if(sortColumn.equals(cid)) {
+ // just toggle order
+ client.updateVariable(id, "sortascending", !sortAscending, false);
+ } else {
+ // set table scrolled by this column
+ client.updateVariable(id, "sortcolumn", cid, false);
+ }
+ // get also cache columns at the same request
+ bodyContainer.setScrollPosition(0);
+ firstvisible = 0;
+ rowRequestHandler.setReqFirstRow(0);
+ rowRequestHandler.setReqRows((int) (2*pageLength*CACHE_RATE + pageLength));
+ rowRequestHandler.deferRowFetch();
+ }
+ break;
+ }
+ System.out.println("Stopped column reordering");
+ hideFloatingCopy();
+ tHead.removeSlotFocus();
+ if(closestSlot != colIndex && closestSlot != (colIndex + 1) ) {
+ if(closestSlot > colIndex)
+ reOrderColumn(cid, closestSlot - 1);
+ else
+ reOrderColumn(cid, closestSlot);
+ }
+ break;
+ case Event.ONMOUSEMOVE:
+ if (dragging) {
+ System.out.print("Dragging column, optimal index...");
+ if(!moved) {
+ createFloatingCopy();
+ moved = true;
+ }
+ int x = DOM.eventGetClientX(event);
+ int slotX = headerX;
+ closestSlot = colIndex;
+ int closestDistance = -1;
+ int start = 0;
+ if(rowHeaders) {
+ start++;
+ }
+ for(int i = start; i <= columnWidths.size() ; i++ ) {
+ if(i > 0) {
+ String colKey = getColKeyByIndex(i-1);
+ slotX += getColWidth(colKey);
+ }
+ int dist = Math.abs(x - slotX);
+ if(closestDistance == -1 || dist < closestDistance) {
+ closestDistance = dist;
+ closestSlot = i;
+ }
+ }
+ tHead.focusSlot(closestSlot);
+
+ updateFloatingCopysPosition(x, -1);
+ System.out.println(closestSlot);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void onResizeEvent(Event event) {
+ switch (DOM.eventGetType(event)) {
+ case Event.ONMOUSEDOWN:
+ isResizing = true;
+ DOM.setCapture(getElement());
+ dragStartX = DOM.eventGetClientX(event);
+ colIndex = getColIndexByKey(cid);
+ originalWidth = IScrollTable.this.tBody.getColWidth(colIndex);
+ DOM.eventPreventDefault(event);
+ break;
+ case Event.ONMOUSEUP:
+ isResizing = false;
+ DOM.releaseCapture(getElement());
+ break;
+ case Event.ONMOUSEMOVE:
+ if (isResizing) {
+ int deltaX = DOM.eventGetClientX(event) - dragStartX ;
+ if(deltaX == 0)
+ return;
+
+ int newWidth = originalWidth + deltaX;
+ if(newWidth < MINIMUM_COL_WIDTH)
+ newWidth = MINIMUM_COL_WIDTH;
+ setColWidth(colIndex, newWidth);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ }
+
+ public class TableHead extends Panel {
+
+ private static final int WRAPPER_WIDTH = 9000;
+
+ Vector cells = new Vector();
+
+ Element div = DOM.createDiv();
+ Element table = DOM.createTable();
+ Element tBody = DOM.createTBody();
+ Element tr = DOM.createTR();
+
+ private int focusedSlot = -1;
+
+ public TableHead() {
+ DOM.appendChild(table, tBody);
+ DOM.appendChild(tBody, tr);
+ DOM.appendChild(div, table);
+ setElement(div);
+ }
+
+ public void disableBrowserIntelligence() {
+ DOM.setStyleAttribute(div, "width", WRAPPER_WIDTH +"px");
+ }
+
+ public void setHeaderCell(int index, HeaderCell cell) {
+ if(index < cells.size()) {
+ // replace
+ // TODO remove old correctly
+ // insert to right slot
+ } else if( index == cells.size()) {
+ //append
+ adopt(cell, tr);
+ cells.add(cell);
+ } else {
+ throw new RuntimeException("Header cells must be appended in order");
+ }
+ }
+
+ public HeaderCell getHeaderCell(int index) {
+ if(index < cells.size())
+ return (HeaderCell) cells.get(index);
+ else
+ return null;
+ }
+
+ public void moveCell(int oldIndex, int newIndex) {
+ HeaderCell hCell = getHeaderCell(oldIndex);
+ Element cell = hCell.getElement();
+
+ cells.remove(oldIndex);
+ DOM.removeChild(tr, cell);
+
+ DOM.insertChild(tr, cell, newIndex);
+ cells.insertElementAt(hCell, newIndex);
+ }
+
+ public Iterator iterator() {
+ return cells.iterator();
+ }
+
+ public boolean remove(Widget w) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ private void focusSlot(int index) {
+ removeSlotFocus();
+ if(index > 0)
+ DOM.setStyleAttribute(DOM.getChild(tr, index - 1), "borderRight", "2px solid black");
+ else
+ DOM.setStyleAttribute(DOM.getChild(tr, index), "borderLeft", "2px solid black");
+ focusedSlot = index;
+ }
+
+ private void removeSlotFocus() {
+ if(focusedSlot < 0)
+ return;
+ if(focusedSlot == 0)
+ DOM.setStyleAttribute(DOM.getChild(tr, focusedSlot), "borderLeft", "none");
+ else if( focusedSlot > 0)
+ DOM.setStyleAttribute(DOM.getChild(tr, focusedSlot - 1), "borderRight", "none");
+ focusedSlot = -1;
+ }
+
+
+ }
+
+ /**
+ * This Panel can only contain IScrollTAbleRow type of
+ * widgets. This "simulates" very large table, keeping
+ * spacers which take room of unrendered rows.
+ *
+ * @author mattitahvonen
+ *
+ */
+ public class IScrollTableBody extends Panel {
+
+ public static final int CELL_EXTRA_WIDTH = 20;
+
+ public static final int DEFAULT_ROW_HEIGHT = 25;
+
+ private int rowHeight = -1;
+
+ private List renderedRows = new Vector();
+
+ private boolean initDone = false;
+
+ private int totalRows;
+
+ Element preSpacer = DOM.createDiv();
+ Element postSpacer = DOM.createDiv();
+
+ Element container = DOM.createDiv();
+
+ Element tBody = DOM.createTBody();
+ Element table = DOM.createTable();
+
+ private int firstRendered;
+
+ private int lastRendered;
+
+ IScrollTableBody() {
+
+ constructDOM();
+
+ setElement(container);
+
+ }
+
+ private void constructDOM() {
+ DOM.setAttribute(table, "className", "iscrolltable-table");
+ DOM.setAttribute(preSpacer, "className", "iscrolltable-rowspacer");
+ DOM.setAttribute(postSpacer, "className", "iscrolltable-rowspacer");
+
+ DOM.appendChild(table, tBody);
+ DOM.appendChild(container, preSpacer);
+ DOM.appendChild(container, table);
+ DOM.appendChild(container, postSpacer);
+
+ }
+
+
+ public void renderInitialRows(UIDL rowData, int firstIndex, int rows, int totalRows) {
+ this.totalRows = totalRows;
+ this.firstRendered = firstIndex;
+ this.lastRendered = firstIndex + rows - 1 ;
+ Iterator it = rowData.getChildIterator();
+ while(it.hasNext()) {
+ IScrollTableRow row = new IScrollTableRow((UIDL) it.next());
+ addRow(row);
+ }
+ if(isAttached())
+ fixSpacers();
+ }
+
+ public void renderRows(UIDL rowData, int firstIndex, int rows) {
+ Iterator it = rowData.getChildIterator();
+ if(firstIndex == lastRendered + 1) {
+ while(it.hasNext()) {
+ IScrollTableRow row = createRow((UIDL) it.next());
+ addRow(row);
+ lastRendered++;
+ }
+ fixSpacers();
+ } else if(firstIndex + rows == firstRendered) {
+ IScrollTableRow[] rowArray = new IScrollTableRow[rows];
+ int i = rows;
+ while(it.hasNext()) {
+ i--;
+ rowArray[i] = createRow((UIDL) it.next());
+ }
+ for(i = 0 ; i < rows; i++) {
+ addRowBeforeFirstRendered(rowArray[i]);
+ firstRendered--;
+ }
+// } else if (firstIndex > lastRendered || firstIndex + rows < firstRendered) {
+ } else if (true) {
+ // complitely new set of rows
+ // create one row before truncating row
+ IScrollTableRow row = createRow((UIDL) it.next());
+ while(lastRendered + 1 > firstRendered)
+ unlinkRow(false);
+ firstRendered = firstIndex;
+ lastRendered = firstIndex - 1 ;
+ fixSpacers();
+ addRow(row);
+ lastRendered++;
+ while(it.hasNext()) {
+ addRow(createRow((UIDL) it.next()));
+ lastRendered++;
+ }
+ fixSpacers();
+ } else {
+ // sorted or column reordering changed
+ client.console.log("Bad update" + firstIndex + "/"+ rows);
+ }
+ }
+
+ /**
+ * This mehtod is used to instantiate new rows for this table.
+ * It automatically sets correct widths to rows cells and assigns
+ * correct client reference for child widgets.
+ *
+ * This method can be called only after table has been initialized
+ *
+ * @param uidl
+ * @param client2
+ */
+ private IScrollTableRow createRow(UIDL uidl) {
+ IScrollTableRow row = new IScrollTableRow(uidl);
+ int cells = DOM.getChildCount(row.getElement());
+ for(int i = 0; i < cells; i++) {
+ Element cell = DOM.getChild(row.getElement(), i);
+ int w = getColWidth(i);
+ DOM.setStyleAttribute(cell, "width", w + "px");
+ DOM.setStyleAttribute(DOM.getFirstChild(cell), "width", w + "px");
+ }
+ return row;
+ }
+
+ private void addRowBeforeFirstRendered(IScrollTableRow row) {
+ DOM.insertChild(tBody, row.getElement(), 0);
+ adopt(row, null);
+ renderedRows.add(0, row);
+ }
+
+ private void addRow(IScrollTableRow row) {
+ DOM.appendChild(tBody, row.getElement());
+ adopt(row, null);
+ renderedRows.add(row);
+ }
+
+ public Iterator iterator() {
+ return renderedRows.iterator();
+ }
+
+ public void unlinkRow(boolean fromBeginning) {
+ if(lastRendered - firstRendered < 0)
+ return;
+ int index;
+ if(fromBeginning) {
+ index = 0;
+ firstRendered++;
+ } else {
+ index = renderedRows.size() - 1;
+ lastRendered--;
+ }
+ IScrollTableRow toBeRemoved = (IScrollTableRow) renderedRows.get(index);
+ this.disown(toBeRemoved);
+ renderedRows.remove(index);
+ fixSpacers();
+ }
+
+ public boolean remove(Widget w) {
+ throw new UnsupportedOperationException();
+ }
+
+ protected void onAttach() {
+ super.onAttach();
+ fixSpacers();
+ // fix container blocks height to avoid "bouncing" when scrolling
+ DOM.setStyleAttribute(container, "height", totalRows*getRowHeight() + "px");
+ }
+
+ private void fixSpacers() {
+ DOM.setStyleAttribute(preSpacer, "height", getRowHeight()*firstRendered + "px");
+ DOM.setStyleAttribute(postSpacer, "height", getRowHeight()*(totalRows - 1 - lastRendered) + "px");
+ }
+
+ public int getTotalRows() {
+ return totalRows;
+ }
+
+ public int getRowHeight() {
+ if(initDone)
+ return rowHeight;
+ else {
+ if(DOM.getChildCount(tBody) > 0) {
+ rowHeight = DOM.getIntAttribute(tBody, "offsetHeight")/DOM.getChildCount(tBody);
+ } else {
+ return DEFAULT_ROW_HEIGHT;
+ }
+ initDone = true;
+ return rowHeight;
+ }
+ }
+
+ public int getColWidth(int i) {
+ Element e = DOM.getChild(DOM.getChild(tBody, 0), i);
+ return DOM.getIntAttribute(e, "offsetWidth");
+ }
+
+ public void setColWidth(int colIndex, int w) {
+ int rows = DOM.getChildCount(tBody);
+ for(int i = 0; i < rows; i++) {
+ Element cell = DOM.getChild(DOM.getChild(tBody, i), colIndex);
+ DOM.setStyleAttribute(cell, "width", w + "px");
+ DOM.setStyleAttribute(DOM.getFirstChild(cell), "width", w + "px");
+ }
+ }
+
+ public int getLastRendered() {
+ return lastRendered;
+ }
+
+ public int getFirstRendered() {
+ return firstRendered;
+ }
+
+ public void moveCol(int oldIndex, int newIndex) {
+
+ // loop all rows and move given index to its new place
+ Iterator rows = iterator();
+ while(rows.hasNext()) {
+ IScrollTableRow row = (IScrollTableRow) rows.next();
+
+ Element td = DOM.getChild(row.getElement(), oldIndex);
+ DOM.removeChild(row.getElement(), td);
+
+ DOM.insertChild(row.getElement(), td, newIndex);
+
+ }
+
+ }
+
+ public class IScrollTableRow extends Panel {
+
+ Vector childWidgets = new Vector();
+ private boolean selected = false;
+ private int rowKey;
+
+ private IScrollTableRow(int rowKey) {
+ this.rowKey = rowKey;
+ this.selected = selected;
+ setElement(DOM.createElement("tr"));
+ DOM.sinkEvents(getElement(), Event.BUTTON_RIGHT | Event.ONCLICK);
+ setStyleName("iscrolltable-row");
+ }
+
+ public String getKey() {
+ return String.valueOf(rowKey);
+ }
+
+ public IScrollTableRow(UIDL uidl) {
+ this(uidl.getIntAttribute("key"));
+ if(uidl.hasAttribute("caption"))
+ addCell(uidl.getStringAttribute("caption"));
+ Iterator cells = uidl.getChildIterator();
+ while(cells.hasNext()) {
+ Object cell = cells.next();
+ if (cell instanceof String) {
+ addCell(cell.toString());
+ } else {
+ Widget cellContent = client.getWidget((UIDL) cell);
+ (( Paintable) cellContent).updateFromUIDL((UIDL) cell, client);
+ }
+ }
+ if(uidl.hasAttribute("selected"))
+ toggleSelection();
+ }
+
+ public void addCell(String text) {
+ // String only content is optimized by not using Label widget
+ Element td = DOM.createTD();
+ Element container = DOM.createDiv();
+ DOM.setAttribute(container, "className", "iscrolltable-cellContent");
+ DOM.setInnerHTML(container, text);
+ DOM.appendChild(td, container);
+ DOM.appendChild(getElement(), td);
+ }
+
+ public void addCell(Widget w) {
+ Element td = DOM.createTD();
+ Element container = DOM.createDiv();
+ DOM.setAttribute(container, "className", "iscrolltable-cellContent");
+ DOM.appendChild(td, container);
+ DOM.appendChild(getElement(), td);
+ adopt(w, container);
+ childWidgets.add(w);
+ }
+
+ public Iterator iterator() {
+ return childWidgets.iterator();
+ }
+
+ public boolean remove(Widget w) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void onBrowserEvent(Event event) {
+ switch (DOM.eventGetType(event)) {
+ case Event.BUTTON_RIGHT:
+ // TODO
+ System.out.println("Context menu");
+ break;
+
+ case Event.ONCLICK:
+ System.out.println("Row click");
+ if(selectMode > ITable.SELECT_MODE_NONE) {
+ toggleSelection();
+ client.updateVariable(id, "selected", selectedRowKeys.toArray(), immediate);
+ }
+ break;
+
+ default:
+ break;
+ }
+ super.onBrowserEvent(event);
+ }
+
+ public boolean isSelected() {
+ return selected;
+ }
+
+ private void toggleSelection() {
+ selected = !selected;
+ if(selected) {
+ if(selectMode == ITable.SELECT_MODE_SINGLE)
+ IScrollTable.this.deselectAll();
+ selectedRowKeys.add(String.valueOf(rowKey));
+
+ setStyleName("iscrolltable-selRow");
+ } else {
+ selectedRowKeys.remove(String.valueOf(rowKey));
+ setStyleName("iscrolltable-row");
+ }
+ }
+
+ }
+
+ }
+
+ public void deselectAll() {
+ Object[] keys = selectedRowKeys.toArray();
+ for (int i = 0; i < keys.length; i++) {
+ IScrollTableRow row = getRenderedRowByKey((String) keys[i]);
+ if(row != null && row.isSelected())
+ row.toggleSelection();
+ }
+ // still ensure all selects are removed from (not necessary rendered)
+ selectedRowKeys.clear();
+
+ }
+}
+++ /dev/null
-package com.itmill.toolkit.terminal.gwt.client.ui;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-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.Timer;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.ScrollListener;
-import com.google.gwt.user.client.ui.ScrollPanel;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.itmill.toolkit.terminal.gwt.client.Client;
-import com.itmill.toolkit.terminal.gwt.client.Paintable;
-import com.itmill.toolkit.terminal.gwt.client.UIDL;
-
-// TODO Blow this out and build it directly with DOM
-// Below is the structure of the 4.0 implementation
-/*
-<div class="border">
- <div id="PID7status" class="tablestatus" style="display: none; margin-top: 35px; margin-left: 122px;"/>
- <div class="hcontainer">
- <div class="colsel-container"></div>
- <div id="PID7hout" class="bg hout" style="overflow: hidden; width: 375px;">
- <div style="width: 6000px;">
- <table id="PID7hin" class="hin" cellspacing="0" cellpadding="0" border="0">
- <tbody>
- <tr>
- <td id="PID7heh" class="heh clickable" cid="heh" style="width: 20px;">
- </td>
- <td id="PID7he0" class="clickable" cid="1" style="width: 91px;">
- </td>
- <td id="PID7he1" class="clickable" cid="2" style="width: 88px;">
- </td>
- <td id="PID7he2" class="clickable" cid="3" style="width: 108px;">
- <img id="PID7ha3" class="colresizer" src="/demo/TableDemo/RES/corporate/img/table/handle.gif"/>
- <div class="headerContent" style="width: 91px; left: 0px; top: 0px;">TITLE</div>
- </td>
- <td id="PID7he3" class="clickable" cid="4" style="width: 68px;">
- </td>
- </tr>
- </tbody>
- </table>
- <div/>
- </div>
- </div>
- </div>
- <div id="PID7cout" class="cout" style="overflow: auto; height: 140px; width: 391px;">
- <div class="spacer" style="height: 0px;"/>
- <table id="PID7cin" class="cin">
- <tbody>
- <tr class="odd clickable">
- <td class="tablecell rowheader" width="20">
- <div class="cellContent" style="width: 16px;">0</div>
- </td>
- ...
- </tr>
- ...
- </tbody>
- </table>
- <div class="spacer" style="height: 13440px;"/>
- </div>
-</div>
- */
-public class ITableScrollingByRecyclingOldDomImplementation extends Composite implements Paintable, ScrollListener {
-
- /**
- * multiple of pagelenght which component will
- * cache when requesting more rows
- */
- private static final double CACHE_RATE = 3;
- /**
- * fraction of pageLenght which can be scrolled without
- * making new request
- */
- private static final double CACHE_REACT_RATE = 1;
-
- private int firstRendered = -1;
- private int lastRendered = -1;
- private int firstRowInViewPort = 0;
- private int pageLength = 15;
-
- private boolean rowHeaders = false;
-
- private Map columnOrder = new HashMap();
-
- private Client client;
- private String id;
- private boolean immediate;
-
- private FlexTable tHead = new FlexTable();
- private FlexTable tBody = new FlexTable();
-
- private ScrollPanel bodyContainer = new ScrollPanel();
- private VerticalPanel bodyContent = new VerticalPanel();
-
- private ScrollPanel headerContainer = new ScrollPanel();
-
- private HTML preSpacer = new HTML();
- private HTML postSpacer = new HTML();
-
- private boolean colWidthsInitialized = false;
- private int totalRows;
- private HashMap columnWidths = new HashMap();
-
- private int rowHeight = 0;
- private RowRequestHandler rowRequestHandler;
-
- public ITableScrollingByRecyclingOldDomImplementation() {
- headerContainer.add(tHead);
- DOM.setStyleAttribute(headerContainer.getElement(), "overflow", "hidden");
-
- tBody.setStyleName("itable-tbody");
-
- bodyContent.add(preSpacer);
- bodyContent.add(tBody);
- bodyContent.add(postSpacer);
- //TODO remove debug color
- DOM.setStyleAttribute(postSpacer.getElement(), "background", "gray");
- bodyContainer.add(bodyContent);
- bodyContainer.addScrollListener(this);
-
- VerticalPanel panel = new VerticalPanel();
- panel.add(headerContainer);
- panel.add(bodyContainer);
-
- rowRequestHandler = new RowRequestHandler();
-
- initWidget(panel);
- }
-
- public void updateFromUIDL(UIDL uidl, Client client) {
- if (client.updateComponent(this, uidl, true))
- return;
-
- this.client = client;
- this.id = uidl.getStringAttribute("id");
- this.immediate = uidl.getBooleanAttribute("immediate");
- this.totalRows = uidl.getIntAttribute("totalrows");
- this.pageLength = uidl.getIntAttribute("pagelength");
- if(uidl.hasAttribute("rowheaders"))
- rowHeaders = true;
-
- UIDL columnInfo = null;
- UIDL rowData = null;
- for(Iterator it = uidl.getChildIterator(); it.hasNext();) {
- UIDL c = (UIDL) it.next();
- if(c.getTag().equals("cols"))
- columnInfo = c;
- else if(c.getTag().equals("rows"))
- rowData = c;
- else if(c.getTag().equals("actions"))
- updateActionMap(c);
- else if(c.getTag().equals("visiblecolumns"))
- ;
- }
- updateHeader(columnInfo);
-
- updateBody(rowData, uidl.getIntAttribute("firstrow"),uidl.getIntAttribute("rows"));
-
- if(!colWidthsInitialized) {
- DeferredCommand.add(new Command() {
- public void execute() {
- initSize();
- updateSpacers();
- bodyContainer.setScrollPosition(getRowHeight()*(firstRowInViewPort -1));
- colWidthsInitialized = true;
- if(totalRows - 1 > lastRendered) {
- // fetch cache rows
- rowRequestHandler.setReqFirstRow(lastRendered+1);
- rowRequestHandler.setReqRows((int) (pageLength*CACHE_RATE));
- rowRequestHandler.deferRowFetch();
- }
- }
- });
- }
- }
-
- private void updateActionMap(UIDL c) {
- // TODO Auto-generated method stub
-
- }
-
- private void updateHeader(UIDL uidl) {
- if(uidl == null)
- return;
- for(Iterator it = uidl.getChildIterator();it.hasNext();) {
- UIDL col = (UIDL) it.next();
- String cid = col.getStringAttribute("cid");
- int colIndex = getColIndexByKey(cid);
- if(colIndex > -1)
- setHeaderText(colIndex, col.getStringAttribute("caption"));
- DOM.setAttribute(tHead.getFlexCellFormatter().getElement(0, colIndex), "cid", cid);
- }
- }
-
- /**
- * Updates row data from uidl. UpdateFromUIDL delegates updating
- * tBody to this method.
- *
- * Updates may be to different part of tBody, depending on update type.
- * It can be initial row data, scroll up, scroll down...
- *
- * @param uidl which contains row data
- * @param firstRow first row in data set
- * @param reqRows amount of rows in data set
- */
- private void updateBody(UIDL uidl, int firstRow, int reqRows) {
- if(uidl == null || reqRows < 1)
- return;
-
- Iterator it = uidl.getChildIterator();
-
- if(firstRendered == -1 || firstRow == lastRendered + 1) {
- //initial data to body or appending rows to table
- while(it.hasNext()) {
- appendRow( (UIDL) it.next() );
- if(colWidthsInitialized)
- updateSpacers();
- }
-// lastRendered = firstRow + reqRows - 1;
- if(firstRendered == -1) {
- firstRendered = firstRow;
- }
- } else if(firstRendered == firstRow + reqRows) {
- // add received rows before old ones
- int rowsAdded = 0;
- while(it.hasNext()){
- tBody.insertRow(rowsAdded);
- updateSpacers();
- updateRow( (UIDL) it.next(), rowsAdded);
- }
- firstRendered = firstRow;
- } else {
- // complitely new set received, truncate body and recurse
- tBody.clear();
- firstRendered = -1;
- lastRendered = -1;
- updateBody(uidl, firstRow, reqRows);
- }
- trimBody();
- }
-
- /**
- * Returns calculated height of row.
- * @return height in pixels
- */
- private int getRowHeight() {
- if(rowHeight == 0)
- rowHeight = tBody.getOffsetHeight()/getRenderedRowCount();
- return rowHeight;
- }
-
- /**
- * This method removes rows from body which are "out of
- * cache area" to keep amount of rendered rows sane.
- */
- private void trimBody() {
- int toBeRemovedFromTheBeginning = (int) (firstRowInViewPort - CACHE_RATE*pageLength) - firstRendered;
- int toBeRemovedFromTheEnd = lastRendered - (int) (firstRowInViewPort + CACHE_RATE*pageLength + pageLength);
- if(toBeRemovedFromTheBeginning > 0) {
- // remove extra rows from the beginning of the table
- while(toBeRemovedFromTheBeginning > 0) {
- tBody.removeRow(0);
- firstRendered++;
- toBeRemovedFromTheBeginning--;
- updateSpacers();
- }
- }
- if(toBeRemovedFromTheEnd > 0) {
- // remove extra rows from the end of the table
- while(toBeRemovedFromTheEnd > 0) {
- tBody.removeRow(tBody.getRowCount() - 1);
- toBeRemovedFromTheEnd--;
- lastRendered--;
- updateSpacers();
- }
- }
-// bodyContainer.setScrollPosition(getRowHeight()*firstRowInViewPort);
- }
-
- private void appendRow(UIDL uidl) {
- lastRendered++;
- updateRow(uidl, lastRendered);
- }
-
- private void updateRow(UIDL uidl, int rowIndex) {
- int colIndex = 0;
- if(rowHeaders) {
- setCellContent(rowIndex, colIndex, uidl.getStringAttribute("caption"));
- colIndex++;
- }
-
- for(Iterator it = uidl.getChildIterator(); it.hasNext();) {
- Object cell = it.next();
- if (cell instanceof String) {
- setCellContent(rowIndex, colIndex, (String) cell);
- } else {
- setCellContent(rowIndex, colIndex, (UIDL) cell);
- }
- colIndex++;
- }
- Element row = tBody.getRowFormatter().getElement(rowIndex);
- DOM.setIntAttribute(row, "key", uidl.getIntAttribute("key"));
- }
-
- private int getColIndexByKey(String colKey) {
- return Integer.parseInt(colKey) - 1 + (rowHeaders ? 1 : 0);
- }
-
- private String getColKeyByIndex(int index) {
- return DOM.getAttribute(tHead.getCellFormatter().getElement(0, index), "cid");
- }
-
- public void setHeaderText(int colIndex, String text) {
- tHead.setText(0, colIndex, text);
- }
-
- public void setCellContent(int rowId, int colId, UIDL cell) {
- if(cell == null)
- return;
- Widget cellContent = client.getWidget(cell);
- tBody.setWidget(rowId, colId, cellContent);
- ((Paintable)cell).updateFromUIDL(cell, client);
- tBody.getCellFormatter().setWordWrap(rowId, colId, false);
- }
-
- public void setCellContent(int rowId, int colId, String text) {
- HTML cellContent = new HTML();
- cellContent.setText(text);
- tBody.setWidget(rowId, colId, cellContent);
- }
-
- /**
- * Run when receices its initial content. Syncs headers and bodys
- * "natural widths and saves the values.
- */
- private void initSize() {
- int cols = tHead.getCellCount(0);
- FlexCellFormatter hf = tHead.getFlexCellFormatter();
- FlexCellFormatter bf = tBody.getFlexCellFormatter();
- for (int i = 0; i < cols; i++) {
- Element hCell = hf.getElement(0, i);
- Element bCell = bf.getElement(1, i);
- int hw = DOM.getIntAttribute(hCell, "offsetWidth");
- int cw = DOM.getIntAttribute(bCell, "offsetWidth");
- setColWidth(i , hw > cw ? hw : cw);
- }
-
- bodyContainer.setHeight(tBody.getOffsetHeight() + "px");
- bodyContainer.setWidth((tBody.getOffsetWidth() + 20) + "px");
-
- }
-
- private void setColWidth(int colIndex, int w) {
- String cid = getColKeyByIndex(colIndex);
- tHead.getCellFormatter().setWidth(0, colIndex, w + "px");
- tBody.getCellFormatter().setWidth(0, colIndex, w + "px");
- columnWidths.put(cid,new Integer(w));
- }
-
- private int getColWidth(String colKey) {
- return ( (Integer) this.columnWidths.get(colKey)).intValue();
- }
-
- private void updateSpacers() {
- int preSpacerHeight = (firstRendered)*getRowHeight();
- int postSpacerHeight = (totalRows - 1 - lastRendered)*getRowHeight();
- preSpacer.setHeight(preSpacerHeight+"px");
- postSpacer.setHeight(postSpacerHeight + "px");
- }
-
- private int getRenderedRowCount() {
- return lastRendered-firstRendered;
- }
-
- /**
- * This method has logick which rows needs to be requested from
- * server when user scrolls
- *
- */
- public void onScroll(Widget widget, int scrollLeft, int scrollTop) {
- rowRequestHandler.cancel();
-
- firstRowInViewPort = (int) Math.ceil( scrollTop / rowHeight );
- client.console.log("At scrolltop: " + scrollTop + " At row " + firstRowInViewPort);
-
- int postLimit = (int) (firstRowInViewPort + pageLength + pageLength*CACHE_REACT_RATE);
- if(postLimit > totalRows)
- postLimit = totalRows;
- int preLimit = (int) (firstRowInViewPort - pageLength*CACHE_REACT_RATE);
- if(preLimit < 0)
- preLimit = 0;
- if(
- (postLimit <= lastRendered && preLimit >= firstRendered )
- ) {
- client.updateVariable(this.id, "firstvisible", firstRowInViewPort, false);
- return; // scrolled withing "non-react area"
- }
-
- if(firstRowInViewPort - pageLength*CACHE_RATE > lastRendered ||
- firstRowInViewPort + pageLength + pageLength*CACHE_RATE < firstRendered ) {
- // need a totally new set
- client.console.log("Table: need a totally new set");
- rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE));
- rowRequestHandler.setReqRows((int) (2*CACHE_RATE*pageLength + pageLength));
- rowRequestHandler.deferRowFetch();
- return;
- }
- if(preLimit < firstRendered ) {
- // need some rows to the beginning of the rendered area
- client.console.log("Table: need some rows to the beginning of the rendered area");
- rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE));
- rowRequestHandler.setReqRows(firstRendered - rowRequestHandler.getReqFirstRow());
- rowRequestHandler.deferRowFetch();
-
- return;
- }
- if(postLimit > lastRendered) {
- // need some rows to the end of the rendered area
- client.console.log("need some rows to the end of the rendered area");
- rowRequestHandler.setReqFirstRow(lastRendered + 1);
- rowRequestHandler.setReqRows((int) ((firstRowInViewPort + pageLength + pageLength*CACHE_RATE) - lastRendered));
- rowRequestHandler.deferRowFetch();
- }
- }
-
- private class RowRequestHandler extends Timer {
-
- private int reqFirstRow = 0;
- private int reqRows = 0;
-
- public void deferRowFetch() {
- if(reqRows > 0 && reqFirstRow < totalRows)
- schedule(250);
- }
-
- public void setReqFirstRow(int reqFirstRow) {
- if(reqFirstRow < 0)
- reqFirstRow = 0;
- else if(reqFirstRow >= totalRows)
- reqFirstRow = totalRows - 1;
- this.reqFirstRow = reqFirstRow;
- }
-
- public void setReqRows(int reqRows) {
- this.reqRows = reqRows;
- }
-
- public void run() {
- client.console.log("Getting " + reqRows + " rows from " + reqFirstRow);
- client.updateVariable(id, "firstvisible", firstRowInViewPort, false);
- client.updateVariable(id, "reqfirstrow", reqFirstRow, false);
- client.updateVariable(id, "reqrows", reqRows, true);
- }
-
- public int getReqFirstRow() {
- return reqFirstRow;
- }
-
- public int getReqRows() {
- return reqRows;
- }
-
- }
-}
+++ /dev/null
-package com.itmill.toolkit.terminal.gwt.client.ui.scrolltable;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.Vector;
-
-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.Event;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.ui.ClickListener;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.ScrollListener;
-import com.google.gwt.user.client.ui.ScrollPanel;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.itmill.toolkit.terminal.gwt.client.Client;
-import com.itmill.toolkit.terminal.gwt.client.Paintable;
-import com.itmill.toolkit.terminal.gwt.client.UIDL;
-import com.itmill.toolkit.terminal.gwt.client.ui.ITable;
-import com.itmill.toolkit.terminal.gwt.client.ui.scrolltable.IScrollTable.IScrollTableBody.IScrollTableRow;
-
-public class IScrollTable extends Composite implements Paintable, ITable, ScrollListener {
-
- /**
- * multiple of pagelenght which component will
- * cache when requesting more rows
- */
- private static final double CACHE_RATE = 2;
- /**
- * fraction of pageLenght which can be scrolled without
- * making new request
- */
- private static final double CACHE_REACT_RATE = 1;
-
- private int firstRowInViewPort = 0;
- private int pageLength = 15;
-
- private boolean rowHeaders = false;
-
- private String[] columnOrder;
-
- private Client client;
- private String id;
-
- private boolean immediate;
-
- private int selectMode = ITable.SELECT_MODE_NONE;
-
- private Vector selectedRowKeys = new Vector();
-
- private boolean initializedAndAttached = false;
-
- private TableHead tHead = new TableHead();
-
- private ScrollPanel bodyContainer = new ScrollPanel();
-
- private ScrollPanel headerContainer = new ScrollPanel();
-
- private boolean colWidthsInitialized = false;
- private int totalRows;
- private HashMap columnWidths = new HashMap();
-
- private RowRequestHandler rowRequestHandler;
- private IScrollTableBody tBody;
- private int width = -1;
- private int height = -1;
- private int firstvisible = 0;
- private boolean sortAscending;
- private String sortColumn;
- private boolean columnReordering;
-
- public IScrollTable() {
- headerContainer.setStyleName("iscrolltable-header");
- headerContainer.add(tHead);
- DOM.setStyleAttribute(headerContainer.getElement(), "overflow", "hidden");
-
- bodyContainer.addScrollListener(this);
-
- VerticalPanel panel = new VerticalPanel();
- panel.add(headerContainer);
- panel.add(bodyContainer);
-
- rowRequestHandler = new RowRequestHandler();
-
- initWidget(panel);
- }
-
- public void updateFromUIDL(UIDL uidl, Client client) {
- if (client.updateComponent(this, uidl, true))
- return;
-
- this.client = client;
- this.id = uidl.getStringAttribute("id");
- this.immediate = uidl.getBooleanAttribute("immediate");
- this.totalRows = uidl.getIntAttribute("totalrows");
- this.pageLength = uidl.getIntAttribute("pagelength");
- if(pageLength == 0)
- pageLength = totalRows;
- this.firstvisible = uidl.hasVariable("firstvisible") ? uidl.getIntVariable("firstvisible") : 0;
- if(uidl.hasAttribute("rowheaders"))
- rowHeaders = true;
- if(uidl.hasAttribute("width"))
- width = uidl.getIntAttribute("width");
- if(uidl.hasAttribute("height"))
- width = uidl.getIntAttribute("height");
-
- if(uidl.hasVariable("sortascending")) {
- this.sortAscending = uidl.getBooleanVariable("sortascending");
- this.sortColumn = uidl.getStringVariable("sortcolumn");
- }
-
- Set selectedKeys = uidl.getStringArrayVariableAsSet("selected");
- selectedRowKeys.clear();
- for(Iterator it = selectedKeys.iterator();it.hasNext();)
- selectedRowKeys.add((String) it.next());
-
-
- if(uidl.hasAttribute("selectmode")) {
- if(uidl.getStringAttribute("selectmode").equals("multi"))
- selectMode = ITable.SELECT_MODE_MULTI;
- else
- selectMode = ITable.SELECT_MODE_SINGLE;
- }
-
- if(uidl.hasVariable("columnorder")) {
- this.columnReordering = true;
- this.columnOrder = uidl.getStringArrayVariable("columnorder");
- }
-
- UIDL columnInfo = null;
- UIDL rowData = null;
- for(Iterator it = uidl.getChildIterator(); it.hasNext();) {
- UIDL c = (UIDL) it.next();
- if(c.getTag().equals("cols"))
- columnInfo = c;
- else if(c.getTag().equals("rows"))
- rowData = c;
- else if(c.getTag().equals("actions"))
- updateActionMap(c);
- else if(c.getTag().equals("visiblecolumns"))
- ;
- }
- updateHeader(columnInfo);
-
- if(initializedAndAttached) {
- updateBody(rowData, uidl.getIntAttribute("firstrow"),uidl.getIntAttribute("rows"));
- } else {
- getTBody().renderInitialRows(rowData,
- uidl.getIntAttribute("firstrow"),
- uidl.getIntAttribute("rows"),
- totalRows);
- bodyContainer.add(tBody);
- initializedAndAttached = true;
- }
- }
-
- private IScrollTableBody getTBody() {
- if(tBody == null || totalRows != tBody.getTotalRows()) {
- if(tBody != null)
- tBody.removeFromParent();
- tBody = new IScrollTableBody();
- }
- return tBody;
- }
-
- private void updateActionMap(UIDL c) {
- // TODO Auto-generated method stub
-
- }
-
- private void updateHeader(UIDL uidl) {
- if(uidl == null)
- return;
- int columnCount = uidl.getChidlCount();
- int colIndex = 0;
- if(rowHeaders) {
- columnCount++;
- HeaderCell c = (HeaderCell) tHead.getHeaderCell(0);
- if(c == null) {
- tHead.setHeaderCell(0, new HeaderCell("0", ""));
- }
- colIndex++;
- }
-
- for(Iterator it = uidl.getChildIterator();it.hasNext();) {
- UIDL col = (UIDL) it.next();
- String cid = col.getStringAttribute("cid");
- HeaderCell c = (HeaderCell) tHead.getHeaderCell(colIndex);
- if(c != null && c.getColKey().equals(cid)) {
- c.setText(col.getStringAttribute("caption"));
- } else {
- c = new HeaderCell(cid, col.getStringAttribute("caption"));
- tHead.setHeaderCell(colIndex, c);
- }
- if(col.hasAttribute("sortable")) {
- c.setSortable(true);
- if(cid.equals(sortColumn))
- c.setSorted(true);
- else
- c.setSorted(false);
- }
- colIndex++;
- }
- }
-
- /**
- * @param uidl which contains row data
- * @param firstRow first row in data set
- * @param reqRows amount of rows in data set
- */
- private void updateBody(UIDL uidl, int firstRow, int reqRows) {
- if(uidl == null || reqRows < 1)
- return;
-
- tBody.renderRows(uidl, firstRow, reqRows);
-
- int optimalFirstRow = (int) (firstRowInViewPort - pageLength*CACHE_RATE);
- while(tBody.getFirstRendered() < optimalFirstRow) {
-// client.console.log("removing row from start");
- tBody.unlinkRow(true);
- }
- int optimalLastRow = (int) (firstRowInViewPort + pageLength + pageLength*CACHE_RATE);
- while(tBody.getLastRendered() > optimalLastRow) {
-// client.console.log("removing row from the end");
- tBody.unlinkRow(false);
- }
-
- }
-
-
- private int getColIndexByKey(String colKey) {
- // return 0 if asked for rowHeaders
- if("0".equals(colKey))
- return 0;
- int index = -1;
- for (int i = 0; i < columnOrder.length; i++) {
- if(columnOrder[i].equals(colKey)) {
- index = i;
- break;
- }
- }
- if(rowHeaders)
- index++;
- return index;
- }
-
- private String getColKeyByIndex(int index) {
- return tHead.getHeaderCell(index).getColKey();
- }
-
- private void setColWidth(int colIndex, int w) {
- HeaderCell cell = tHead.getHeaderCell(colIndex);
- cell.setWidth(w);
- tBody.setColWidth(colIndex, w);
- String cid = cell.getColKey();;
- columnWidths.put(cid,new Integer(w));
- }
-
- private int getColWidth(String colKey) {
- return ( (Integer) this.columnWidths.get(colKey)).intValue();
- }
-
- private IScrollTableRow getRenderedRowByKey(String key) {
- Iterator it = tBody.iterator();
- IScrollTableRow r = null;
- while(it.hasNext()) {
- r = (IScrollTableRow) it.next();
- if(r.getKey().equals(key))
- return r;
- }
- return null;
- }
-
- private int getRenderedRowCount() {
- return tBody.getLastRendered()-tBody.getFirstRendered();
- }
-
- private void reOrderColumn(String columnKey, int newIndex) {
-
- int oldIndex = getColIndexByKey(columnKey);
-
- // Change header order
- tHead.moveCell(oldIndex, newIndex);
-
- // Change body order
- tBody.moveCol(oldIndex, newIndex);
-
- // build new columnOrder and update it to server
-
- String[] newOrder = new String[columnOrder.length];
-
- Iterator hCells = tHead.iterator();
-
- if(rowHeaders)
- hCells.next();
- int index = 0;
- while(hCells.hasNext()) {
- newOrder[index++] = ((HeaderCell) hCells.next()).getColKey();
- }
- columnOrder = newOrder;
- client.updateVariable(id, "columnorder", newOrder, false);
-
- }
-
- /**
- * This method has logick which rows needs to be requested from
- * server when user scrolls
- *
- */
- public void onScroll(Widget widget, int scrollLeft, int scrollTop) {
- if(!initializedAndAttached)
- return;
-
- rowRequestHandler.cancel();
-
- // fix headers horizontal scrolling
- headerContainer.setHorizontalScrollPosition(scrollLeft);
-
- firstRowInViewPort = (int) Math.ceil( scrollTop / (double) tBody.getRowHeight() );
- client.console.log("At scrolltop: " + scrollTop + " At row " + firstRowInViewPort);
-
- int postLimit = (int) (firstRowInViewPort + pageLength + pageLength*CACHE_REACT_RATE);
- if(postLimit > totalRows -1 )
- postLimit = totalRows - 1;
- int preLimit = (int) (firstRowInViewPort - pageLength*CACHE_REACT_RATE);
- if(preLimit < 0)
- preLimit = 0;
- int lastRendered = tBody.getLastRendered();
- int firstRendered = tBody.getFirstRendered();
- if( postLimit <= lastRendered && preLimit >= firstRendered ) {
- client.updateVariable(this.id, "firstvisible", firstRowInViewPort, false);
- return; // scrolled withing "non-react area"
- }
-
- if(firstRowInViewPort - pageLength*CACHE_RATE > lastRendered ||
- firstRowInViewPort + pageLength + pageLength*CACHE_RATE < firstRendered ) {
- // need a totally new set
- client.console.log("Table: need a totally new set");
- rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE));
- rowRequestHandler.setReqRows((int) (2*CACHE_RATE*pageLength + pageLength));
- rowRequestHandler.deferRowFetch();
- return;
- }
- if(preLimit < firstRendered ) {
- // need some rows to the beginning of the rendered area
- client.console.log("Table: need some rows to the beginning of the rendered area");
- rowRequestHandler.setReqFirstRow((int) (firstRowInViewPort - pageLength*CACHE_RATE));
- rowRequestHandler.setReqRows(firstRendered - rowRequestHandler.getReqFirstRow());
- rowRequestHandler.deferRowFetch();
-
- return;
- }
- if(postLimit > lastRendered) {
- // need some rows to the end of the rendered area
- client.console.log("need some rows to the end of the rendered area");
- rowRequestHandler.setReqFirstRow(lastRendered + 1);
- rowRequestHandler.setReqRows((int) ((firstRowInViewPort + pageLength + pageLength*CACHE_RATE) - lastRendered));
- rowRequestHandler.deferRowFetch();
- }
-
- }
-
-
-
- protected void onAttach() {
-
- super.onAttach();
- int bodyWidth = tBody.getOffsetWidth();
-
- // sync column widths
- initColumnWidths();
-
- if(height < 0) {
- bodyContainer.setHeight((tBody.getRowHeight()*pageLength) + "px");
- } else {
- bodyContainer.setHeight(height + "px");
- }
-
- if(width < 0) {
- bodyWidth = tBody.getOffsetWidth();
- bodyContainer.setWidth((tBody.getOffsetWidth() + getScrollBarWidth() ) + "px");
- headerContainer.setWidth((tBody.getOffsetWidth()) + "px");
- } else {
- bodyContainer.setWidth(width + "px");
- headerContainer.setWidth(width + "px");
- }
-
- tHead.disableBrowserIntelligence();
-
- if(firstvisible > 0)
- bodyContainer.setScrollPosition(firstvisible*tBody.getRowHeight());
-
- DeferredCommand.add(new Command() {
- public void execute() {
- if(totalRows - 1 > tBody.getLastRendered()) {
- // fetch cache rows
- rowRequestHandler.setReqFirstRow(tBody.getLastRendered()+1);
- rowRequestHandler.setReqRows((int) (pageLength*CACHE_RATE));
- rowRequestHandler.deferRowFetch();
- }
- }
- });
-
-
- }
-
- /**
- * Run when receices its initial content. Syncs headers and bodys
- * "natural widths and saves the values.
- */
- private void initColumnWidths() {
- Iterator headCells = tHead.iterator();
- int i = 0;
- while(headCells.hasNext()) {
- Element hCell = ((HeaderCell) headCells.next()).getElement();
- int hw = DOM.getIntAttribute(hCell, "offsetWidth");
- int cw = tBody.getColWidth(i);
- int w = (hw > cw ? hw : cw) + IScrollTableBody.CELL_EXTRA_WIDTH;
- setColWidth(i , w);
- i++;
- }
- }
-
- private int getScrollBarWidth() {
- // TODO Auto-generated method stub
- return 30;
- }
-
- private class RowRequestHandler extends Timer {
-
- private int reqFirstRow = 0;
- private int reqRows = 0;
-
- public void deferRowFetch() {
- if(reqRows > 0 && reqFirstRow < totalRows)
- schedule(250);
- }
-
- public void setReqFirstRow(int reqFirstRow) {
- if(reqFirstRow < 0)
- reqFirstRow = 0;
- else if(reqFirstRow >= totalRows)
- reqFirstRow = totalRows - 1;
- this.reqFirstRow = reqFirstRow;
- }
-
- public void setReqRows(int reqRows) {
- this.reqRows = reqRows;
- }
-
- public void run() {
- client.console.log("Getting " + reqRows + " rows from " + reqFirstRow);
- client.updateVariable(id, "firstvisible", firstRowInViewPort, false);
- client.updateVariable(id, "reqfirstrow", reqFirstRow, false);
- client.updateVariable(id, "reqrows", reqRows, true);
- }
-
- public int getReqFirstRow() {
- return reqFirstRow;
- }
-
- public int getReqRows() {
- return reqRows;
- }
-
- }
-
- public class HeaderCell extends Widget {
-
- private static final int DRAG_WIDGET_WIDTH = 2;
-
- private static final int MINIMUM_COL_WIDTH = 20;
-
- Element td = DOM.createTD();
-
- Element captionContainer = DOM.createDiv();
-
- Element colResizeWidget = DOM.createDiv();
-
- Element floatingCopyOfHeaderCell;
-
- private boolean sortable = false;
- private String cid;
- private boolean dragging;
-
- private int dragStartX;
- private int colIndex;
- private int originalWidth;
-
- private boolean isResizing;
-
- private int headerX;
-
- private boolean moved;
-
- private int closestSlot;
-
-
- private HeaderCell(){};
-
- public void setSortable(boolean b) {
- if(b == sortable)
- return;
- sortable = b;
- }
-
- public HeaderCell(String colId, String headerText) {
- this.cid = colId;
-
- DOM.setStyleAttribute(colResizeWidget,"display", "block");
- DOM.setStyleAttribute(colResizeWidget, "width", DRAG_WIDGET_WIDTH +"px");
- DOM.setStyleAttribute(colResizeWidget,"height", "20px");
- DOM.setStyleAttribute(colResizeWidget,"cssFloat", "right");
- DOM.setStyleAttribute(colResizeWidget, "styleFloat", "right");
- DOM.setStyleAttribute(colResizeWidget,"background", "brown");
- DOM.setStyleAttribute(colResizeWidget,"cursor", "e-resize");
- DOM.sinkEvents(colResizeWidget,Event.MOUSEEVENTS);
-
- setText(headerText);
-
- DOM.appendChild(td, colResizeWidget);
-
-
- DOM.setStyleAttribute(captionContainer, "cssFloat", "right");
- DOM.setStyleAttribute(captionContainer, "styleFloat", "right");
- DOM.setStyleAttribute(captionContainer, "overflow", "hidden");
- DOM.setStyleAttribute(captionContainer, "white-space", "nowrap");
- DOM.setStyleAttribute(captionContainer, "display", "inline");
- DOM.sinkEvents(captionContainer, Event.MOUSEEVENTS);
-
- DOM.appendChild(td, captionContainer);
-
- setElement(td);
- }
-
- public void setWidth(int w) {
- DOM.setStyleAttribute(captionContainer, "width", (w - DRAG_WIDGET_WIDTH - 4) + "px");
- setWidth(w + "px");
- }
-
- public void setText(String headerText) {
- DOM.setInnerHTML(captionContainer, headerText);
- }
- public String getColKey() {
- return cid;
- }
-
- private void setSorted(boolean sorted) {
- if(sorted) {
- if(sortAscending)
- this.setStyleName("headerCellAsc");
- else
- this.setStyleName("headerCellDesc");
- } else {
- this.setStyleName("headerCell");
- }
- }
-
- /**
- * Handle column reordering.
- */
- public void onBrowserEvent(Event event) {
-
- Element target = DOM.eventGetTarget(event);
-
- if(isResizing || DOM.compare(target, colResizeWidget)) {
- onResizeEvent(event);
- } else {
- handleCaptionEvent(event);
- }
-
-
- super.onBrowserEvent(event);
- }
-
-
- private void createFloatingCopy() {
- floatingCopyOfHeaderCell = DOM.createDiv();
- DOM.setInnerHTML(floatingCopyOfHeaderCell, DOM.getInnerHTML(td));
- floatingCopyOfHeaderCell = DOM.getChild(floatingCopyOfHeaderCell, 1);
- // TODO isolate non-standard css attribute (filter)
- // TODO move styles to css file
- DOM.setStyleAttribute(floatingCopyOfHeaderCell, "position", "absolute");
- DOM.setStyleAttribute(floatingCopyOfHeaderCell, "background", "#000000");
- DOM.setStyleAttribute(floatingCopyOfHeaderCell, "color", "#ffffff");
- DOM.setStyleAttribute(floatingCopyOfHeaderCell, "opacity", "0.5");
- DOM.setStyleAttribute(floatingCopyOfHeaderCell, "filter", "alpha(opacity=100)");
- updateFloatingCopysPosition(DOM.getAbsoluteLeft(td), DOM.getAbsoluteTop(td));
- DOM.appendChild(IScrollTable.this.getElement(), floatingCopyOfHeaderCell);
- }
-
- private void updateFloatingCopysPosition(int x, int y) {
- x -= DOM.getIntAttribute(floatingCopyOfHeaderCell, "offsetWidth")/2;
- DOM.setStyleAttribute(floatingCopyOfHeaderCell, "left", x + "px");
- if(y > 0)
- DOM.setStyleAttribute(floatingCopyOfHeaderCell, "top", (y + 7) + "px");
- }
-
- private void hideFloatingCopy() {
- DOM.removeChild(IScrollTable.this.getElement(), floatingCopyOfHeaderCell);
- floatingCopyOfHeaderCell = null;
- }
-
- private void handleCaptionEvent(Event event) {
- switch (DOM.eventGetType(event)) {
- case Event.ONMOUSEDOWN:
- dragging = true;
- moved = false;
- colIndex = getColIndexByKey(cid);
- DOM.setCapture(getElement());
-
- this.headerX = tHead.getAbsoluteLeft();
-
- DOM.eventPreventDefault(event);
- break;
- case Event.ONMOUSEUP:
- dragging = false;
- DOM.releaseCapture(getElement());
-
- if(!moved) {
- // mouse event was a click to header -> sort column
- if(sortable) {
- if(sortColumn.equals(cid)) {
- // just toggle order
- client.updateVariable(id, "sortascending", !sortAscending, false);
- } else {
- // set table scrolled by this column
- client.updateVariable(id, "sortcolumn", cid, false);
- }
- // get also cache columns at the same request
- bodyContainer.setScrollPosition(0);
- firstvisible = 0;
- rowRequestHandler.setReqFirstRow(0);
- rowRequestHandler.setReqRows((int) (2*pageLength*CACHE_RATE + pageLength));
- rowRequestHandler.deferRowFetch();
- }
- break;
- }
- System.out.println("Stopped column reordering");
- hideFloatingCopy();
- tHead.removeSlotFocus();
- if(closestSlot != colIndex && closestSlot != (colIndex + 1) ) {
- if(closestSlot > colIndex)
- reOrderColumn(cid, closestSlot - 1);
- else
- reOrderColumn(cid, closestSlot);
- }
- break;
- case Event.ONMOUSEMOVE:
- if (dragging) {
- System.out.print("Dragging column, optimal index...");
- if(!moved) {
- createFloatingCopy();
- moved = true;
- }
- int x = DOM.eventGetClientX(event);
- int slotX = headerX;
- closestSlot = colIndex;
- int closestDistance = -1;
- int start = 0;
- if(rowHeaders) {
- start++;
- }
- for(int i = start; i <= columnWidths.size() ; i++ ) {
- if(i > 0) {
- String colKey = getColKeyByIndex(i-1);
- slotX += getColWidth(colKey);
- }
- int dist = Math.abs(x - slotX);
- if(closestDistance == -1 || dist < closestDistance) {
- closestDistance = dist;
- closestSlot = i;
- }
- }
- tHead.focusSlot(closestSlot);
-
- updateFloatingCopysPosition(x, -1);
- System.out.println(closestSlot);
- }
- break;
- default:
- break;
- }
- }
-
- private void onResizeEvent(Event event) {
- switch (DOM.eventGetType(event)) {
- case Event.ONMOUSEDOWN:
- isResizing = true;
- DOM.setCapture(getElement());
- dragStartX = DOM.eventGetClientX(event);
- colIndex = getColIndexByKey(cid);
- originalWidth = IScrollTable.this.tBody.getColWidth(colIndex);
- DOM.eventPreventDefault(event);
- break;
- case Event.ONMOUSEUP:
- isResizing = false;
- DOM.releaseCapture(getElement());
- break;
- case Event.ONMOUSEMOVE:
- if (isResizing) {
- int deltaX = DOM.eventGetClientX(event) - dragStartX ;
- if(deltaX == 0)
- return;
-
- int newWidth = originalWidth + deltaX;
- if(newWidth < MINIMUM_COL_WIDTH)
- newWidth = MINIMUM_COL_WIDTH;
- setColWidth(colIndex, newWidth);
- }
- break;
- default:
- break;
- }
- }
-
-
- }
-
- public class TableHead extends Panel {
-
- private static final int WRAPPER_WIDTH = 9000;
-
- Vector cells = new Vector();
-
- Element div = DOM.createDiv();
- Element table = DOM.createTable();
- Element tBody = DOM.createTBody();
- Element tr = DOM.createTR();
-
- private int focusedSlot = -1;
-
- public TableHead() {
- DOM.appendChild(table, tBody);
- DOM.appendChild(tBody, tr);
- DOM.appendChild(div, table);
- setElement(div);
- }
-
- public void disableBrowserIntelligence() {
- DOM.setStyleAttribute(div, "width", WRAPPER_WIDTH +"px");
- }
-
- public void setHeaderCell(int index, HeaderCell cell) {
- if(index < cells.size()) {
- // replace
- // TODO remove old correctly
- // insert to right slot
- } else if( index == cells.size()) {
- //append
- adopt(cell, tr);
- cells.add(cell);
- } else {
- throw new RuntimeException("Header cells must be appended in order");
- }
- }
-
- public HeaderCell getHeaderCell(int index) {
- if(index < cells.size())
- return (HeaderCell) cells.get(index);
- else
- return null;
- }
-
- public void moveCell(int oldIndex, int newIndex) {
- HeaderCell hCell = getHeaderCell(oldIndex);
- Element cell = hCell.getElement();
-
- cells.remove(oldIndex);
- DOM.removeChild(tr, cell);
-
- DOM.insertChild(tr, cell, newIndex);
- cells.insertElementAt(hCell, newIndex);
- }
-
- public Iterator iterator() {
- return cells.iterator();
- }
-
- public boolean remove(Widget w) {
- // TODO Auto-generated method stub
- return false;
- }
-
- private void focusSlot(int index) {
- removeSlotFocus();
- if(index > 0)
- DOM.setStyleAttribute(DOM.getChild(tr, index - 1), "borderRight", "2px solid black");
- else
- DOM.setStyleAttribute(DOM.getChild(tr, index), "borderLeft", "2px solid black");
- focusedSlot = index;
- }
-
- private void removeSlotFocus() {
- if(focusedSlot < 0)
- return;
- if(focusedSlot == 0)
- DOM.setStyleAttribute(DOM.getChild(tr, focusedSlot), "borderLeft", "none");
- else if( focusedSlot > 0)
- DOM.setStyleAttribute(DOM.getChild(tr, focusedSlot - 1), "borderRight", "none");
- focusedSlot = -1;
- }
-
-
- }
-
- /**
- * This Panel can only contain IScrollTAbleRow type of
- * widgets. This "simulates" very large table, keeping
- * spacers which take room of unrendered rows.
- *
- * @author mattitahvonen
- *
- */
- public class IScrollTableBody extends Panel {
-
- public static final int CELL_EXTRA_WIDTH = 20;
-
- public static final int DEFAULT_ROW_HEIGHT = 25;
-
- private int rowHeight = -1;
-
- private List renderedRows = new Vector();
-
- private boolean initDone = false;
-
- private int totalRows;
-
- Element preSpacer = DOM.createDiv();
- Element postSpacer = DOM.createDiv();
-
- Element container = DOM.createDiv();
-
- Element tBody = DOM.createTBody();
- Element table = DOM.createTable();
-
- private int firstRendered;
-
- private int lastRendered;
-
- IScrollTableBody() {
-
- constructDOM();
-
- setElement(container);
-
- }
-
- private void constructDOM() {
- DOM.setAttribute(table, "className", "iscrolltable-table");
- DOM.setAttribute(preSpacer, "className", "iscrolltable-rowspacer");
- DOM.setAttribute(postSpacer, "className", "iscrolltable-rowspacer");
-
- DOM.appendChild(table, tBody);
- DOM.appendChild(container, preSpacer);
- DOM.appendChild(container, table);
- DOM.appendChild(container, postSpacer);
-
- }
-
-
- public void renderInitialRows(UIDL rowData, int firstIndex, int rows, int totalRows) {
- this.totalRows = totalRows;
- this.firstRendered = firstIndex;
- this.lastRendered = firstIndex + rows - 1 ;
- Iterator it = rowData.getChildIterator();
- while(it.hasNext()) {
- IScrollTableRow row = new IScrollTableRow((UIDL) it.next());
- addRow(row);
- }
- if(isAttached())
- fixSpacers();
- }
-
- public void renderRows(UIDL rowData, int firstIndex, int rows) {
- Iterator it = rowData.getChildIterator();
- if(firstIndex == lastRendered + 1) {
- while(it.hasNext()) {
- IScrollTableRow row = createRow((UIDL) it.next());
- addRow(row);
- lastRendered++;
- }
- fixSpacers();
- } else if(firstIndex + rows == firstRendered) {
- IScrollTableRow[] rowArray = new IScrollTableRow[rows];
- int i = rows;
- while(it.hasNext()) {
- i--;
- rowArray[i] = createRow((UIDL) it.next());
- }
- for(i = 0 ; i < rows; i++) {
- addRowBeforeFirstRendered(rowArray[i]);
- firstRendered--;
- }
-// } else if (firstIndex > lastRendered || firstIndex + rows < firstRendered) {
- } else if (true) {
- // complitely new set of rows
- // create one row before truncating row
- IScrollTableRow row = createRow((UIDL) it.next());
- while(lastRendered + 1 > firstRendered)
- unlinkRow(false);
- firstRendered = firstIndex;
- lastRendered = firstIndex - 1 ;
- fixSpacers();
- addRow(row);
- lastRendered++;
- while(it.hasNext()) {
- addRow(createRow((UIDL) it.next()));
- lastRendered++;
- }
- fixSpacers();
- } else {
- // sorted or column reordering changed
- client.console.log("Bad update" + firstIndex + "/"+ rows);
- }
- }
-
- /**
- * This mehtod is used to instantiate new rows for this table.
- * It automatically sets correct widths to rows cells and assigns
- * correct client reference for child widgets.
- *
- * This method can be called only after table has been initialized
- *
- * @param uidl
- * @param client2
- */
- private IScrollTableRow createRow(UIDL uidl) {
- IScrollTableRow row = new IScrollTableRow(uidl);
- int cells = DOM.getChildCount(row.getElement());
- for(int i = 0; i < cells; i++) {
- Element cell = DOM.getChild(row.getElement(), i);
- int w = getColWidth(i);
- DOM.setStyleAttribute(cell, "width", w + "px");
- DOM.setStyleAttribute(DOM.getFirstChild(cell), "width", w + "px");
- }
- return row;
- }
-
- private void addRowBeforeFirstRendered(IScrollTableRow row) {
- DOM.insertChild(tBody, row.getElement(), 0);
- adopt(row, null);
- renderedRows.add(0, row);
- }
-
- private void addRow(IScrollTableRow row) {
- DOM.appendChild(tBody, row.getElement());
- adopt(row, null);
- renderedRows.add(row);
- }
-
- public Iterator iterator() {
- return renderedRows.iterator();
- }
-
- public void unlinkRow(boolean fromBeginning) {
- if(lastRendered - firstRendered < 0)
- return;
- int index;
- if(fromBeginning) {
- index = 0;
- firstRendered++;
- } else {
- index = renderedRows.size() - 1;
- lastRendered--;
- }
- IScrollTableRow toBeRemoved = (IScrollTableRow) renderedRows.get(index);
- this.disown(toBeRemoved);
- renderedRows.remove(index);
- fixSpacers();
- }
-
- public boolean remove(Widget w) {
- throw new UnsupportedOperationException();
- }
-
- protected void onAttach() {
- super.onAttach();
- fixSpacers();
- // fix container blocks height to avoid "bouncing" when scrolling
- DOM.setStyleAttribute(container, "height", totalRows*getRowHeight() + "px");
- }
-
- private void fixSpacers() {
- DOM.setStyleAttribute(preSpacer, "height", getRowHeight()*firstRendered + "px");
- DOM.setStyleAttribute(postSpacer, "height", getRowHeight()*(totalRows - 1 - lastRendered) + "px");
- }
-
- public int getTotalRows() {
- return totalRows;
- }
-
- public int getRowHeight() {
- if(initDone)
- return rowHeight;
- else {
- if(DOM.getChildCount(tBody) > 0) {
- rowHeight = DOM.getIntAttribute(tBody, "offsetHeight")/DOM.getChildCount(tBody);
- } else {
- return DEFAULT_ROW_HEIGHT;
- }
- initDone = true;
- return rowHeight;
- }
- }
-
- public int getColWidth(int i) {
- Element e = DOM.getChild(DOM.getChild(tBody, 0), i);
- return DOM.getIntAttribute(e, "offsetWidth");
- }
-
- public void setColWidth(int colIndex, int w) {
- int rows = DOM.getChildCount(tBody);
- for(int i = 0; i < rows; i++) {
- Element cell = DOM.getChild(DOM.getChild(tBody, i), colIndex);
- DOM.setStyleAttribute(cell, "width", w + "px");
- DOM.setStyleAttribute(DOM.getFirstChild(cell), "width", w + "px");
- }
- }
-
- public int getLastRendered() {
- return lastRendered;
- }
-
- public int getFirstRendered() {
- return firstRendered;
- }
-
- public void moveCol(int oldIndex, int newIndex) {
-
- // loop all rows and move given index to its new place
- Iterator rows = iterator();
- while(rows.hasNext()) {
- IScrollTableRow row = (IScrollTableRow) rows.next();
-
- Element td = DOM.getChild(row.getElement(), oldIndex);
- DOM.removeChild(row.getElement(), td);
-
- DOM.insertChild(row.getElement(), td, newIndex);
-
- }
-
- }
-
- public class IScrollTableRow extends Panel {
-
- Vector childWidgets = new Vector();
- private boolean selected = false;
- private int rowKey;
-
- private IScrollTableRow(int rowKey) {
- this.rowKey = rowKey;
- this.selected = selected;
- setElement(DOM.createElement("tr"));
- DOM.sinkEvents(getElement(), Event.BUTTON_RIGHT | Event.ONCLICK);
- setStyleName("iscrolltable-row");
- }
-
- public String getKey() {
- return String.valueOf(rowKey);
- }
-
- public IScrollTableRow(UIDL uidl) {
- this(uidl.getIntAttribute("key"));
- if(uidl.hasAttribute("caption"))
- addCell(uidl.getStringAttribute("caption"));
- Iterator cells = uidl.getChildIterator();
- while(cells.hasNext()) {
- Object cell = cells.next();
- if (cell instanceof String) {
- addCell(cell.toString());
- } else {
- Widget cellContent = client.getWidget((UIDL) cell);
- (( Paintable) cellContent).updateFromUIDL((UIDL) cell, client);
- }
- }
- if(uidl.hasAttribute("selected"))
- toggleSelection();
- }
-
- public void addCell(String text) {
- // String only content is optimized by not using Label widget
- Element td = DOM.createTD();
- Element container = DOM.createDiv();
- DOM.setAttribute(container, "className", "iscrolltable-cellContent");
- DOM.setInnerHTML(container, text);
- DOM.appendChild(td, container);
- DOM.appendChild(getElement(), td);
- }
-
- public void addCell(Widget w) {
- Element td = DOM.createTD();
- Element container = DOM.createDiv();
- DOM.setAttribute(container, "className", "iscrolltable-cellContent");
- DOM.appendChild(td, container);
- DOM.appendChild(getElement(), td);
- adopt(w, container);
- childWidgets.add(w);
- }
-
- public Iterator iterator() {
- return childWidgets.iterator();
- }
-
- public boolean remove(Widget w) {
- // TODO Auto-generated method stub
- return false;
- }
-
- public void onBrowserEvent(Event event) {
- switch (DOM.eventGetType(event)) {
- case Event.BUTTON_RIGHT:
- // TODO
- System.out.println("Context menu");
- break;
-
- case Event.ONCLICK:
- System.out.println("Row click");
- if(selectMode > ITable.SELECT_MODE_NONE) {
- toggleSelection();
- client.updateVariable(id, "selected", selectedRowKeys.toArray(), immediate);
- }
- break;
-
- default:
- break;
- }
- super.onBrowserEvent(event);
- }
-
- public boolean isSelected() {
- return selected;
- }
-
- private void toggleSelection() {
- selected = !selected;
- if(selected) {
- if(selectMode == ITable.SELECT_MODE_SINGLE)
- IScrollTable.this.deselectAll();
- selectedRowKeys.add(String.valueOf(rowKey));
-
- setStyleName("iscrolltable-selRow");
- } else {
- selectedRowKeys.remove(String.valueOf(rowKey));
- setStyleName("iscrolltable-row");
- }
- }
-
- }
-
- }
-
- public void deselectAll() {
- Object[] keys = selectedRowKeys.toArray();
- for (int i = 0; i < keys.length; i++) {
- IScrollTableRow row = getRenderedRowByKey((String) keys[i]);
- if(row != null && row.isSelected())
- row.toggleSelection();
- }
- // still ensure all selects are removed from (not necessary rendered)
- selectedRowKeys.clear();
-
- }
-}