1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.vaadin.client.ui;
18
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.LinkedList;
22 import java.util.List;
23
24 import com.google.gwt.animation.client.Animation;
25 import com.google.gwt.core.client.Scheduler;
26 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
27 import com.google.gwt.dom.client.Document;
28 import com.google.gwt.dom.client.ImageElement;
29 import com.google.gwt.dom.client.SpanElement;
30 import com.google.gwt.dom.client.Style.Display;
31 import com.google.gwt.dom.client.Style.Unit;
32 import com.google.gwt.dom.client.Style.Visibility;
33 import com.google.gwt.dom.client.TableCellElement;
34 import com.google.gwt.event.dom.client.KeyCodes;
35 import com.google.gwt.user.client.DOM;
36 import com.google.gwt.user.client.Element;
37 import com.google.gwt.user.client.Event;
38 import com.google.gwt.user.client.ui.Widget;
39 import com.vaadin.client.ComputedStyle;
40 import com.vaadin.client.UIDL;
41 import com.vaadin.client.Util;
42 import com.vaadin.client.ui.VTreeTable.VTreeTableScrollBody.VTreeTableRow;
43
44 public class VTreeTable extends VScrollTable {
45
46
47 public static class PendingNavigationEvent {
48 public final int keycode;
49 public final boolean ctrl;
50 public final boolean shift;
51
52 public PendingNavigationEvent(int keycode, boolean ctrl, boolean shift) {
53 this.keycode = keycode;
54 this.ctrl = ctrl;
55 this.shift = shift;
56 }
57
58 @Override
59 public String toString() {
60 String string = "Keyboard event: " + keycode;
61 if (ctrl) {
62 string += " + ctrl";
63 }
64 if (shift) {
65 string += " + shift";
66 }
67 return string;
68 }
69 }
70
71
72 public boolean collapseRequest;
73
74 private boolean selectionPending;
75
76
77 public int colIndexOfHierarchy;
78
79
80 public String collapsedRowKey;
81
82
83 public VTreeTableScrollBody scrollBody;
84
85
86 public boolean animationsEnabled;
87
88
89 public LinkedList<PendingNavigationEvent> pendingNavigationEvents = new LinkedList<VTreeTable.PendingNavigationEvent>();
90
91
92 public boolean focusParentResponsePending;
93
94 @Override
95 protected VScrollTableBody createScrollBody() {
96 scrollBody = new VTreeTableScrollBody();
97 return scrollBody;
98 }
99
100
101
102
103 @Override
104 public void addAndRemoveRows(UIDL partialRowAdditions) {
105 if (partialRowAdditions == null) {
106 return;
107 }
108
109 if (animationsEnabled) {
110 if (partialRowAdditions.hasAttribute("hide")) {
111 scrollBody.unlinkRowsAnimatedAndUpdateCacheWhenFinished(
112 partialRowAdditions.getIntAttribute("firstprowix"),
113 partialRowAdditions.getIntAttribute("numprows"));
114 } else {
115 scrollBody.insertRowsAnimated(partialRowAdditions,
116 partialRowAdditions.getIntAttribute("firstprowix"),
117 partialRowAdditions.getIntAttribute("numprows"));
118 discardRowsOutsideCacheWindow();
119 }
120 } else {
121 super.addAndRemoveRows(partialRowAdditions);
122 }
123 }
124
125 @Override
126 protected int getHierarchyColumnIndex() {
127 return colIndexOfHierarchy + (showRowHeaders ? 1 : 0);
128 }
129
130 public class VTreeTableScrollBody extends VScrollTable.VScrollTableBody {
131 private int indentWidth = -1;
132 private int maxIndent = 0;
133
134 VTreeTableScrollBody() {
135 super();
136 }
137
138 @Override
139 protected VScrollTableRow createRow(UIDL uidl, char[] aligns2) {
140 if (uidl.hasAttribute("gen_html")) {
141
142 return new VTreeTableGeneratedRow(uidl, aligns2);
143 }
144 return new VTreeTableRow(uidl, aligns2);
145 }
146
147 public class VTreeTableRow extends
148 VScrollTable.VScrollTableBody.VScrollTableRow {
149
150 private boolean isTreeCellAdded = false;
151 private SpanElement treeSpacer;
152 private boolean open;
153 private int depth;
154 private boolean canHaveChildren;
155 protected Widget widgetInHierarchyColumn;
156
157 public VTreeTableRow(UIDL uidl, char[] aligns2) {
158 super(uidl, aligns2);
159 }
160
161 @Override
162 public void addCell(UIDL rowUidl, String text, char align,
163 String style, boolean textIsHTML, boolean isSorted,
164 String description) {
165 super.addCell(rowUidl, text, align, style, textIsHTML,
166 isSorted, description);
167
168 addTreeSpacer(rowUidl);
169 }
170
171 protected boolean addTreeSpacer(UIDL rowUidl) {
172 if (cellShowsTreeHierarchy(getElement().getChildCount() - 1)) {
173 Element container = (Element) getElement().getLastChild()
174 .getFirstChild();
175
176 if (rowUidl.hasAttribute("icon")) {
177
178 ImageElement icon = Document.get().createImageElement();
179 icon.setClassName("v-icon");
180 icon.setAlt("icon");
181 icon.setSrc(client.translateVaadinUri(rowUidl
182 .getStringAttribute("icon")));
183 container.insertFirst(icon);
184 }
185
186 String classname = "v-treetable-treespacer";
187 if (rowUidl.getBooleanAttribute("ca")) {
188 canHaveChildren = true;
189 open = rowUidl.getBooleanAttribute("open");
190 classname += open ? " v-treetable-node-open"
191 : " v-treetable-node-closed";
192 }
193
194 treeSpacer = Document.get().createSpanElement();
195
196 treeSpacer.setClassName(classname);
197 container.insertFirst(treeSpacer);
198 depth = rowUidl.hasAttribute("depth") ? rowUidl
199 .getIntAttribute("depth") : 0;
200 setIndent();
201 isTreeCellAdded = true;
202 return true;
203 }
204 return false;
205 }
206
207 private boolean cellShowsTreeHierarchy(int curColIndex) {
208 if (isTreeCellAdded) {
209 return false;
210 }
211 return curColIndex == getHierarchyColumnIndex();
212 }
213
214 @Override
215 public void onBrowserEvent(Event event) {
216 if (event.getEventTarget().cast() == treeSpacer
217 && treeSpacer.getClassName().contains("node")) {
218 if (event.getTypeInt() == Event.ONMOUSEUP) {
219 sendToggleCollapsedUpdate(getKey());
220 }
221 return;
222 }
223 super.onBrowserEvent(event);
224 }
225
226 @Override
227 public void addCell(UIDL rowUidl, Widget w, char align,
228 String style, boolean isSorted, String description) {
229 super.addCell(rowUidl, w, align, style, isSorted, description);
230 if (addTreeSpacer(rowUidl)) {
231 widgetInHierarchyColumn = w;
232 }
233
234 }
235
236 private void setIndent() {
237 if (getIndentWidth() > 0) {
238 treeSpacer.getParentElement().getStyle()
239 .setPaddingLeft(getIndent(), Unit.PX);
240 treeSpacer.getStyle().setWidth(getIndent(), Unit.PX);
241 int colWidth = getColWidth(getHierarchyColumnIndex());
242 if (colWidth > 0 && getIndent() > colWidth) {
243 VTreeTable.this.setColWidth(getHierarchyColumnIndex(),
244 getIndent(), false);
245 }
246 }
247 }
248
249 @Override
250 protected void onAttach() {
251 super.onAttach();
252 if (getIndentWidth() < 0) {
253 detectIndent(this);
254
255
256
257 int w = getCellWidthFromDom(getHierarchyColumnIndex());
258 if (w >= 0) {
259 setColWidth(getHierarchyColumnIndex(), w);
260 }
261 }
262 }
263
264 private int getCellWidthFromDom(int cellIndex) {
265 final Element cell = DOM.getChild(getElement(), cellIndex);
266 String w = cell.getStyle().getProperty("width");
267 if (w == null || "".equals(w) || !w.endsWith("px")) {
268 return -1;
269 } else {
270 return Integer.parseInt(w.substring(0, w.length() - 2));
271 }
272 }
273
274 private int getHierarchyAndIconWidth() {
275 int consumedSpace = treeSpacer.getOffsetWidth();
276 if (treeSpacer.getParentElement().getChildCount() > 2) {
277
278 consumedSpace += ((com.google.gwt.dom.client.Element) treeSpacer
279 .getNextSibling()).getOffsetWidth();
280 }
281 return consumedSpace;
282 }
283
284 @Override
285 protected void setCellWidth(int cellIx, int width) {
286 if (cellIx == getHierarchyColumnIndex()) {
287
288
289 int indent = getIndent();
290 if (indent != -1) {
291 width = Math.max(width - indent, 0);
292 }
293 }
294 super.setCellWidth(cellIx, width);
295 }
296
297 private int getIndent() {
298 return (depth + 1) * getIndentWidth();
299 }
300 }
301
302 protected class VTreeTableGeneratedRow extends VTreeTableRow {
303 private boolean spanColumns;
304 private boolean htmlContentAllowed;
305
306 public VTreeTableGeneratedRow(UIDL uidl, char[] aligns) {
307 super(uidl, aligns);
308 addStyleName("v-table-generated-row");
309 }
310
311 public boolean isSpanColumns() {
312 return spanColumns;
313 }
314
315 @Override
316 protected void initCellWidths() {
317 if (spanColumns) {
318 setSpannedColumnWidthAfterDOMFullyInited();
319 } else {
320 super.initCellWidths();
321 }
322 }
323
324 private void setSpannedColumnWidthAfterDOMFullyInited() {
325
326
327
328 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
329
330 @Override
331 public void execute() {
332 if (showRowHeaders) {
333 setCellWidth(0, tHead.getHeaderCell(0)
334 .getWidthWithIndent());
335 calcAndSetSpanWidthOnCell(1);
336 } else {
337 calcAndSetSpanWidthOnCell(0);
338 }
339 }
340 });
341 }
342
343 @Override
344 protected boolean isRenderHtmlInCells() {
345 return htmlContentAllowed;
346 }
347
348 @Override
349 protected void addCellsFromUIDL(UIDL uidl, char[] aligns, int col,
350 int visibleColumnIndex) {
351 htmlContentAllowed = uidl.getBooleanAttribute("gen_html");
352 spanColumns = uidl.getBooleanAttribute("gen_span");
353
354 final Iterator<?> cells = uidl.getChildIterator();
355 if (spanColumns) {
356 int colCount = uidl.getChildCount();
357 if (cells.hasNext()) {
358 final Object cell = cells.next();
359 if (cell instanceof String) {
360 addSpannedCell(uidl, cell.toString(), aligns[0],
361 "", htmlContentAllowed, false, null,
362 colCount);
363 } else {
364 addSpannedCell(uidl, (Widget) cell, aligns[0], "",
365 false, colCount);
366 }
367 }
368 } else {
369 super.addCellsFromUIDL(uidl, aligns, col,
370 visibleColumnIndex);
371 }
372 }
373
374 private void addSpannedCell(UIDL rowUidl, Widget w, char align,
375 String style, boolean sorted, int colCount) {
376 TableCellElement td = DOM.createTD().cast();
377 td.setColSpan(colCount);
378 initCellWithWidget(w, align, style, sorted, td);
379 td.getStyle().setHeight(getRowHeight(), Unit.PX);
380 if (addTreeSpacer(rowUidl)) {
381 widgetInHierarchyColumn = w;
382 }
383 }
384
385 private void addSpannedCell(UIDL rowUidl, String text, char align,
386 String style, boolean textIsHTML, boolean sorted,
387 String description, int colCount) {
388
389 final TableCellElement td = DOM.createTD().cast();
390 td.setColSpan(colCount);
391 initCellWithText(text, align, style, textIsHTML, sorted,
392 description, td);
393 td.getStyle().setHeight(getRowHeight(), Unit.PX);
394 addTreeSpacer(rowUidl);
395 }
396
397 @Override
398 protected void setCellWidth(int cellIx, int width) {
399 if (isSpanColumns()) {
400 if (showRowHeaders) {
401 if (cellIx == 0) {
402 super.setCellWidth(0, width);
403 } else {
404
405
406 calcAndSetSpanWidthOnCell(1);
407 }
408 } else {
409
410 calcAndSetSpanWidthOnCell(0);
411 }
412 } else {
413 super.setCellWidth(cellIx, width);
414 }
415 }
416
417 private void calcAndSetSpanWidthOnCell(final int cellIx) {
418 int spanWidth = 0;
419 for (int ix = (showRowHeaders ? 1 : 0); ix < tHead
420 .getVisibleCellCount(); ix++) {
421 spanWidth += tHead.getHeaderCell(ix).getOffsetWidth();
422 }
423 Util.setWidthExcludingPaddingAndBorder((Element) getElement()
424 .getChild(cellIx), spanWidth, 13, false);
425 }
426 }
427
428 private int getIndentWidth() {
429 return indentWidth;
430 }
431
432 @Override
433 protected int getMaxIndent() {
434 return maxIndent;
435 }
436
437 @Override
438 protected void calculateMaxIndent() {
439 int maxIndent = 0;
440 Iterator<Widget> iterator = iterator();
441 while (iterator.hasNext()) {
442 VTreeTableRow next = (VTreeTableRow) iterator.next();
443 maxIndent = Math.max(maxIndent, next.getIndent());
444 }
445 this.maxIndent = maxIndent;
446 }
447
448 private void detectIndent(VTreeTableRow vTreeTableRow) {
449 indentWidth = vTreeTableRow.treeSpacer.getOffsetWidth();
450 if (indentWidth == 0) {
451 indentWidth = -1;
452 return;
453 }
454 Iterator<Widget> iterator = iterator();
455 while (iterator.hasNext()) {
456 VTreeTableRow next = (VTreeTableRow) iterator.next();
457 next.setIndent();
458 }
459 calculateMaxIndent();
460 }
461
462 protected void unlinkRowsAnimatedAndUpdateCacheWhenFinished(
463 final int firstIndex, final int rows) {
464 List<VScrollTableRow> rowsToDelete = new ArrayList<VScrollTableRow>();
465 for (int ix = firstIndex; ix < firstIndex + rows; ix++) {
466 VScrollTableRow row = getRowByRowIndex(ix);
467 if (row != null) {
468 rowsToDelete.add(row);
469 }
470 }
471 if (!rowsToDelete.isEmpty()) {
472
473 RowCollapseAnimation anim = new RowCollapseAnimation(
474 rowsToDelete) {
475 @Override
476 protected void onComplete() {
477 super.onComplete();
478
479
480
481 unlinkAndReindexRows(firstIndex, rows);
482 discardRowsOutsideCacheWindow();
483 ensureCacheFilled();
484 }
485 };
486 anim.run(150);
487 }
488 }
489
490 protected List<VScrollTableRow> insertRowsAnimated(UIDL rowData,
491 int firstIndex, int rows) {
492 List<VScrollTableRow> insertedRows = insertAndReindexRows(rowData,
493 firstIndex, rows);
494 if (!insertedRows.isEmpty()) {
495
496 RowExpandAnimation anim = new RowExpandAnimation(insertedRows);
497 anim.run(150);
498 }
499 scrollBody.calculateMaxIndent();
500 return insertedRows;
501 }
502
503
504
505
506
507
508
509 private class AnimationPreparator {
510 private final int lastItemIx;
511
512 public AnimationPreparator(int lastItemIx) {
513 this.lastItemIx = lastItemIx;
514 }
515
516 public void prepareTableForAnimation() {
517 int ix = lastItemIx;
518 VScrollTableRow row = null;
519 while ((row = getRowByRowIndex(ix)) != null) {
520 copyTRBackgroundsToTDs(row);
521 --ix;
522 }
523 }
524
525 private void copyTRBackgroundsToTDs(VScrollTableRow row) {
526 Element tr = row.getElement();
527 ComputedStyle cs = new ComputedStyle(tr);
528 String backgroundAttachment = cs
529 .getProperty("backgroundAttachment");
530 String backgroundClip = cs.getProperty("backgroundClip");
531 String backgroundColor = cs.getProperty("backgroundColor");
532 String backgroundImage = cs.getProperty("backgroundImage");
533 String backgroundOrigin = cs.getProperty("backgroundOrigin");
534 for (int ix = 0; ix < tr.getChildCount(); ix++) {
535 Element td = tr.getChild(ix).cast();
536 if (!elementHasBackground(td)) {
537 td.getStyle().setProperty("backgroundAttachment",
538 backgroundAttachment);
539 td.getStyle().setProperty("backgroundClip",
540 backgroundClip);
541 td.getStyle().setProperty("backgroundColor",
542 backgroundColor);
543 td.getStyle().setProperty("backgroundImage",
544 backgroundImage);
545 td.getStyle().setProperty("backgroundOrigin",
546 backgroundOrigin);
547 }
548 }
549 }
550
551 private boolean elementHasBackground(Element element) {
552 ComputedStyle cs = new ComputedStyle(element);
553 String clr = cs.getProperty("backgroundColor");
554 String img = cs.getProperty("backgroundImage");
555 return !("rgba(0, 0, 0, 0)".equals(clr.trim())
556 || "transparent".equals(clr.trim()) || img == null);
557 }
558
559 public void restoreTableAfterAnimation() {
560 int ix = lastItemIx;
561 VScrollTableRow row = null;
562 while ((row = getRowByRowIndex(ix)) != null) {
563 restoreStyleForTDsInRow(row);
564
565 --ix;
566 }
567 }
568
569 private void restoreStyleForTDsInRow(VScrollTableRow row) {
570 Element tr = row.getElement();
571 for (int ix = 0; ix < tr.getChildCount(); ix++) {
572 Element td = tr.getChild(ix).cast();
573 td.getStyle().clearProperty("backgroundAttachment");
574 td.getStyle().clearProperty("backgroundClip");
575 td.getStyle().clearProperty("backgroundColor");
576 td.getStyle().clearProperty("backgroundImage");
577 td.getStyle().clearProperty("backgroundOrigin");
578 }
579 }
580 }
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615 private class RowExpandAnimation extends Animation {
616
617 private final List<VScrollTableRow> rows;
618 private Element cloneDiv;
619 private Element cloneTable;
620 private AnimationPreparator preparator;
621
622
623
624
625
626 public RowExpandAnimation(List<VScrollTableRow> rows) {
627 this.rows = rows;
628 buildAndInsertAnimatingDiv();
629 preparator = new AnimationPreparator(rows.get(0).getIndex() - 1);
630 preparator.prepareTableForAnimation();
631 for (VScrollTableRow row : rows) {
632 cloneAndAppendRow(row);
633 row.addStyleName("v-table-row-animating");
634 setCellWrapperDivsToDisplayNone(row);
635 row.setHeight(getInitialHeight());
636 }
637 }
638
639 protected String getInitialHeight() {
640 return "0px";
641 }
642
643 private void cloneAndAppendRow(VScrollTableRow row) {
644 Element clonedTR = null;
645 clonedTR = row.getElement().cloneNode(true).cast();
646 clonedTR.getStyle().setVisibility(Visibility.VISIBLE);
647 cloneTable.appendChild(clonedTR);
648 }
649
650 protected double getBaseOffset() {
651 return rows.get(0).getAbsoluteTop()
652 - rows.get(0).getParent().getAbsoluteTop()
653 - rows.size() * getRowHeight();
654 }
655
656 private void buildAndInsertAnimatingDiv() {
657 cloneDiv = DOM.createDiv();
658 cloneDiv.addClassName("v-treetable-animation-clone-wrapper");
659 cloneTable = DOM.createTable();
660 cloneTable.addClassName("v-treetable-animation-clone");
661 cloneDiv.appendChild(cloneTable);
662 insertAnimatingDiv();
663 }
664
665 private void insertAnimatingDiv() {
666 Element tableBody = getElement().cast();
667 Element tableBodyParent = tableBody.getParentElement().cast();
668 tableBodyParent.insertAfter(cloneDiv, tableBody);
669 }
670
671 @Override
672 protected void onUpdate(double progress) {
673 animateDiv(progress);
674 animateRowHeights(progress);
675 }
676
677 private void animateDiv(double progress) {
678 double offset = calculateDivOffset(progress, getRowHeight());
679
680 cloneDiv.getStyle().setTop(getBaseOffset() + offset, Unit.PX);
681 }
682
683 private void animateRowHeights(double progress) {
684 double rh = getRowHeight();
685 double vlh = calculateHeightOfAllVisibleLines(progress, rh);
686 int ix = 0;
687
688 while (ix < rows.size()) {
689 double height = vlh < rh ? vlh : rh;
690 rows.get(ix).setHeight(height + "px");
691 vlh -= height;
692 ix++;
693 }
694 }
695
696 protected double calculateHeightOfAllVisibleLines(double progress,
697 double rh) {
698 return rows.size() * rh * progress;
699 }
700
701 protected double calculateDivOffset(double progress, double rh) {
702 return progress * rows.size() * rh;
703 }
704
705 @Override
706 protected void onComplete() {
707 preparator.restoreTableAfterAnimation();
708 for (VScrollTableRow row : rows) {
709 resetCellWrapperDivsDisplayProperty(row);
710 row.removeStyleName("v-table-row-animating");
711 }
712 Element tableBodyParent = (Element) getElement()
713 .getParentElement();
714 tableBodyParent.removeChild(cloneDiv);
715 }
716
717 private void setCellWrapperDivsToDisplayNone(VScrollTableRow row) {
718 Element tr = row.getElement();
719 for (int ix = 0; ix < tr.getChildCount(); ix++) {
720 getWrapperDiv(tr, ix).getStyle().setDisplay(Display.NONE);
721 }
722 }
723
724 private Element getWrapperDiv(Element tr, int tdIx) {
725 Element td = tr.getChild(tdIx).cast();
726 return td.getChild(0).cast();
727 }
728
729 private void resetCellWrapperDivsDisplayProperty(VScrollTableRow row) {
730 Element tr = row.getElement();
731 for (int ix = 0; ix < tr.getChildCount(); ix++) {
732 getWrapperDiv(tr, ix).getStyle().clearProperty("display");
733 }
734 }
735
736 }
737
738
739
740
741
742 private class RowCollapseAnimation extends RowExpandAnimation {
743
744 private final List<VScrollTableRow> rows;
745
746
747
748
749
750 public RowCollapseAnimation(List<VScrollTableRow> rows) {
751 super(rows);
752 this.rows = rows;
753 }
754
755 @Override
756 protected String getInitialHeight() {
757 return getRowHeight() + "px";
758 }
759
760 @Override
761 protected double getBaseOffset() {
762 return getRowHeight();
763 }
764
765 @Override
766 protected double calculateHeightOfAllVisibleLines(double progress,
767 double rh) {
768 return rows.size() * rh * (1 - progress);
769 }
770
771 @Override
772 protected double calculateDivOffset(double progress, double rh) {
773 return -super.calculateDivOffset(progress, rh);
774 }
775 }
776 }
777
778
779
780
781
782 @Override
783 protected String buildCaptionHtmlSnippet(UIDL uidl) {
784 if (uidl.getTag().equals("column")) {
785 return super.buildCaptionHtmlSnippet(uidl);
786 } else {
787 String s = uidl.getStringAttribute("caption");
788 return s;
789 }
790 }
791
792
793 @Override
794 public boolean handleNavigation(int keycode, boolean ctrl, boolean shift) {
795 if (collapseRequest || focusParentResponsePending) {
796
797
798 if (pendingNavigationEvents.size() < 10) {
799
800 PendingNavigationEvent pendingNavigationEvent = new PendingNavigationEvent(
801 keycode, ctrl, shift);
802 pendingNavigationEvents.add(pendingNavigationEvent);
803 }
804 return true;
805 }
806
807 VTreeTableRow focusedRow = (VTreeTableRow) getFocusedRow();
808 if (focusedRow != null) {
809 if (focusedRow.canHaveChildren
810 && ((keycode == KeyCodes.KEY_RIGHT && !focusedRow.open) || (keycode == KeyCodes.KEY_LEFT && focusedRow.open))) {
811 if (!ctrl) {
812 client.updateVariable(paintableId, "selectCollapsed", true,
813 false);
814 }
815 sendSelectedRows(false);
816 sendToggleCollapsedUpdate(focusedRow.getKey());
817 return true;
818 } else if (keycode == KeyCodes.KEY_RIGHT && focusedRow.open) {
819
820
821 VTreeTableScrollBody body = (VTreeTableScrollBody) focusedRow
822 .getParent();
823 Iterator<Widget> iterator = body.iterator();
824 VTreeTableRow next = null;
825 while (iterator.hasNext()) {
826 next = (VTreeTableRow) iterator.next();
827 if (next == focusedRow) {
828 next = (VTreeTableRow) iterator.next();
829 break;
830 }
831 }
832 if (next != null) {
833 if (next.depth > focusedRow.depth) {
834 selectionPending = true;
835 return super.handleNavigation(getNavigationDownKey(),
836 ctrl, shift);
837 }
838 } else {
839
840
841
842 selectionPending = true;
843 return super.handleNavigation(getNavigationDownKey(), ctrl,
844 shift);
845 }
846 } else if (keycode == KeyCodes.KEY_LEFT) {
847
848
849
850
851
852 client.updateVariable(paintableId, "focusParent",
853 focusedRow.getKey(), true);
854
855
856
857 focusParentResponsePending = true;
858
859 return true;
860 }
861 }
862 return super.handleNavigation(keycode, ctrl, shift);
863 }
864
865 private void sendToggleCollapsedUpdate(String rowKey) {
866 collapsedRowKey = rowKey;
867 collapseRequest = true;
868 client.updateVariable(paintableId, "toggleCollapsed", rowKey, true);
869 }
870
871 @Override
872 public void onBrowserEvent(Event event) {
873 super.onBrowserEvent(event);
874 if (event.getTypeInt() == Event.ONKEYUP && selectionPending) {
875 sendSelectedRows();
876 }
877 }
878
879 @Override
880 protected void sendSelectedRows(boolean immediately) {
881 super.sendSelectedRows(immediately);
882 selectionPending = false;
883 }
884
885 @Override
886 protected void reOrderColumn(String columnKey, int newIndex) {
887 super.reOrderColumn(columnKey, newIndex);
888
889
890 client.sendPendingVariableChanges();
891 }
892
893 @Override
894 public void setStyleName(String style) {
895 super.setStyleName(style + " v-treetable");
896 }
897
898 @Override
899 public void updateTotalRows(UIDL uidl) {
900
901
902 int newTotalRows = uidl.getIntAttribute("totalrows");
903 setTotalRows(newTotalRows);
904 }
905
906 }