this.client = client;
paintableId = uidl.getStringAttribute("id");
immediate = uidl.getBooleanAttribute("immediate");
- final int newTotalRows = uidl.getIntAttribute("totalrows");
- if (newTotalRows != totalRows) {
- if (scrollBody != null) {
- if (totalRows == 0) {
- tHead.clear();
- tFoot.clear();
- }
- initializedAndAttached = false;
- initialContentReceived = false;
- isNewBody = true;
- }
- totalRows = newTotalRows;
- }
- dragmode = uidl.hasAttribute("dragmode") ? uidl
- .getIntAttribute("dragmode") : 0;
- if (BrowserInfo.get().isIE()) {
- if (dragmode > 0) {
- getElement().setPropertyJSO("onselectstart",
- getPreventTextSelectionIEHack());
- } else {
- getElement().setPropertyJSO("onselectstart", null);
- }
- }
+ updateTotalRows(uidl);
- tabIndex = uidl.hasAttribute("tabindex") ? uidl
- .getIntAttribute("tabindex") : 0;
+ updateDragMode(uidl);
- if (!BrowserInfo.get().isTouchDevice()) {
- multiselectmode = uidl.hasAttribute("multiselectmode") ? uidl
- .getIntAttribute("multiselectmode")
- : MULTISELECT_MODE_DEFAULT;
- }
+ updateSelectionProperties(uidl);
if (uidl.hasAttribute("alb")) {
bodyActionKeys = uidl.getStringArrayAttribute("alb");
bodyActionKeys = null;
}
- setCacheRate(uidl.hasAttribute("cr") ? uidl.getDoubleAttribute("cr")
- : CACHE_RATE_DEFAULT);
+ setCacheRateFromUIDL(uidl);
recalcWidths = uidl.hasAttribute("recalcWidths");
if (recalcWidths) {
tFoot.clear();
}
- int oldPageLength = pageLength;
- if (uidl.hasAttribute("pagelength")) {
- pageLength = uidl.getIntAttribute("pagelength");
- } else {
- // pagelenght is "0" meaning scrolling is turned off
- pageLength = totalRows;
- }
-
- if (oldPageLength != pageLength && initializedAndAttached) {
- // page length changed, need to update size
- sizeInit();
- }
+ updatePageLength(uidl);
- firstvisible = uidl.hasVariable("firstvisible") ? uidl
- .getIntVariable("firstvisible") : 0;
- if (firstvisible != lastRequestedFirstvisible && scrollBody != null) {
- // received 'surprising' firstvisible from server: scroll there
- firstRowInViewPort = firstvisible;
- scrollBodyPanel.setScrollPosition((int) (firstvisible * scrollBody
- .getRowHeight()));
- }
+ updateFirstVisibleAndScrollIfNeeded(uidl);
showRowHeaders = uidl.getBooleanAttribute("rowheaders");
showColHeaders = uidl.getBooleanAttribute("colheaders");
- nullSelectionAllowed = uidl.hasAttribute("nsa") ? uidl
- .getBooleanAttribute("nsa") : true;
+ updateSortingProperties(uidl);
- String oldSortColumn = sortColumn;
- if (uidl.hasVariable("sortascending")) {
- sortAscending = uidl.getBooleanVariable("sortascending");
- sortColumn = uidl.getStringVariable("sortcolumn");
- }
+ boolean keyboardSelectionOverRowFetchInProgress = selectSelectedRows(uidl);
- boolean keyboardSelectionOverRowFetchInProgress = false;
+ updateColumnProperties(uidl);
- if (uidl.hasVariable("selected")) {
- final Set<String> selectedKeys = uidl
- .getStringArrayVariableAsSet("selected");
- if (scrollBody != null) {
- Iterator<Widget> iterator = scrollBody.iterator();
- while (iterator.hasNext()) {
- /*
- * Make the focus reflect to the server side state unless we
- * are currently selecting multiple rows with keyboard.
- */
- VScrollTableRow row = (VScrollTableRow) iterator.next();
- boolean selected = selectedKeys.contains(row.getKey());
- if (!selected
- && unSyncedselectionsBeforeRowFetch != null
- && unSyncedselectionsBeforeRowFetch.contains(row
- .getKey())) {
- selected = true;
- keyboardSelectionOverRowFetchInProgress = true;
- }
- if (selected != row.isSelected()) {
- row.toggleSelection();
- }
- }
- }
- }
- unSyncedselectionsBeforeRowFetch = null;
-
- if (uidl.hasAttribute("selectmode")) {
- if (uidl.getBooleanAttribute("readonly")) {
- selectMode = Table.SELECT_MODE_NONE;
- } else if (uidl.getStringAttribute("selectmode").equals("multi")) {
- selectMode = Table.SELECT_MODE_MULTI;
- } else if (uidl.getStringAttribute("selectmode").equals("single")) {
- selectMode = Table.SELECT_MODE_SINGLE;
- } else {
- selectMode = Table.SELECT_MODE_NONE;
- }
- }
-
- if (uidl.hasVariable("columnorder")) {
- columnReordering = true;
- columnOrder = uidl.getStringArrayVariable("columnorder");
- } else {
- columnReordering = false;
- columnOrder = null;
- }
-
- if (uidl.hasVariable("collapsedcolumns")) {
- tHead.setColumnCollapsingAllowed(true);
- collapsedColumns = uidl
- .getStringArrayVariableAsSet("collapsedcolumns");
- } else {
- tHead.setColumnCollapsingAllowed(false);
- }
-
- UIDL rowData = null;
- UIDL ac = null;
- for (final Iterator<Object> it = uidl.getChildIterator(); it.hasNext();) {
- final UIDL c = (UIDL) it.next();
- if (c.getTag().equals("rows")) {
- rowData = c;
- } else if (c.getTag().equals("actions")) {
- updateActionMap(c);
- } else if (c.getTag().equals("visiblecolumns")) {
- tHead.updateCellsFromUIDL(c);
- tFoot.updateCellsFromUIDL(c);
- } else if (c.getTag().equals("-ac")) {
- ac = c;
- }
- }
+ UIDL ac = uidl.getChildByTagName("-ac");
if (ac == null) {
if (dropHandler != null) {
// remove dropHandler if not present anymore
}
dropHandler.updateAcceptRules(ac);
}
- updateHeader(uidl.getStringArrayAttribute("vcolorder"));
-
- updateFooter(uidl.getStringArrayAttribute("vcolorder"));
- if (!recalcWidths && initializedAndAttached) {
- updateBody(rowData, uidl.getIntAttribute("firstrow"),
- uidl.getIntAttribute("rows"));
- if (headerChangedDuringUpdate) {
- lazyAdjustColumnWidths.schedule(1);
- } else {
- // webkits may still bug with their disturbing scrollbar bug,
- // See #3457
- // run overflow fix for scrollable area
- Scheduler.get().scheduleDeferred(new Command() {
- public void execute() {
- Util.runWebkitOverflowAutoFix(scrollBodyPanel
- .getElement());
- }
- });
- }
+ UIDL partialRowAdditions = uidl.getChildByTagName("prows");
+ UIDL partialRowUpdates = uidl.getChildByTagName("urows");
+ if (partialRowUpdates != null || partialRowAdditions != null) {
+ updateRowsInBody(partialRowUpdates);
+ addRowsToBody(partialRowAdditions);
} else {
- if (scrollBody != null) {
- scrollBody.removeFromParent();
- lazyUnregistryBag.add(scrollBody);
- }
- scrollBody = createScrollBody();
-
- scrollBody.renderInitialRows(rowData,
- uidl.getIntAttribute("firstrow"),
- uidl.getIntAttribute("rows"));
- scrollBodyPanel.add(scrollBody);
- initialContentReceived = true;
- if (isAttached()) {
- sizeInit();
+ UIDL rowData = uidl.getChildByTagName("rows");
+ if (!recalcWidths && initializedAndAttached) {
+ updateBody(rowData, uidl.getIntAttribute("firstrow"),
+ uidl.getIntAttribute("rows"));
+ if (headerChangedDuringUpdate) {
+ lazyAdjustColumnWidths.schedule(1);
+ } else {
+ // webkits may still bug with their disturbing scrollbar
+ // bug,
+ // See #3457
+ // run overflow fix for scrollable area
+ Scheduler.get().scheduleDeferred(new Command() {
+ public void execute() {
+ Util.runWebkitOverflowAutoFix(scrollBodyPanel
+ .getElement());
+ }
+ });
+ }
+ } else {
+ initializeRows(uidl, rowData);
}
- scrollBody.restoreRowVisibility();
}
if (selectMode == Table.SELECT_MODE_NONE) {
}
}
+ tabIndex = uidl.hasAttribute("tabindex") ? uidl
+ .getIntAttribute("tabindex") : 0;
setProperTabIndex();
+ rendering = false;
+ headerChangedDuringUpdate = false;
+
+ }
+
+ private void initializeRows(UIDL uidl, UIDL rowData) {
+ if (scrollBody != null) {
+ scrollBody.removeFromParent();
+ lazyUnregistryBag.add(scrollBody);
+ }
+ scrollBody = createScrollBody();
+
+ scrollBody.renderInitialRows(rowData, uidl.getIntAttribute("firstrow"),
+ uidl.getIntAttribute("rows"));
+ scrollBodyPanel.add(scrollBody);
+ initialContentReceived = true;
+ if (isAttached()) {
+ sizeInit();
+ }
+ scrollBody.restoreRowVisibility();
+ }
+
+ private void updateColumnProperties(UIDL uidl) {
+ updateColumnOrder(uidl);
+
+ updateCollapsedColumns(uidl);
+
+ UIDL vc = uidl.getChildByTagName("visiblecolumns");
+ if (vc != null) {
+ tHead.updateCellsFromUIDL(vc);
+ tFoot.updateCellsFromUIDL(vc);
+ }
+
+ updateHeader(uidl.getStringArrayAttribute("vcolorder"));
+
+ updateFooter(uidl.getStringArrayAttribute("vcolorder"));
+ }
+
+ private void updateCollapsedColumns(UIDL uidl) {
+ if (uidl.hasVariable("collapsedcolumns")) {
+ tHead.setColumnCollapsingAllowed(true);
+ collapsedColumns = uidl
+ .getStringArrayVariableAsSet("collapsedcolumns");
+ } else {
+ tHead.setColumnCollapsingAllowed(false);
+ }
+ }
+
+ private void updateColumnOrder(UIDL uidl) {
+ if (uidl.hasVariable("columnorder")) {
+ columnReordering = true;
+ columnOrder = uidl.getStringArrayVariable("columnorder");
+ } else {
+ columnReordering = false;
+ columnOrder = null;
+ }
+ }
+
+ private boolean selectSelectedRows(UIDL uidl) {
+ boolean keyboardSelectionOverRowFetchInProgress = false;
+
+ if (uidl.hasVariable("selected")) {
+ final Set<String> selectedKeys = uidl
+ .getStringArrayVariableAsSet("selected");
+ if (scrollBody != null) {
+ Iterator<Widget> iterator = scrollBody.iterator();
+ while (iterator.hasNext()) {
+ /*
+ * Make the focus reflect to the server side state unless we
+ * are currently selecting multiple rows with keyboard.
+ */
+ VScrollTableRow row = (VScrollTableRow) iterator.next();
+ boolean selected = selectedKeys.contains(row.getKey());
+ if (!selected
+ && unSyncedselectionsBeforeRowFetch != null
+ && unSyncedselectionsBeforeRowFetch.contains(row
+ .getKey())) {
+ selected = true;
+ keyboardSelectionOverRowFetchInProgress = true;
+ }
+ if (selected != row.isSelected()) {
+ row.toggleSelection();
+ }
+ }
+ }
+ }
+ unSyncedselectionsBeforeRowFetch = null;
+ return keyboardSelectionOverRowFetchInProgress;
+ }
+
+ private void updateSortingProperties(UIDL uidl) {
+ String oldSortColumn = sortColumn;
+ if (uidl.hasVariable("sortascending")) {
+ sortAscending = uidl.getBooleanVariable("sortascending");
+ sortColumn = uidl.getStringVariable("sortcolumn");
+ }
+
// Force recalculation of the captionContainer element inside the header
// cell to accomodate for the size of the sort arrow.
HeaderCell sortedHeader = tHead.getHeaderCell(sortColumn);
if (oldSortedHeader != null) {
oldSortedHeader.resizeCaptionContainer();
}
+ }
- rendering = false;
- headerChangedDuringUpdate = false;
+ private void updateFirstVisibleAndScrollIfNeeded(UIDL uidl) {
+ firstvisible = uidl.hasVariable("firstvisible") ? uidl
+ .getIntVariable("firstvisible") : 0;
+ if (firstvisible != lastRequestedFirstvisible && scrollBody != null) {
+ // received 'surprising' firstvisible from server: scroll there
+ firstRowInViewPort = firstvisible;
+ scrollBodyPanel.setScrollPosition((int) (firstvisible * scrollBody
+ .getRowHeight()));
+ }
+ }
+
+ private void updatePageLength(UIDL uidl) {
+ int oldPageLength = pageLength;
+ if (uidl.hasAttribute("pagelength")) {
+ pageLength = uidl.getIntAttribute("pagelength");
+ } else {
+ // pagelenght is "0" meaning scrolling is turned off
+ pageLength = totalRows;
+ }
+ if (oldPageLength != pageLength && initializedAndAttached) {
+ // page length changed, need to update size
+ sizeInit();
+ }
+ }
+
+ private void updateSelectionProperties(UIDL uidl) {
+ if (!BrowserInfo.get().isTouchDevice()) {
+ multiselectmode = uidl.hasAttribute("multiselectmode") ? uidl
+ .getIntAttribute("multiselectmode")
+ : MULTISELECT_MODE_DEFAULT;
+ }
+ nullSelectionAllowed = uidl.hasAttribute("nsa") ? uidl
+ .getBooleanAttribute("nsa") : true;
+
+ if (uidl.hasAttribute("selectmode")) {
+ if (uidl.getBooleanAttribute("readonly")) {
+ selectMode = Table.SELECT_MODE_NONE;
+ } else if (uidl.getStringAttribute("selectmode").equals("multi")) {
+ selectMode = Table.SELECT_MODE_MULTI;
+ } else if (uidl.getStringAttribute("selectmode").equals("single")) {
+ selectMode = Table.SELECT_MODE_SINGLE;
+ } else {
+ selectMode = Table.SELECT_MODE_NONE;
+ }
+ }
+ }
+
+ private void updateDragMode(UIDL uidl) {
+ dragmode = uidl.hasAttribute("dragmode") ? uidl
+ .getIntAttribute("dragmode") : 0;
+ if (BrowserInfo.get().isIE()) {
+ if (dragmode > 0) {
+ getElement().setPropertyJSO("onselectstart",
+ getPreventTextSelectionIEHack());
+ } else {
+ getElement().setPropertyJSO("onselectstart", null);
+ }
+ }
+ }
+
+ private void updateTotalRows(UIDL uidl) {
+ int newTotalRows = uidl.getIntAttribute("totalrows");
+ if (newTotalRows != getTotalRows()) {
+ if (scrollBody != null) {
+ if (getTotalRows() == 0) {
+ tHead.clear();
+ tFoot.clear();
+ }
+ initializedAndAttached = false;
+ initialContentReceived = false;
+ isNewBody = true;
+ }
+ setTotalRows(newTotalRows);
+ }
+ }
+
+ protected void setTotalRows(int newTotalRows) {
+ totalRows = newTotalRows;
+ }
+
+ protected int getTotalRows() {
+ return totalRows;
}
private void focusRowFromBody() {
}
}
+ private void setCacheRateFromUIDL(UIDL uidl) {
+ setCacheRate(uidl.hasAttribute("cr") ? uidl.getDoubleAttribute("cr")
+ : CACHE_RATE_DEFAULT);
+ }
+
private void setCacheRate(double d) {
if (cache_rate != d) {
cache_rate = d;
scrollBody.renderRows(uidl, firstRow, reqRows);
+ updateCache();
+ }
+
+ private void updateRowsInBody(UIDL partialRowUpdates) {
+ if (partialRowUpdates == null) {
+ return;
+ }
+ int firstRowIx = partialRowUpdates.getIntAttribute("firsturowix");
+ int count = partialRowUpdates.getIntAttribute("numurows");
+ scrollBody.unlinkRows(firstRowIx, count);
+ scrollBody.insertRows(partialRowUpdates, firstRowIx, count);
+
+ updateCache();
+ }
+
+ private void updateCache() {
final int optimalFirstRow = (int) (firstRowInViewPort - pageLength
* cache_rate);
boolean cont = true;
scrollBody.restoreRowVisibility();
}
+ private void addRowsToBody(UIDL partialRowAdditions) {
+ if (partialRowAdditions == null) {
+ return;
+ }
+ if (partialRowAdditions.hasAttribute("hide")) {
+ scrollBody.unlinkRows(
+ partialRowAdditions.getIntAttribute("firstprowix"),
+ partialRowAdditions.getIntAttribute("numprows"));
+ } else {
+ scrollBody.insertRows(partialRowAdditions,
+ partialRowAdditions.getIntAttribute("firstprowix"),
+ partialRowAdditions.getIntAttribute("numprows"));
+ }
+
+ updateCache();
+ }
+
/**
* Gives correct column index for given column key ("cid" in UIDL).
*
private double rowHeight = -1;
- private final List<Widget> renderedRows = new ArrayList<Widget>();
+ private final LinkedList<Widget> renderedRows = new LinkedList<Widget>();
/**
* Due some optimizations row height measuring is deferred and initial
}
fixSpacers();
}
+
// this may be a new set of rows due content change,
// ensure we have proper cache rows
int reactFirstRow = (int) (firstRowInViewPort - pageLength
* Branch for fetching cache above visible area.
*
* If cache needed for both before and after visible area, this
- * will be rendered after-cache is reveived and rendered. So in
- * some rare situations table may take two cache visits to
+ * will be rendered after-cache is received and rendered. So in
+ * some rare situations the table may make two cache visits to
* server.
*/
rowRequestHandler.setReqFirstRow(reactFirstRow);
}
}
+ public void insertRows(UIDL rowData, int firstIndex, int rows) {
+ aligns = tHead.getColumnAlignments();
+ final Iterator<?> it = rowData.getChildIterator();
+
+ if (firstIndex == lastRendered + 1) {
+ while (it.hasNext()) {
+ final VScrollTableRow row = prepareRow((UIDL) it.next());
+ addRow(row);
+ lastRendered++;
+ }
+ fixSpacers();
+ } else if (firstIndex + rows == firstRendered) {
+ final VScrollTableRow[] rowArray = new VScrollTableRow[rows];
+ int i = rows;
+ while (it.hasNext()) {
+ i--;
+ rowArray[i] = prepareRow((UIDL) it.next());
+ }
+ for (i = 0; i < rows; i++) {
+ addRowBeforeFirstRendered(rowArray[i]);
+ firstRendered--;
+ }
+ } else {
+ // insert in the middle
+ int realIx = firstIndex - firstRendered;
+ while (it.hasNext()) {
+ insertRowAt(prepareRow((UIDL) it.next()), realIx);
+ lastRendered++;
+ realIx++;
+ }
+ fixSpacers();
+ }
+ }
+
/**
* This method is used to instantiate new rows for this table. It
* automatically sets correct widths to rows cells and assigns correct
renderedRows.add(row);
}
+ private void insertRowAt(VScrollTableRow row, int index) {
+ row.setIndex(index);
+ if (row.isSelected()) {
+ row.addStyleName("v-selected");
+ }
+ if (index > 0) {
+ VScrollTableRow sibling = getRowByRowIndex(index - 1);
+ tBodyElement
+ .insertAfter(row.getElement(), sibling.getElement());
+ } else {
+ VScrollTableRow sibling = getRowByRowIndex(index);
+ tBodyElement.insertBefore(row.getElement(),
+ sibling.getElement());
+ }
+ adopt(row);
+ renderedRows.add(index, row);
+
+ // TODO: could this be made more efficient? like looping only once
+ // after all rows have been inserted
+ for (int ix = index + 1; ix < renderedRows.size(); ix++) {
+ VScrollTableRow r = (VScrollTableRow) renderedRows.get(ix);
+ r.setIndex(r.getIndex() + 1);
+ }
+ }
+
public Iterator<Widget> iterator() {
return renderedRows.iterator();
}
lastRendered--;
}
if (index >= 0) {
- final VScrollTableRow toBeRemoved = (VScrollTableRow) renderedRows
- .get(index);
- lazyUnregistryBag.add(toBeRemoved);
- tBodyElement.removeChild(toBeRemoved.getElement());
- orphan(toBeRemoved);
- renderedRows.remove(index);
+ unlinkRowAtIndex(index);
fixSpacers();
return true;
} else {
}
}
+ public void unlinkRows(int firstIndex, int count) {
+ if (count < 1) {
+ return;
+ }
+ if (firstRendered > firstIndex
+ && firstRendered < firstIndex + count) {
+ firstIndex = firstRendered;
+ }
+ int lastIndex = firstIndex + count - 1;
+ if (lastRendered < lastIndex) {
+ lastIndex = lastRendered;
+ }
+ for (int ix = lastIndex; ix >= firstIndex; ix--) {
+ unlinkRowAtIndex(ix);
+ lastRendered--;
+ }
+ fixSpacers();
+ }
+
+ public void unlinkAllRowsAfter(int index) {
+ if (firstRendered > index) {
+ index = firstRendered;
+ }
+ for (int ix = renderedRows.size() - 1; ix > index; ix--) {
+ unlinkRowAtIndex(ix);
+ lastRendered--;
+ }
+ fixSpacers();
+ }
+
+ private void unlinkRowAtIndex(int index) {
+ final VScrollTableRow toBeRemoved = (VScrollTableRow) renderedRows
+ .get(index);
+ lazyUnregistryBag.add(toBeRemoved);
+ tBodyElement.removeChild(toBeRemoved.getElement());
+ orphan(toBeRemoved);
+ renderedRows.remove(index);
+ for (int ix = index; ix < renderedRows.size(); ix++) {
+ VScrollTableRow r = (VScrollTableRow) renderedRows.get(ix);
+ r.setIndex(r.getIndex() - 1);
+ }
+ }
+
@Override
public boolean remove(Widget w) {
throw new UnsupportedOperationException();
// Inverted logic to be backwards compatible with earlier 6.4.
// It is very strange because rows 1,3,5 are considered "even"
// and 2,4,6 "odd".
+ //
+ // First remove any old styles so that both styles aren't
+ // applied when indexes are updated.
+ removeStyleName(ROW_CLASSNAME_ODD);
+ removeStyleName(ROW_CLASSNAME_EVEN);
if (!isOdd) {
addStyleName(ROW_CLASSNAME_ODD);
} else {
return;
}
- if (isContentRefreshesEnabled) {
-
- HashSet<Property> oldListenedProperties = listenedProperties;
- HashSet<Component> oldVisibleComponents = visibleComponents;
-
- // initialize the listener collections
- listenedProperties = new HashSet<Property>();
- visibleComponents = new HashSet<Component>();
-
- // Collects the basic facts about the table page
- final Object[] colids = getVisibleColumns();
- final int cols = colids.length;
- final int pagelen = getPageLength();
- int firstIndex = getCurrentPageFirstItemIndex();
- int rows, totalRows;
- rows = totalRows = size();
- if (rows > 0 && firstIndex >= 0) {
- rows -= firstIndex;
- }
- if (pagelen > 0 && pagelen < rows) {
- rows = pagelen;
- }
+ if (!isContentRefreshesEnabled) {
+ return;
+ }
- // If "to be painted next" variables are set, use them
- if (lastToBeRenderedInClient - firstToBeRenderedInClient > 0) {
- rows = lastToBeRenderedInClient - firstToBeRenderedInClient + 1;
- }
- Object id;
- if (firstToBeRenderedInClient >= 0) {
- if (firstToBeRenderedInClient < totalRows) {
- firstIndex = firstToBeRenderedInClient;
- } else {
- firstIndex = totalRows - 1;
- }
+ // initialize the listener collections
+ listenedProperties = new HashSet<Property>();
+ visibleComponents = new HashSet<Component>();
+
+ // Collects the basic facts about the table page
+ final int pagelen = getPageLength();
+ int firstIndex = getCurrentPageFirstItemIndex();
+ int rows, totalRows;
+ rows = totalRows = size();
+ if (rows > 0 && firstIndex >= 0) {
+ rows -= firstIndex;
+ }
+ if (pagelen > 0 && pagelen < rows) {
+ rows = pagelen;
+ }
+
+ // If "to be painted next" variables are set, use them
+ if (lastToBeRenderedInClient - firstToBeRenderedInClient > 0) {
+ rows = lastToBeRenderedInClient - firstToBeRenderedInClient + 1;
+ }
+ if (firstToBeRenderedInClient >= 0) {
+ if (firstToBeRenderedInClient < totalRows) {
+ firstIndex = firstToBeRenderedInClient;
} else {
- // initial load
- firstToBeRenderedInClient = firstIndex;
+ firstIndex = totalRows - 1;
}
- if (totalRows > 0) {
- if (rows + firstIndex > totalRows) {
- rows = totalRows - firstIndex;
- }
- } else {
- rows = 0;
+ } else {
+ // initial load
+ firstToBeRenderedInClient = firstIndex;
+ }
+ if (totalRows > 0) {
+ if (rows + firstIndex > totalRows) {
+ rows = totalRows - firstIndex;
}
+ } else {
+ rows = 0;
+ }
- Object[][] cells = new Object[cols + CELL_FIRSTCOL][rows];
- if (rows == 0) {
- pageBuffer = cells;
- unregisterPropertiesAndComponents(oldListenedProperties,
- oldVisibleComponents);
+ // Saves the results to internal buffer
+ pageBuffer = getVisibleCellsNoCache(firstIndex, rows);
- /*
- * We need to repaint so possible header or footer changes are
- * sent to the server
- */
- requestRepaint();
+ if (rows > 0) {
+ pageBufferFirstIndex = firstIndex;
+ }
- return;
- }
+ requestRepaint();
+ }
- // Gets the first item id
- if (items instanceof Container.Indexed) {
- id = getIdByIndex(firstIndex);
- } else {
- id = firstItemId();
- for (int i = 0; i < firstIndex; i++) {
- id = nextItemId(id);
- }
- }
+ private Object[][] getVisibleCellsNoCache(int firstIndex, int rows) {
+ final Object[] colids = getVisibleColumns();
+ final int cols = colids.length;
- final int headmode = getRowHeaderMode();
- final boolean[] iscomponent = new boolean[cols];
- for (int i = 0; i < cols; i++) {
- iscomponent[i] = columnGenerators.containsKey(colids[i])
- || Component.class.isAssignableFrom(getType(colids[i]));
- }
- int firstIndexNotInCache;
- if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
- firstIndexNotInCache = pageBufferFirstIndex
- + pageBuffer[CELL_ITEMID].length;
- } else {
- firstIndexNotInCache = -1;
+ HashSet<Property> oldListenedProperties = listenedProperties;
+ HashSet<Component> oldVisibleComponents = visibleComponents;
+
+ Object[][] cells = new Object[cols + CELL_FIRSTCOL][rows];
+ if (rows == 0) {
+ unregisterPropertiesAndComponents(oldListenedProperties,
+ oldVisibleComponents);
+ return cells;
+ }
+
+ // Gets the first item id
+ Object id;
+ if (items instanceof Container.Indexed) {
+ id = getIdByIndex(firstIndex);
+ } else {
+ id = firstItemId();
+ for (int i = 0; i < firstIndex; i++) {
+ id = nextItemId(id);
}
+ }
- // Creates the page contents
- int filledRows = 0;
- for (int i = 0; i < rows && id != null; i++) {
- cells[CELL_ITEMID][i] = id;
- cells[CELL_KEY][i] = itemIdMapper.key(id);
- if (headmode != ROW_HEADER_MODE_HIDDEN) {
- switch (headmode) {
- case ROW_HEADER_MODE_INDEX:
- cells[CELL_HEADER][i] = String.valueOf(i + firstIndex
- + 1);
- break;
- default:
- cells[CELL_HEADER][i] = getItemCaption(id);
- }
- cells[CELL_ICON][i] = getItemIcon(id);
+ final int headmode = getRowHeaderMode();
+ final boolean[] iscomponent = new boolean[cols];
+ for (int i = 0; i < cols; i++) {
+ iscomponent[i] = columnGenerators.containsKey(colids[i])
+ || Component.class.isAssignableFrom(getType(colids[i]));
+ }
+ int firstIndexNotInCache;
+ if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
+ firstIndexNotInCache = pageBufferFirstIndex
+ + pageBuffer[CELL_ITEMID].length;
+ } else {
+ firstIndexNotInCache = -1;
+ }
+
+ // Creates the page contents
+ int filledRows = 0;
+ for (int i = 0; i < rows && id != null; i++) {
+ cells[CELL_ITEMID][i] = id;
+ cells[CELL_KEY][i] = itemIdMapper.key(id);
+ if (headmode != ROW_HEADER_MODE_HIDDEN) {
+ switch (headmode) {
+ case ROW_HEADER_MODE_INDEX:
+ cells[CELL_HEADER][i] = String.valueOf(i + firstIndex + 1);
+ break;
+ default:
+ cells[CELL_HEADER][i] = getItemCaption(id);
}
+ cells[CELL_ICON][i] = getItemIcon(id);
+ }
- if (cols > 0) {
- for (int j = 0; j < cols; j++) {
- if (isColumnCollapsed(colids[j])) {
- continue;
- }
- Property p = null;
- Object value = "";
- boolean isGenerated = columnGenerators
- .containsKey(colids[j]);
+ for (int j = 0; j < cols; j++) {
+ if (isColumnCollapsed(colids[j])) {
+ continue;
+ }
+ Property p = null;
+ Object value = "";
+ boolean isGenerated = columnGenerators.containsKey(colids[j]);
- if (!isGenerated) {
- p = getContainerProperty(id, colids[j]);
- }
+ if (!isGenerated) {
+ p = getContainerProperty(id, colids[j]);
+ }
- // check in current pageBuffer already has row
- int index = firstIndex + i;
- if (p != null || isGenerated) {
- if (index < firstIndexNotInCache
- && index >= pageBufferFirstIndex) {
- // we have data already in our cache,
- // recycle it instead of fetching it via
- // getValue/getPropertyValue
- int indexInOldBuffer = index
- - pageBufferFirstIndex;
- value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer];
- if (!isGenerated && iscomponent[j]
- || !(value instanceof Component)) {
- listenProperty(p, oldListenedProperties);
- }
- } else {
- if (isGenerated) {
- ColumnGenerator cg = columnGenerators
- .get(colids[j]);
- value = cg
- .generateCell(this, id, colids[j]);
-
- } else if (iscomponent[j]) {
- value = p.getValue();
- listenProperty(p, oldListenedProperties);
- } else if (p != null) {
- value = getPropertyValue(id, colids[j], p);
- /*
- * If returned value is Component (via
- * fieldfactory or overridden
- * getPropertyValue) we excpect it to listen
- * property value changes. Otherwise if
- * property emits value change events, table
- * will start to listen them and refresh
- * content when needed.
- */
- if (!(value instanceof Component)) {
- listenProperty(p, oldListenedProperties);
- }
- } else {
- value = getPropertyValue(id, colids[j],
- null);
- }
- }
+ // check in current pageBuffer already has row
+ int index = firstIndex + i;
+ if (p != null || isGenerated) {
+ if (index < firstIndexNotInCache
+ && index >= pageBufferFirstIndex) {
+ // we have data already in our cache,
+ // recycle it instead of fetching it via
+ // getValue/getPropertyValue
+ int indexInOldBuffer = index - pageBufferFirstIndex;
+ value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer];
+ if (!isGenerated && iscomponent[j]
+ || !(value instanceof Component)) {
+ listenProperty(p, oldListenedProperties);
}
-
- if (value instanceof Component) {
- if (oldVisibleComponents == null
- || !oldVisibleComponents.contains(value)) {
- ((Component) value).setParent(this);
+ } else {
+ if (isGenerated) {
+ ColumnGenerator cg = columnGenerators
+ .get(colids[j]);
+ value = cg.generateCell(this, id, colids[j]);
+
+ } else if (iscomponent[j]) {
+ value = p.getValue();
+ listenProperty(p, oldListenedProperties);
+ } else if (p != null) {
+ value = getPropertyValue(id, colids[j], p);
+ /*
+ * If returned value is Component (via fieldfactory
+ * or overridden getPropertyValue) we excpect it to
+ * listen property value changes. Otherwise if
+ * property emits value change events, table will
+ * start to listen them and refresh content when
+ * needed.
+ */
+ if (!(value instanceof Component)) {
+ listenProperty(p, oldListenedProperties);
}
- visibleComponents.add((Component) value);
+ } else {
+ value = getPropertyValue(id, colids[j], null);
}
- cells[CELL_FIRSTCOL + j][i] = value;
}
}
- // Gets the next item id
- if (items instanceof Container.Indexed) {
- int index = firstIndex + i + 1;
- if (index < totalRows) {
- id = getIdByIndex(index);
- } else {
- id = null;
+ if (value instanceof Component) {
+ if (oldVisibleComponents == null
+ || !oldVisibleComponents.contains(value)) {
+ ((Component) value).setParent(this);
}
- } else {
- id = nextItemId(id);
+ visibleComponents.add((Component) value);
}
-
- filledRows++;
+ cells[CELL_FIRSTCOL + j][i] = value;
}
- // Assures that all the rows of the cell-buffer are valid
- if (filledRows != cells[0].length) {
- final Object[][] temp = new Object[cells.length][filledRows];
- for (int i = 0; i < cells.length; i++) {
- for (int j = 0; j < filledRows; j++) {
- temp[i][j] = cells[i][j];
- }
+ // Gets the next item id
+ if (items instanceof Container.Indexed) {
+ int index = firstIndex + i + 1;
+ if (index < size()) {
+ id = getIdByIndex(index);
+ } else {
+ id = null;
}
- cells = temp;
+ } else {
+ id = nextItemId(id);
}
- pageBufferFirstIndex = firstIndex;
-
- // Saves the results to internal buffer
- pageBuffer = cells;
-
- unregisterPropertiesAndComponents(oldListenedProperties,
- oldVisibleComponents);
+ filledRows++;
+ }
- requestRepaint();
+ // Assures that all the rows of the cell-buffer are valid
+ if (filledRows != cells[0].length) {
+ final Object[][] temp = new Object[cells.length][filledRows];
+ for (int i = 0; i < cells.length; i++) {
+ for (int j = 0; j < filledRows; j++) {
+ temp[i][j] = cells[i][j];
+ }
+ }
+ cells = temp;
}
+ unregisterPropertiesAndComponents(oldListenedProperties,
+ oldVisibleComponents);
+
+ return cells;
}
private void listenProperty(Property p,
*/
@Override
public void paintContent(PaintTarget target) throws PaintException {
+ /*
+ * Body actions - Actions which has the target null and can be invoked
+ * by right clicking on the table body.
+ */
+ final Set<Action> actionSet = findAndPaintBodyActions(target);
- // The tab ordering number
- if (getTabIndex() > 0) {
- target.addAttribute("tabindex", getTabIndex());
+ final Object[][] cells = getVisibleCells();
+ int rows = findNumRowsToPaint(target, cells);
+
+ int total = size();
+ if (shouldHideNullSelectionItem()) {
+ total--;
+ rows--;
}
- if (dragMode != TableDragMode.NONE) {
- target.addAttribute("dragmode", dragMode.ordinal());
+ // Table attributes
+ paintTableAttributes(target, rows, total);
+
+ paintVisibleColumnOrder(target);
+
+ // Rows
+ if (isPartialRowUpdate()) {
+ paintPartialRowUpdate(target, actionSet);
+ } else {
+ paintRows(target, cells, actionSet);
}
- if (multiSelectMode != MultiSelectMode.DEFAULT) {
- target.addAttribute("multiselectmode", multiSelectMode.ordinal());
+ paintSorting(target);
+
+ resetVariablesAndPageBuffer(target);
+
+ // Actions
+ paintActions(target, actionSet);
+
+ paintColumnOrder(target);
+
+ // Available columns
+ paintAvailableColumns(target);
+
+ paintVisibleColumns(target);
+
+ if (dropHandler != null) {
+ dropHandler.getAcceptCriterion().paint(target);
}
+ }
- // Initialize temps
- final Object[] colids = getVisibleColumns();
- final int cols = colids.length;
- final int first = getCurrentPageFirstItemIndex();
- int total = size();
- final int pagelen = getPageLength();
- final int colHeadMode = getColumnHeaderMode();
- final boolean colheads = colHeadMode != COLUMN_HEADER_MODE_HIDDEN;
- final Object[][] cells = getVisibleCells();
- final boolean iseditable = isEditable();
- int rows;
- if (reqRowsToPaint >= 0) {
- rows = reqRowsToPaint;
- } else {
- rows = cells[0].length;
- if (alwaysRecalculateColumnWidths) {
- // TODO experimental feature for now: tell the client to
- // recalculate column widths.
- // We'll only do this for paints that do not originate from
- // table scroll/cache requests (i.e when reqRowsToPaint<0)
- target.addAttribute("recalcWidths", true);
+ private void paintPartialRowUpdate(PaintTarget target, Set<Action> actionSet)
+ throws PaintException {
+ paintPartialRowUpdates(target, actionSet);
+ paintPartialRowAdditions(target, actionSet);
+ }
+
+ private void paintPartialRowUpdates(PaintTarget target,
+ Set<Action> actionSet) throws PaintException {
+ final boolean[] iscomponent = findCellsWithComponents();
+
+ int firstIx = getFirstUpdatedItemIndex();
+ int count = getUpdatedRowCount();
+
+ target.startTag("urows");
+ target.addAttribute("firsturowix", firstIx);
+ target.addAttribute("numurows", count);
+
+ // Partial row updates bypass the normal caching mechanism.
+ Object[][] cells = getVisibleCellsNoCache(firstIx, count);
+ for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) {
+ final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
+
+ if (shouldHideNullSelectionItem()) {
+ // Remove null selection item if null selection is not allowed
+ continue;
}
- }
- if (!isNullSelectionAllowed() && getNullSelectionItemId() != null
- && containsId(getNullSelectionItemId())) {
- total--;
- rows--;
+ paintRow(target, cells, isEditable(), actionSet, iscomponent,
+ indexInRowbuffer, itemId);
}
+ target.endTag("urows");
+ }
- // selection support
- LinkedList<String> selectedKeys = new LinkedList<String>();
- if (isMultiSelect()) {
- HashSet<?> sel = new HashSet<Object>((Set<?>) getValue());
- Collection<?> vids = getVisibleItemIds();
- for (Iterator<?> it = vids.iterator(); it.hasNext();) {
- Object id = it.next();
- if (sel.contains(id)) {
- selectedKeys.add(itemIdMapper.key(id));
+ private void paintPartialRowAdditions(PaintTarget target,
+ Set<Action> actionSet) throws PaintException {
+ final boolean[] iscomponent = findCellsWithComponents();
+
+ int firstIx = getFirstAddedItemIndex();
+ int count = getAddedRowCount();
+
+ target.startTag("prows");
+ target.addAttribute("firstprowix", firstIx);
+ target.addAttribute("numprows", count);
+
+ if (!shouldHideAddedRows()) {
+ // Partial row additions bypass the normal caching mechanism.
+ Object[][] cells = getVisibleCellsNoCache(firstIx, count);
+ for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) {
+ final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
+ if (shouldHideNullSelectionItem()) {
+ // Remove null selection item if null selection is not
+ // allowed
+ continue;
}
+
+ paintRow(target, cells, isEditable(), actionSet, iscomponent,
+ indexInRowbuffer, itemId);
}
} else {
- Object value = getValue();
- if (value == null) {
- value = getNullSelectionItemId();
- }
- if (value != null) {
- selectedKeys.add(itemIdMapper.key(value));
- }
+ target.addAttribute("hide", true);
}
+ target.endTag("prows");
+ }
- // Table attributes
- if (isSelectable()) {
- target.addAttribute("selectmode", (isMultiSelect() ? "multi"
- : "single"));
- } else {
- target.addAttribute("selectmode", "none");
- }
+ /**
+ * Subclass and override this to enable partial row updates and additions,
+ * which bypass the normal caching mechanism. This is useful for e.g.
+ * TreeTable.
+ *
+ * @return true if this update is a partial row update, false if not. For
+ * plain Table it is always false.
+ */
+ protected boolean isPartialRowUpdate() {
+ return false;
+ }
+
+ /**
+ * Subclass and override this to enable partial row additions, bypassing the
+ * normal caching mechanism. This is useful for e.g. TreeTable, where
+ * expanding a node should only fetch and add the items inside of that node.
+ *
+ * @return The index of the first added item. For plain Table it is always
+ * 0.
+ */
+ protected int getFirstAddedItemIndex() {
+ return 0;
+ }
+
+ /**
+ * Subclass and override this to enable partial row additions, bypassing the
+ * normal caching mechanism. This is useful for e.g. TreeTable, where
+ * expanding a node should only fetch and add the items inside of that node.
+ *
+ * @return the number of rows to be added, starting at the index returned by
+ * {@link #getFirstAddedItemIndex()}. For plain Table it is always
+ * 0.
+ */
+ protected int getAddedRowCount() {
+ return 0;
+ }
+
+ /**
+ * Subclass and override this to enable removing of rows, bypassing the
+ * normal caching and lazy loading mechanism. This is useful for e.g.
+ * TreeTable, when you need to hide certain rows as a node is collapsed.
+ *
+ * This should return true if the rows pointed to by
+ * {@link #getFirstAddedItemIndex()} and {@link #getAddedRowCount()} should
+ * be hidden instead of added.
+ *
+ * @return whether the rows to add (see {@link #getFirstAddedItemIndex()}
+ * and {@link #getAddedRowCount()}) should be added or hidden. For
+ * plain Table it is always false.
+ */
+ protected boolean shouldHideAddedRows() {
+ return false;
+ }
+
+ /**
+ * Subclass and override this to enable partial row updates, bypassing the
+ * normal caching and lazy loading mechanism. This is useful for updating
+ * the state of certain rows, e.g. in the TreeTable the collapsed state of a
+ * single node is updated using this mechanism.
+ *
+ * @return the index of the first item to be updated. For plain Table it is
+ * always 0.
+ */
+ protected int getFirstUpdatedItemIndex() {
+ return 0;
+ }
+
+ /**
+ * Subclass and override this to enable partial row updates, bypassing the
+ * normal caching and lazy loading mechanism. This is useful for updating
+ * the state of certain rows, e.g. in the TreeTable the collapsed state of a
+ * single node is updated using this mechanism.
+ *
+ * @return the number of rows to update, starting at the index returned by
+ * {@link #getFirstUpdatedItemIndex()}. For plain table it is always
+ * 0.
+ */
+ protected int getUpdatedRowCount() {
+ return 0;
+ }
+
+ private void paintTableAttributes(PaintTarget target, int rows, int total)
+ throws PaintException {
+ paintTabIndex(target);
+ paintDragMode(target);
+ paintSelectMode(target);
if (cacheRate != CACHE_RATE_DEFAULT) {
target.addAttribute("cr", cacheRate);
}
- target.addAttribute("cols", cols);
+ target.addAttribute("cols", getVisibleColumns().length);
target.addAttribute("rows", rows);
- if (!isNullSelectionAllowed()) {
- target.addAttribute("nsa", false);
- }
-
target.addAttribute("firstrow",
(reqFirstRowToPaint >= 0 ? reqFirstRowToPaint
: firstToBeRenderedInClient));
target.addAttribute("totalrows", total);
- if (pagelen != 0) {
- target.addAttribute("pagelength", pagelen);
+ if (getPageLength() != 0) {
+ target.addAttribute("pagelength", getPageLength());
}
- if (colheads) {
+ if (areColumnHeadersEnabled()) {
target.addAttribute("colheaders", true);
}
if (rowHeadersAreEnabled()) {
target.addAttribute("colfooters", columnFootersVisible);
- /*
- * Body actions - Actions which has the target null and can be invoked
- * by right clicking on the table body.
- */
- final Set<Action> actionSet = new LinkedHashSet<Action>();
- if (actionHandlers != null) {
- final ArrayList<String> keys = new ArrayList<String>();
- for (Handler ah : actionHandlers) {
- // Getting actions for the null item, which in this case means
- // the body item
- final Action[] aa = ah.getActions(null, this);
- if (aa != null) {
- for (int ai = 0; ai < aa.length; ai++) {
- final String key = actionMapper.key(aa[ai]);
- actionSet.add(aa[ai]);
- keys.add(key);
+ // The cursors are only shown on pageable table
+ if (getCurrentPageFirstItemIndex() != 0 || getPageLength() > 0) {
+ target.addVariable(this, "firstvisible",
+ getCurrentPageFirstItemIndex());
+ }
+ }
+
+ /**
+ * Resets and paints "to be painted next" variables. Also reset pageBuffer
+ */
+ private void resetVariablesAndPageBuffer(PaintTarget target)
+ throws PaintException {
+ reqFirstRowToPaint = -1;
+ reqRowsToPaint = -1;
+ containerChangeToBeRendered = false;
+ target.addVariable(this, "reqrows", reqRowsToPaint);
+ target.addVariable(this, "reqfirstrow", reqFirstRowToPaint);
+ }
+
+ private boolean areColumnHeadersEnabled() {
+ return getColumnHeaderMode() != COLUMN_HEADER_MODE_HIDDEN;
+ }
+
+ private void paintVisibleColumns(PaintTarget target) throws PaintException {
+ target.startTag("visiblecolumns");
+ if (rowHeadersAreEnabled()) {
+ target.startTag("column");
+ target.addAttribute("cid", ROW_HEADER_COLUMN_KEY);
+ paintColumnWidth(target, ROW_HEADER_FAKE_PROPERTY_ID);
+ target.endTag("column");
+ }
+ final Collection<?> sortables = getSortableContainerPropertyIds();
+ for (Object colId : visibleColumns) {
+ if (colId != null) {
+ target.startTag("column");
+ target.addAttribute("cid", columnIdMap.key(colId));
+ final String head = getColumnHeader(colId);
+ target.addAttribute("caption", (head != null ? head : ""));
+ final String foot = getColumnFooter(colId);
+ target.addAttribute("fcaption", (foot != null ? foot : ""));
+ if (isColumnCollapsed(colId)) {
+ target.addAttribute("collapsed", true);
+ }
+ if (areColumnHeadersEnabled()) {
+ if (getColumnIcon(colId) != null) {
+ target.addAttribute("icon", getColumnIcon(colId));
+ }
+ if (sortables.contains(colId)) {
+ target.addAttribute("sortable", true);
}
}
+ if (!ALIGN_LEFT.equals(getColumnAlignment(colId))) {
+ target.addAttribute("align", getColumnAlignment(colId));
+ }
+ paintColumnWidth(target, colId);
+ target.endTag("column");
}
- target.addAttribute("alb", keys.toArray());
}
+ target.endTag("visiblecolumns");
+ }
- // Visible column order
- final Collection<?> sortables = getSortableContainerPropertyIds();
- final ArrayList<String> visibleColOrder = new ArrayList<String>();
- for (final Iterator<Object> it = visibleColumns.iterator(); it
- .hasNext();) {
- final Object columnId = it.next();
- if (!isColumnCollapsed(columnId)) {
- visibleColOrder.add(columnIdMap.key(columnId));
+ private void paintAvailableColumns(PaintTarget target)
+ throws PaintException {
+ if (columnCollapsingAllowed) {
+ final HashSet<Object> collapsedCols = new HashSet<Object>();
+ for (Object colId : visibleColumns) {
+ if (isColumnCollapsed(colId)) {
+ collapsedCols.add(colId);
+ }
+ }
+ final String[] collapsedKeys = new String[collapsedCols.size()];
+ int nextColumn = 0;
+ for (Object colId : visibleColumns) {
+ if (isColumnCollapsed(colId)) {
+ collapsedKeys[nextColumn++] = columnIdMap.key(colId);
+ }
}
+ target.addVariable(this, "collapsedcolumns", collapsedKeys);
}
- target.addAttribute("vcolorder", visibleColOrder.toArray());
+ }
- // Rows
- final boolean selectable = isSelectable();
- final boolean[] iscomponent = new boolean[visibleColumns.size()];
- int iscomponentIndex = 0;
- for (final Iterator<Object> it = visibleColumns.iterator(); it
- .hasNext() && iscomponentIndex < iscomponent.length;) {
- final Object columnId = it.next();
- if (columnGenerators.containsKey(columnId)) {
- iscomponent[iscomponentIndex++] = true;
- } else {
- final Class<?> colType = getType(columnId);
- iscomponent[iscomponentIndex++] = colType != null
- && Component.class.isAssignableFrom(colType);
+ private void paintActions(PaintTarget target, final Set<Action> actionSet)
+ throws PaintException {
+ if (!actionSet.isEmpty()) {
+ target.addVariable(this, "action", "");
+ target.startTag("actions");
+ for (Action a : actionSet) {
+ target.startTag("action");
+ if (a.getCaption() != null) {
+ target.addAttribute("caption", a.getCaption());
+ }
+ if (a.getIcon() != null) {
+ target.addAttribute("icon", a.getIcon());
+ }
+ target.addAttribute("key", actionMapper.key(a));
+ target.endTag("action");
+ }
+ target.endTag("actions");
+ }
+ }
+
+ private void paintColumnOrder(PaintTarget target) throws PaintException {
+ if (columnReorderingAllowed) {
+ final String[] colorder = new String[visibleColumns.size()];
+ int i = 0;
+ for (Object colId : visibleColumns) {
+ colorder[i++] = columnIdMap.key(colId);
}
+ target.addVariable(this, "columnorder", colorder);
+ }
+ }
+
+ private void paintSorting(PaintTarget target) throws PaintException {
+ // Sorting
+ if (getContainerDataSource() instanceof Container.Sortable) {
+ target.addVariable(this, "sortcolumn",
+ columnIdMap.key(sortContainerPropertyId));
+ target.addVariable(this, "sortascending", sortAscending);
}
+ }
+
+ private void paintRows(PaintTarget target, final Object[][] cells,
+ final Set<Action> actionSet) throws PaintException {
+ final boolean[] iscomponent = findCellsWithComponents();
+
target.startTag("rows");
// cells array contains all that are supposed to be visible on client,
// but we'll start from the one requested by client
for (int indexInRowbuffer = start; indexInRowbuffer < end; indexInRowbuffer++) {
final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
- if (!isNullSelectionAllowed() && getNullSelectionItemId() != null
- && itemId == getNullSelectionItemId()) {
+ if (shouldHideNullSelectionItem()) {
// Remove null selection item if null selection is not allowed
continue;
}
- paintRow(target, cells, iseditable, actionSet, iscomponent,
+ paintRow(target, cells, isEditable(), actionSet, iscomponent,
indexInRowbuffer, itemId);
}
target.endTag("rows");
+ }
- // The select variable is only enabled if selectable
- if (selectable) {
- target.addVariable(this, "selected",
- selectedKeys.toArray(new String[selectedKeys.size()]));
+ private boolean[] findCellsWithComponents() {
+ final boolean[] isComponent = new boolean[visibleColumns.size()];
+ int isComponentIndex = 0;
+ for (final Iterator<Object> it = visibleColumns.iterator(); it
+ .hasNext() && isComponentIndex < isComponent.length;) {
+ final Object columnId = it.next();
+ if (columnGenerators.containsKey(columnId)) {
+ isComponent[isComponentIndex++] = true;
+ } else {
+ final Class<?> colType = getType(columnId);
+ isComponent[isComponentIndex++] = colType != null
+ && Component.class.isAssignableFrom(colType);
+ }
}
+ return isComponent;
+ }
- // The cursors are only shown on pageable table
- if (first != 0 || getPageLength() > 0) {
- target.addVariable(this, "firstvisible", first);
+ private void paintVisibleColumnOrder(PaintTarget target) {
+ // Visible column order
+ final ArrayList<String> visibleColOrder = new ArrayList<String>();
+ for (final Iterator<Object> it = visibleColumns.iterator(); it
+ .hasNext();) {
+ final Object columnId = it.next();
+ if (!isColumnCollapsed(columnId)) {
+ visibleColOrder.add(columnIdMap.key(columnId));
+ }
}
+ target.addAttribute("vcolorder", visibleColOrder.toArray());
+ }
- // Sorting
- if (getContainerDataSource() instanceof Container.Sortable) {
- target.addVariable(this, "sortcolumn",
- columnIdMap.key(sortContainerPropertyId));
- target.addVariable(this, "sortascending", sortAscending);
+ private Set<Action> findAndPaintBodyActions(PaintTarget target) {
+ Set<Action> actionSet = new LinkedHashSet<Action>();
+ if (actionHandlers != null) {
+ final ArrayList<String> keys = new ArrayList<String>();
+ for (Handler ah : actionHandlers) {
+ // Getting actions for the null item, which in this case means
+ // the body item
+ final Action[] actions = ah.getActions(null, this);
+ if (actions != null) {
+ for (Action action : actions) {
+ actionSet.add(action);
+ keys.add(actionMapper.key(action));
+ }
+ }
+ }
+ target.addAttribute("alb", keys.toArray());
}
+ return actionSet;
+ }
- // Resets and paints "to be painted next" variables. Also reset
- // pageBuffer
- reqFirstRowToPaint = -1;
- reqRowsToPaint = -1;
- containerChangeToBeRendered = false;
- target.addVariable(this, "reqrows", reqRowsToPaint);
- target.addVariable(this, "reqfirstrow", reqFirstRowToPaint);
+ private boolean shouldHideNullSelectionItem() {
+ return !isNullSelectionAllowed() && getNullSelectionItemId() != null
+ && containsId(getNullSelectionItemId());
+ }
- // Actions
- if (!actionSet.isEmpty()) {
- target.addVariable(this, "action", "");
- target.startTag("actions");
- for (final Iterator<Action> it = actionSet.iterator(); it.hasNext();) {
- final Action a = it.next();
- target.startTag("action");
- if (a.getCaption() != null) {
- target.addAttribute("caption", a.getCaption());
- }
- if (a.getIcon() != null) {
- target.addAttribute("icon", a.getIcon());
- }
- target.addAttribute("key", actionMapper.key(a));
- target.endTag("action");
+ private int findNumRowsToPaint(PaintTarget target, final Object[][] cells)
+ throws PaintException {
+ int rows;
+ if (reqRowsToPaint >= 0) {
+ rows = reqRowsToPaint;
+ } else {
+ rows = cells[0].length;
+ if (alwaysRecalculateColumnWidths) {
+ // TODO experimental feature for now: tell the client to
+ // recalculate column widths.
+ // We'll only do this for paints that do not originate from
+ // table scroll/cache requests (i.e when reqRowsToPaint<0)
+ target.addAttribute("recalcWidths", true);
}
- target.endTag("actions");
}
- if (columnReorderingAllowed) {
- final String[] colorder = new String[visibleColumns.size()];
- int i = 0;
- for (final Iterator<Object> it = visibleColumns.iterator(); it
- .hasNext() && i < colorder.length;) {
- colorder[i++] = columnIdMap.key(it.next());
- }
- target.addVariable(this, "columnorder", colorder);
+ return rows;
+ }
+
+ private void paintSelectMode(PaintTarget target) throws PaintException {
+ if (multiSelectMode != MultiSelectMode.DEFAULT) {
+ target.addAttribute("multiselectmode", multiSelectMode.ordinal());
}
- // Available columns
- if (columnCollapsingAllowed) {
- final HashSet<Object> ccs = new HashSet<Object>();
- for (final Iterator<Object> i = visibleColumns.iterator(); i
- .hasNext();) {
- final Object o = i.next();
- if (isColumnCollapsed(o)) {
- ccs.add(o);
- }
- }
- final String[] collapsedkeys = new String[ccs.size()];
- int nextColumn = 0;
- for (final Iterator<Object> it = visibleColumns.iterator(); it
- .hasNext() && nextColumn < collapsedkeys.length;) {
- final Object columnId = it.next();
- if (isColumnCollapsed(columnId)) {
- collapsedkeys[nextColumn++] = columnIdMap.key(columnId);
- }
- }
- target.addVariable(this, "collapsedcolumns", collapsedkeys);
+ if (isSelectable()) {
+ target.addAttribute("selectmode", (isMultiSelect() ? "multi"
+ : "single"));
+ } else {
+ target.addAttribute("selectmode", "none");
}
- target.startTag("visiblecolumns");
- if (rowHeadersAreEnabled()) {
- target.startTag("column");
- target.addAttribute("cid", ROW_HEADER_COLUMN_KEY);
- paintColumnWidth(target, ROW_HEADER_FAKE_PROPERTY_ID);
- target.endTag("column");
+ if (!isNullSelectionAllowed()) {
+ target.addAttribute("nsa", false);
}
- int i = 0;
- for (final Iterator<Object> it = visibleColumns.iterator(); it
- .hasNext(); i++) {
- final Object columnId = it.next();
- if (columnId != null) {
- target.startTag("column");
- target.addAttribute("cid", columnIdMap.key(columnId));
- final String head = getColumnHeader(columnId);
- target.addAttribute("caption", (head != null ? head : ""));
- final String foot = getColumnFooter(columnId);
- target.addAttribute("fcaption", (foot != null ? foot : ""));
- if (isColumnCollapsed(columnId)) {
- target.addAttribute("collapsed", true);
- }
- if (colheads) {
- if (getColumnIcon(columnId) != null) {
- target.addAttribute("icon", getColumnIcon(columnId));
- }
- if (sortables.contains(columnId)) {
- target.addAttribute("sortable", true);
- }
- }
- if (!ALIGN_LEFT.equals(getColumnAlignment(columnId))) {
- target.addAttribute("align", getColumnAlignment(columnId));
+
+ // selection support
+ // The select variable is only enabled if selectable
+ if (isSelectable()) {
+ target.addVariable(this, "selected", findSelectedKeys());
+ }
+ }
+
+ private String[] findSelectedKeys() {
+ LinkedList<String> selectedKeys = new LinkedList<String>();
+ if (isMultiSelect()) {
+ HashSet<?> sel = new HashSet<Object>((Set<?>) getValue());
+ Collection<?> vids = getVisibleItemIds();
+ for (Iterator<?> it = vids.iterator(); it.hasNext();) {
+ Object id = it.next();
+ if (sel.contains(id)) {
+ selectedKeys.add(itemIdMapper.key(id));
}
- paintColumnWidth(target, columnId);
- target.endTag("column");
+ }
+ } else {
+ Object value = getValue();
+ if (value == null) {
+ value = getNullSelectionItemId();
+ }
+ if (value != null) {
+ selectedKeys.add(itemIdMapper.key(value));
}
}
- target.endTag("visiblecolumns");
+ return selectedKeys.toArray(new String[selectedKeys.size()]);
+ }
- if (dropHandler != null) {
- dropHandler.getAcceptCriterion().paint(target);
+ private void paintDragMode(PaintTarget target) throws PaintException {
+ if (dragMode != TableDragMode.NONE) {
+ target.addAttribute("dragmode", dragMode.ordinal());
+ }
+ }
+
+ private void paintTabIndex(PaintTarget target) throws PaintException {
+ // The tab ordering number
+ if (getTabIndex() > 0) {
+ target.addAttribute("tabindex", getTabIndex());
}
}