1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.vaadin.v7.client.ui;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.logging.Level;
30 import java.util.logging.Logger;
31
32 import com.google.gwt.core.client.JavaScriptObject;
33 import com.google.gwt.core.client.Scheduler;
34 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
35 import com.google.gwt.dom.client.Document;
36 import com.google.gwt.dom.client.Element;
37 import com.google.gwt.dom.client.NativeEvent;
38 import com.google.gwt.dom.client.Node;
39 import com.google.gwt.dom.client.NodeList;
40 import com.google.gwt.dom.client.Style;
41 import com.google.gwt.dom.client.Style.Display;
42 import com.google.gwt.dom.client.Style.Overflow;
43 import com.google.gwt.dom.client.Style.Position;
44 import com.google.gwt.dom.client.Style.TextAlign;
45 import com.google.gwt.dom.client.Style.Unit;
46 import com.google.gwt.dom.client.Style.Visibility;
47 import com.google.gwt.dom.client.TableCellElement;
48 import com.google.gwt.dom.client.TableRowElement;
49 import com.google.gwt.dom.client.TableSectionElement;
50 import com.google.gwt.dom.client.Touch;
51 import com.google.gwt.event.dom.client.ContextMenuEvent;
52 import com.google.gwt.event.dom.client.ContextMenuHandler;
53 import com.google.gwt.event.dom.client.BlurEvent;
54 import com.google.gwt.event.dom.client.BlurHandler;
55 import com.google.gwt.event.dom.client.FocusEvent;
56 import com.google.gwt.event.dom.client.FocusHandler;
57 import com.google.gwt.event.dom.client.KeyCodes;
58 import com.google.gwt.event.dom.client.KeyDownEvent;
59 import com.google.gwt.event.dom.client.KeyDownHandler;
60 import com.google.gwt.event.dom.client.KeyPressEvent;
61 import com.google.gwt.event.dom.client.KeyPressHandler;
62 import com.google.gwt.event.dom.client.KeyUpEvent;
63 import com.google.gwt.event.dom.client.KeyUpHandler;
64 import com.google.gwt.event.dom.client.ScrollEvent;
65 import com.google.gwt.event.dom.client.ScrollHandler;
66 import com.google.gwt.event.logical.shared.CloseEvent;
67 import com.google.gwt.event.logical.shared.CloseHandler;
68 import com.google.gwt.event.shared.HandlerRegistration;
69 import com.google.gwt.regexp.shared.MatchResult;
70 import com.google.gwt.regexp.shared.RegExp;
71 import com.google.gwt.user.client.Command;
72 import com.google.gwt.user.client.DOM;
73 import com.google.gwt.user.client.Event;
74 import com.google.gwt.user.client.Event.NativePreviewEvent;
75 import com.google.gwt.user.client.Event.NativePreviewHandler;
76 import com.google.gwt.user.client.Timer;
77 import com.google.gwt.user.client.Window;
78 import com.google.gwt.user.client.ui.FlowPanel;
79 import com.google.gwt.user.client.ui.Panel;
80 import com.google.gwt.user.client.ui.RootPanel;
81 import com.google.gwt.user.client.ui.PopupPanel;
82 import com.google.gwt.user.client.ui.UIObject;
83 import com.google.gwt.user.client.ui.Widget;
84 import com.vaadin.client.ApplicationConnection;
85 import com.vaadin.client.BrowserInfo;
86 import com.vaadin.client.ComponentConnector;
87 import com.vaadin.client.ConnectorMap;
88 import com.vaadin.client.DeferredWorker;
89 import com.vaadin.client.Focusable;
90 import com.vaadin.client.HasChildMeasurementHintConnector.ChildMeasurementHint;
91 import com.vaadin.client.MouseEventDetailsBuilder;
92 import com.vaadin.client.StyleConstants;
93 import com.vaadin.client.TooltipInfo;
94 import com.vaadin.client.UIDL;
95 import com.vaadin.client.Util;
96 import com.vaadin.client.VTooltip;
97 import com.vaadin.client.WidgetUtil;
98 import com.vaadin.client.ui.Action;
99 import com.vaadin.client.ui.ActionOwner;
100 import com.vaadin.client.ui.FocusableScrollPanel;
101 import com.vaadin.client.ui.Icon;
102 import com.vaadin.client.ui.SubPartAware;
103 import com.vaadin.client.ui.TouchScrollDelegate;
104 import com.vaadin.client.ui.TreeAction;
105 import com.vaadin.client.ui.VContextMenu;
106 import com.vaadin.client.ui.VEmbedded;
107 import com.vaadin.client.ui.VOverlay;
108 import com.vaadin.client.ui.dd.DDUtil;
109 import com.vaadin.client.ui.dd.VAbstractDropHandler;
110 import com.vaadin.client.ui.dd.VAcceptCallback;
111 import com.vaadin.client.ui.dd.VDragAndDropManager;
112 import com.vaadin.client.ui.dd.VDragEvent;
113 import com.vaadin.client.ui.dd.VHasDropHandler;
114 import com.vaadin.client.ui.dd.VTransferable;
115 import com.vaadin.shared.AbstractComponentState;
116 import com.vaadin.shared.MouseEventDetails;
117 import com.vaadin.shared.ui.dd.VerticalDropLocation;
118 import com.vaadin.v7.client.ui.VScrollTablePatched.VScrollTableBody.VScrollTableRow;
119 import com.vaadin.v7.shared.ui.table.CollapseMenuContent;
120 import com.vaadin.v7.shared.ui.table.TableConstants;
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public class VScrollTablePatched extends FlowPanel
146 implements ScrollHandler, VHasDropHandler, FocusHandler,
147 BlurHandler, Focusable, ActionOwner, SubPartAware, DeferredWorker {
148
149 protected TableHead createTableHead() {
150 return new TableHead();
151 }
152
153 protected HeaderCell createHeaderCell(String colId, String headerText) {
154 return new HeaderCell(colId, headerText);
155 }
156
157 public boolean isVisibleInHierarchy() {
158 boolean isVisible = isVisible();
159 Widget current = getParent();
160 while (isVisible && current != null) {
161 isVisible &= current.isVisible();
162 current = current.getParent();
163 }
164 return isVisible;
165 }
166
167
168
169
170
171
172
173 private interface ContextMenuOwner {
174 public void showContextMenu(Event event);
175 }
176
177
178
179
180
181
182
183 private class TouchContextProvider {
184 private static final int TOUCH_CONTEXT_MENU_TIMEOUT = 500;
185 private Timer contextTouchTimeout;
186
187 private Event touchStart;
188 private int touchStartY;
189 private int touchStartX;
190
191 private ContextMenuOwner target;
192
193
194
195
196
197
198
199 public TouchContextProvider(ContextMenuOwner target) {
200 this.target = target;
201 }
202
203
204
205
206 public void cancel() {
207 if (contextTouchTimeout != null) {
208 contextTouchTimeout.cancel();
209 contextTouchTimeout = null;
210 }
211 touchStart = null;
212 }
213
214
215
216
217
218
219
220 public void handleTouchEvent(final Event event) {
221 int type = event.getTypeInt();
222
223 switch (type) {
224 case Event.ONCONTEXTMENU:
225 target.showContextMenu(event);
226 break;
227 case Event.ONTOUCHSTART:
228
229
230 touchStart = event;
231
232 Touch touch = event.getChangedTouches().get(0);
233 touchStartX = touch.getClientX();
234 touchStartY = touch.getClientY();
235
236 if (contextTouchTimeout == null) {
237 contextTouchTimeout = new Timer() {
238
239 @Override
240 public void run() {
241 if (touchStart != null) {
242
243
244 target.showContextMenu(touchStart);
245 event.preventDefault();
246 touchStart = null;
247 }
248 }
249 };
250 }
251 contextTouchTimeout.schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
252 break;
253 case Event.ONTOUCHCANCEL:
254 case Event.ONTOUCHEND:
255 cancel();
256 break;
257 case Event.ONTOUCHMOVE:
258 if (isSignificantMove(event)) {
259
260
261 cancel();
262 }
263 }
264 }
265
266
267
268
269
270
271
272
273
274
275 protected boolean isSignificantMove(Event event) {
276 if (touchStart == null) {
277
278 return false;
279 }
280
281
282
283 Touch touch = event.getChangedTouches().get(0);
284 int deltaX = touch.getClientX() - touchStartX;
285 int deltaY = touch.getClientY() - touchStartY;
286 int delta = deltaX * deltaX + deltaY * deltaY;
287
288
289
290 if (delta > TouchScrollDelegate.SIGNIFICANT_MOVE_THRESHOLD
291 * TouchScrollDelegate.SIGNIFICANT_MOVE_THRESHOLD) {
292 return true;
293 }
294 return false;
295 }
296 }
297
298 public static final String STYLENAME = "v-table";
299
300 public enum SelectMode {
301 NONE(0), SINGLE(1), MULTI(2);
302 private int id;
303
304 private SelectMode(int id) {
305 this.id = id;
306 }
307
308 public int getId() {
309 return id;
310 }
311 }
312
313 private static final String ROW_HEADER_COLUMN_KEY = "0";
314
315 private static final double CACHE_RATE_DEFAULT = 2;
316
317
318
319
320
321
322 private static final int MULTISELECT_MODE_DEFAULT = 0;
323
324
325
326
327
328
329
330 private static final int MULTISELECT_MODE_SIMPLE = 1;
331
332
333
334
335
336 private double cacheRate = CACHE_RATE_DEFAULT;
337
338
339
340 private double cacheReactRate = 0.75 * cacheRate;
341
342 public static final char ALIGN_CENTER = 'c';
343 public static final char ALIGN_LEFT = 'b';
344 public static final char ALIGN_RIGHT = 'e';
345 private static final int CHARCODE_SPACE = 32;
346 private int firstRowInViewPort = 0;
347 private int pageLength = 15;
348
349 private int lastRequestedFirstvisible = 0;
350
351
352 private int firstvisibleOnLastPage = -1;
353
354
355 public boolean showRowHeaders = false;
356
357 private String[] columnOrder;
358
359 protected ApplicationConnection client;
360
361
362 public String paintableId;
363
364
365 public boolean immediate;
366
367 private boolean updatedReqRows = true;
368
369 private boolean nullSelectionAllowed = true;
370
371 private SelectMode selectMode = SelectMode.NONE;
372
373 public final HashSet<String> selectedRowKeys = new HashSet<String>();
374
375
376
377
378
379 private HashSet<Object> unSyncedselectionsBeforeRowFetch;
380
381
382
383
384
385
386 public boolean selectLastItemInNextRender = false;
387
388 public boolean selectFirstItemInNextRender = false;
389
390 public boolean focusFirstItemInNextRender = false;
391
392 public boolean focusLastItemInNextRender = false;
393
394
395
396
397
398
399 public VScrollTableRow focusedRow;
400
401
402
403
404
405
406 public VScrollTableRow selectionRangeStart;
407
408
409
410
411
412
413
414 public boolean selectionChanged = false;
415
416
417
418
419 private int scrollingVelocity = 10;
420
421 private Timer scrollingVelocityTimer = null;
422
423
424 public String[] bodyActionKeys;
425
426 private boolean enableDebug = false;
427
428 private static final boolean HAS_NATIVE_TOUCH_SCROLLLING = BrowserInfo.get()
429 .isTouchDevice()
430 && !BrowserInfo.get().requiresTouchScrollDelegate();
431
432 private Set<String> noncollapsibleColumns;
433
434
435
436
437
438
439
440
441
442
443
444
445 private double lastKnownRowHeight = Double.NaN;
446
447
448
449
450
451 private int detachedScrollPosition = 0;
452
453
454
455
456 private class SelectionRange {
457 private VScrollTableRow startRow;
458 private final int length;
459
460
461
462
463 public SelectionRange(VScrollTableRow row1, VScrollTableRow row2) {
464 VScrollTableRow endRow;
465 if (row2.isBefore(row1)) {
466 startRow = row2;
467 endRow = row1;
468 } else {
469 startRow = row1;
470 endRow = row2;
471 }
472 length = endRow.getIndex() - startRow.getIndex() + 1;
473 }
474
475 public SelectionRange(VScrollTableRow row, int length) {
476 startRow = row;
477 this.length = length;
478 }
479
480
481
482
483
484
485
486 @Override
487 public String toString() {
488 return startRow.getKey() + "-" + length;
489 }
490
491 private boolean inRange(VScrollTableRow row) {
492 return row.getIndex() >= startRow.getIndex()
493 && row.getIndex() < startRow.getIndex() + length;
494 }
495
496 public Collection<SelectionRange> split(VScrollTableRow row) {
497 assert row.isAttached();
498 List<SelectionRange> ranges = new ArrayList<SelectionRange>(2);
499
500 int endOfFirstRange = row.getIndex() - 1;
501 if (endOfFirstRange >= startRow.getIndex()) {
502
503 ranges.add(new SelectionRange(startRow,
504 endOfFirstRange - startRow.getIndex() + 1));
505 }
506 int startOfSecondRange = row.getIndex() + 1;
507 if (getEndIndex() >= startOfSecondRange) {
508
509 VScrollTableRow startOfRange = scrollBody
510 .getRowByRowIndex(startOfSecondRange);
511 if (startOfRange != null) {
512 ranges.add(new SelectionRange(startOfRange,
513 getEndIndex() - startOfSecondRange + 1));
514 }
515 }
516 return ranges;
517 }
518
519 private int getEndIndex() {
520 return startRow.getIndex() + length - 1;
521 }
522
523 }
524
525 private final HashSet<SelectionRange> selectedRowRanges = new HashSet<SelectionRange>();
526
527
528 public boolean initializedAndAttached = false;
529
530
531
532
533
534
535 public boolean headerChangedDuringUpdate = false;
536
537
538 public final TableHead tHead = createTableHead();
539
540
541 public final TableFooter tFoot = new TableFooter();
542
543
544 private ContextMenuOwner contextMenuOwner = new ContextMenuOwner() {
545
546 @Override
547 public void showContextMenu(Event event) {
548 int left = WidgetUtil.getTouchOrMouseClientX(event);
549 int top = WidgetUtil.getTouchOrMouseClientY(event);
550 boolean menuShown = handleBodyContextMenu(left, top);
551 if (menuShown) {
552 event.stopPropagation();
553 event.preventDefault();
554 }
555 }
556 };
557
558
559 private TouchContextProvider touchContextProvider = new TouchContextProvider(
560 contextMenuOwner);
561
562
563
564
565
566
567
568
569
570
571
572 public class FocusableScrollContextPanel extends FocusableScrollPanel {
573 @Override
574 public void onBrowserEvent(Event event) {
575 super.onBrowserEvent(event);
576 touchContextProvider.handleTouchEvent(event);
577 };
578
579 public FocusableScrollContextPanel(boolean useFakeFocusElement) {
580 super(useFakeFocusElement);
581 }
582 }
583
584
585 public final FocusableScrollContextPanel scrollBodyPanel = new FocusableScrollContextPanel(
586 true);
587
588 private KeyPressHandler navKeyPressHandler = new KeyPressHandler() {
589
590 @Override
591 public void onKeyPress(KeyPressEvent keyPressEvent) {
592
593
594
595 if (!BrowserInfo.get().isGecko()) {
596 return;
597 }
598
599 NativeEvent event = keyPressEvent.getNativeEvent();
600 if (!enabled) {
601
602
603 event.preventDefault();
604 } else if (hasFocus()) {
605
606
607 int keyCode = event.getKeyCode();
608 if (keyCode == 0 && event.getCharCode() == ' ') {
609
610
611 keyCode = CHARCODE_SPACE;
612 }
613
614 if (handleNavigation(keyCode,
615 event.getCtrlKey() || event.getMetaKey(),
616 event.getShiftKey())) {
617 event.preventDefault();
618 }
619
620 startScrollingVelocityTimer();
621 }
622 }
623
624 };
625
626 private KeyUpHandler navKeyUpHandler = new KeyUpHandler() {
627
628 @Override
629 public void onKeyUp(KeyUpEvent keyUpEvent) {
630 NativeEvent event = keyUpEvent.getNativeEvent();
631 int keyCode = event.getKeyCode();
632
633 if (!isFocusable()) {
634 cancelScrollingVelocityTimer();
635 } else if (isNavigationKey(keyCode)) {
636 if (keyCode == getNavigationDownKey()
637 || keyCode == getNavigationUpKey()) {
638
639
640
641
642
643 if (!event.getShiftKey() && !event.getCtrlKey()) {
644 instructServerToForgetPreviousSelections();
645 }
646 sendSelectedRows();
647 }
648 cancelScrollingVelocityTimer();
649 navKeyDown = false;
650 }
651 }
652 };
653
654 private KeyDownHandler navKeyDownHandler = new KeyDownHandler() {
655
656 @Override
657 public void onKeyDown(KeyDownEvent keyDownEvent) {
658 NativeEvent event = keyDownEvent.getNativeEvent();
659
660 if (BrowserInfo.get().isGecko()) {
661 return;
662 }
663
664 if (!enabled) {
665
666
667 event.preventDefault();
668 } else if (hasFocus()) {
669 if (handleNavigation(event.getKeyCode(),
670 event.getCtrlKey() || event.getMetaKey(),
671 event.getShiftKey())) {
672 navKeyDown = true;
673 event.preventDefault();
674 }
675
676 startScrollingVelocityTimer();
677 }
678 }
679 };
680
681
682 public int totalRows;
683
684 private Set<String> collapsedColumns;
685
686
687 public final RowRequestHandler rowRequestHandler;
688
689
690 public VScrollTableBody scrollBody;
691
692 private int firstvisible = 0;
693 private boolean sortAscending;
694 private String sortColumn;
695 private String oldSortColumn;
696 private boolean columnReordering;
697
698
699
700
701
702 private final Map<Object, String> actionMap = new HashMap<Object, String>();
703 private String[] visibleColOrder;
704 private boolean initialContentReceived = false;
705 private Element scrollPositionElement;
706
707
708 public boolean enabled;
709
710
711 public boolean showColHeaders;
712
713
714 public boolean showColFooters;
715
716
717 private boolean isNewBody = true;
718
719
720
721
722
723
724
725
726
727 public boolean recalcWidths = false;
728
729
730 public boolean rendering = false;
731
732 private boolean hasFocus = false;
733 private int dragmode;
734
735 protected int multiselectmode;
736
737
738
739
740 private ChildMeasurementHint childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS;
741
742
743 public int tabIndex;
744
745 private TouchScrollDelegate touchScrollDelegate;
746
747
748 public int lastRenderedHeight;
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764 public int serverCacheFirst = -1;
765 public int serverCacheLast = -1;
766
767
768
769
770
771
772 public boolean postponeSanityCheckForLastRendered;
773
774
775 public boolean sizeNeedsInit = true;
776
777
778
779
780
781
782
783 public class ContextMenuDetails implements CloseHandler<PopupPanel> {
784 public String rowKey;
785 public int left;
786 public int top;
787 HandlerRegistration closeRegistration;
788
789 public ContextMenuDetails(VContextMenu menu, String rowKey, int left,
790 int top) {
791 this.rowKey = rowKey;
792 this.left = left;
793 this.top = top;
794 closeRegistration = menu.addCloseHandler(this);
795 }
796
797 @Override
798 public void onClose(CloseEvent<PopupPanel> event) {
799 contextMenu = null;
800 closeRegistration.removeHandler();
801 }
802 }
803
804
805 public ContextMenuDetails contextMenu = null;
806
807 private boolean hadScrollBars = false;
808
809 private HandlerRegistration addCloseHandler;
810
811
812
813
814
815
816
817 private Element lastMouseDownTarget;
818
819
820
821
822
823 private boolean mouseUpPreviewMatched = false;
824
825 private HandlerRegistration mouseUpEventPreviewRegistration;
826
827
828
829
830
831 private final NativePreviewHandler mouseUpPreviewHandler = new NativePreviewHandler() {
832
833 @Override
834 public void onPreviewNativeEvent(NativePreviewEvent event) {
835 if (event.getTypeInt() == Event.ONMOUSEUP) {
836 mouseUpEventPreviewRegistration.removeHandler();
837
838
839
840 Element elementUnderMouse = WidgetUtil
841 .getElementUnderMouse(event.getNativeEvent());
842 if (lastMouseDownTarget != null && lastMouseDownTarget
843 .isOrHasChild(elementUnderMouse)) {
844 mouseUpPreviewMatched = true;
845 } else {
846 getLogger().log(Level.FINEST,
847 "Ignoring mouseup from " + elementUnderMouse
848 + " when mousedown was on "
849 + lastMouseDownTarget);
850 }
851 }
852 }
853 };
854
855 public VScrollTablePatched() {
856 setMultiSelectMode(MULTISELECT_MODE_DEFAULT);
857
858 scrollBodyPanel.addFocusHandler(this);
859 scrollBodyPanel.addBlurHandler(this);
860
861 scrollBodyPanel.addScrollHandler(this);
862
863
864
865
866
867
868 if (BrowserInfo.get().isGecko()) {
869 scrollBodyPanel.addKeyPressHandler(navKeyPressHandler);
870 } else {
871 scrollBodyPanel.addKeyDownHandler(navKeyDownHandler);
872 }
873 scrollBodyPanel.addKeyUpHandler(navKeyUpHandler);
874
875 scrollBodyPanel.sinkEvents(Event.TOUCHEVENTS | Event.ONCONTEXTMENU);
876
877 setStyleName(STYLENAME);
878
879 add(tHead);
880 add(scrollBodyPanel);
881 add(tFoot);
882
883 rowRequestHandler = new RowRequestHandler();
884 }
885
886 @Override
887 public void setStyleName(String style) {
888 updateStyleNames(style, false);
889 }
890
891 @Override
892 public void setStylePrimaryName(String style) {
893 updateStyleNames(style, true);
894 }
895
896 private void updateStyleNames(String newStyle, boolean isPrimary) {
897 scrollBodyPanel
898 .removeStyleName(getStylePrimaryName() + "-body-wrapper");
899 scrollBodyPanel.removeStyleName(getStylePrimaryName() + "-body");
900
901 if (scrollBody != null) {
902 scrollBody.removeStyleName(
903 getStylePrimaryName() + "-body-noselection");
904 }
905
906 if (isPrimary) {
907 super.setStylePrimaryName(newStyle);
908 } else {
909 super.setStyleName(newStyle);
910 }
911
912 scrollBodyPanel.addStyleName(getStylePrimaryName() + "-body-wrapper");
913 scrollBodyPanel.addStyleName(getStylePrimaryName() + "-body");
914
915 tHead.updateStyleNames(getStylePrimaryName());
916 tFoot.updateStyleNames(getStylePrimaryName());
917
918 if (scrollBody != null) {
919 scrollBody.updateStyleNames(getStylePrimaryName());
920 }
921 }
922
923 public void init(ApplicationConnection client) {
924 this.client = client;
925
926
927 addCloseHandler = client.getContextMenu()
928 .addCloseHandler(new CloseHandler<PopupPanel>() {
929
930 @Override
931 public void onClose(CloseEvent<PopupPanel> event) {
932 contextMenu = null;
933 }
934 });
935 }
936
937
938
939
940
941
942
943
944
945
946 private boolean handleBodyContextMenu(int left, int top) {
947 if (enabled && bodyActionKeys != null) {
948 top += Window.getScrollTop();
949 left += Window.getScrollLeft();
950 client.getContextMenu().showAt(this, left, top);
951 return true;
952 }
953 return false;
954 }
955
956
957
958
959
960
961
962
963
964
965
966
967 private void fireColumnResizeEvent(String columnId, int originalWidth,
968 int newWidth) {
969 client.updateVariable(paintableId, "columnResizeEventColumn", columnId,
970 false);
971 client.updateVariable(paintableId, "columnResizeEventPrev",
972 originalWidth, false);
973 client.updateVariable(paintableId, "columnResizeEventCurr", newWidth,
974 immediate);
975
976 }
977
978
979
980
981
982
983
984
985 private void sendColumnWidthUpdates(Collection<HeaderCell> columns) {
986 String[] newSizes = new String[columns.size()];
987 int ix = 0;
988 for (HeaderCell cell : columns) {
989 newSizes[ix++] = cell.getColKey() + ":" + cell.getWidth();
990 }
991 client.updateVariable(paintableId, "columnWidthUpdates", newSizes,
992 false);
993 }
994
995
996
997
998
999
1000 private boolean moveFocusDown() {
1001 return moveFocusDown(0);
1002 }
1003
1004
1005
1006
1007
1008
1009
1010 private boolean moveFocusDown(int offset) {
1011 if (isSelectable()) {
1012 if (focusedRow == null && scrollBody.iterator().hasNext()) {
1013
1014
1015 return setRowFocus(
1016 (VScrollTableRow) scrollBody.iterator().next());
1017 } else {
1018 VScrollTableRow next = getNextRow(focusedRow, offset);
1019 if (next != null) {
1020 return setRowFocus(next);
1021 }
1022 }
1023 }
1024
1025 return false;
1026 }
1027
1028
1029
1030
1031
1032
1033 private boolean moveFocusUp() {
1034 return moveFocusUp(0);
1035 }
1036
1037
1038
1039
1040
1041
1042
1043
1044 private boolean moveFocusUp(int offset) {
1045 if (isSelectable()) {
1046 if (focusedRow == null && scrollBody.iterator().hasNext()) {
1047
1048
1049 return setRowFocus(
1050 (VScrollTableRow) scrollBody.iterator().next());
1051 } else {
1052 VScrollTableRow prev = getPreviousRow(focusedRow, offset);
1053 if (prev != null) {
1054 return setRowFocus(prev);
1055 } else {
1056 getLogger().info("no previous available");
1057 }
1058 }
1059 }
1060
1061 return false;
1062 }
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 private void selectFocusedRow(boolean ctrlSelect, boolean shiftSelect) {
1074 if (focusedRow != null) {
1075
1076 if (isSelectable() && !ctrlSelect && !shiftSelect) {
1077 deselectAll();
1078 focusedRow.toggleSelection();
1079 selectionRangeStart = focusedRow;
1080 } else if (isSelectable() && ctrlSelect && !shiftSelect) {
1081
1082 selectionRangeStart = focusedRow;
1083
1084 } else if (isMultiSelectModeAny() && !ctrlSelect && shiftSelect) {
1085
1086 focusedRow.toggleShiftSelection(shiftSelect);
1087 }
1088 }
1089 }
1090
1091
1092
1093
1094 protected void sendSelectedRows() {
1095 sendSelectedRows(immediate);
1096 }
1097
1098 private void updateFirstVisibleAndSendSelectedRows() {
1099 updateFirstVisibleRow();
1100 sendSelectedRows(immediate);
1101 }
1102
1103
1104
1105
1106
1107
1108
1109
1110 protected void sendSelectedRows(boolean immediately) {
1111
1112 if (!selectionChanged) {
1113 return;
1114 }
1115
1116
1117 selectionChanged = false;
1118
1119
1120
1121 if (isMultiSelectModeDefault()) {
1122
1123 Set<String> ranges = new HashSet<String>();
1124 for (SelectionRange range : selectedRowRanges) {
1125 ranges.add(range.toString());
1126 }
1127
1128
1129 client.updateVariable(paintableId, "selectedRanges",
1130 ranges.toArray(new String[selectedRowRanges.size()]),
1131 false);
1132 selectedRowRanges.clear();
1133
1134
1135 for (Iterator<String> iterator = selectedRowKeys
1136 .iterator(); iterator.hasNext();) {
1137 String key = iterator.next();
1138 VScrollTableRow renderedRowByKey = getRenderedRowByKey(key);
1139 if (renderedRowByKey != null) {
1140 for (SelectionRange range : selectedRowRanges) {
1141 if (range.inRange(renderedRowByKey)) {
1142 iterator.remove();
1143 }
1144 }
1145 } else {
1146
1147 iterator.remove();
1148 }
1149
1150 }
1151 }
1152
1153
1154 client.updateVariable(paintableId, "selected",
1155 selectedRowKeys.toArray(new String[selectedRowKeys.size()]),
1156 false);
1157
1158 if (immediately) {
1159 selectionSynchronizer.schedule(500);
1160 }
1161 }
1162
1163 private Timer selectionSynchronizer = new Timer() {
1164 @Override
1165 public void run() {
1166 client.sendPendingVariableChanges();
1167 }
1168 };
1169
1170
1171
1172
1173
1174
1175
1176
1177 protected int getNavigationUpKey() {
1178 return KeyCodes.KEY_UP;
1179 }
1180
1181
1182
1183
1184
1185
1186
1187
1188 protected int getNavigationDownKey() {
1189 return KeyCodes.KEY_DOWN;
1190 }
1191
1192
1193
1194
1195
1196
1197
1198
1199 protected int getNavigationLeftKey() {
1200 return KeyCodes.KEY_LEFT;
1201 }
1202
1203
1204
1205
1206
1207
1208
1209
1210 protected int getNavigationRightKey() {
1211 return KeyCodes.KEY_RIGHT;
1212 }
1213
1214
1215
1216
1217
1218
1219
1220
1221 protected int getNavigationSelectKey() {
1222 return CHARCODE_SPACE;
1223 }
1224
1225
1226
1227
1228
1229
1230
1231
1232 protected int getNavigationPageUpKey() {
1233 return KeyCodes.KEY_PAGEUP;
1234 }
1235
1236
1237
1238
1239
1240
1241
1242
1243 protected int getNavigationPageDownKey() {
1244 return KeyCodes.KEY_PAGEDOWN;
1245 }
1246
1247
1248
1249
1250
1251
1252
1253
1254 protected int getNavigationStartKey() {
1255 return KeyCodes.KEY_HOME;
1256 }
1257
1258
1259
1260
1261
1262
1263
1264
1265 protected int getNavigationEndKey() {
1266 return KeyCodes.KEY_END;
1267 }
1268
1269
1270 public void initializeRows(UIDL uidl, UIDL rowData) {
1271 if (scrollBody != null) {
1272 scrollBody.removeFromParent();
1273 }
1274
1275
1276
1277
1278 int pos = scrollBodyPanel.getScrollPosition();
1279
1280
1281 if (pos == 0) {
1282 firstRowInViewPort = 0;
1283 }
1284
1285 scrollBody = createScrollBody();
1286
1287 scrollBody.renderInitialRows(rowData, uidl.getIntAttribute("firstrow"),
1288 uidl.getIntAttribute("rows"));
1289 scrollBodyPanel.add(scrollBody);
1290
1291 initialContentReceived = true;
1292 sizeNeedsInit = true;
1293 scrollBody.restoreRowVisibility();
1294
1295
1296 if (scrollBodyPanel != null) {
1297 VScrollTableRow row;
1298 if (selectedRowKeys.size() == 1) {
1299 String selectedRowKey = selectedRowKeys.iterator().next();
1300 row = getRenderedRowByKey(selectedRowKey);
1301 } else {
1302 row = (VScrollTableRow) scrollBody.iterator().next();
1303 }
1304 lazyRevertFocusToRow(row);
1305 }
1306 }
1307
1308
1309 public void updateColumnProperties(UIDL uidl) {
1310 updateColumnOrder(uidl);
1311
1312 updateCollapsedColumns(uidl);
1313
1314 UIDL vc = uidl.getChildByTagName("visiblecolumns");
1315 if (vc != null) {
1316 tHead.updateCellsFromUIDL(vc);
1317 tFoot.updateCellsFromUIDL(vc);
1318 }
1319
1320 updateHeader(uidl.getStringArrayAttribute("vcolorder"));
1321 updateFooter(uidl.getStringArrayAttribute("vcolorder"));
1322 if (uidl.hasVariable("noncollapsiblecolumns")) {
1323 noncollapsibleColumns = uidl
1324 .getStringArrayVariableAsSet("noncollapsiblecolumns");
1325 }
1326 }
1327
1328 private void updateCollapsedColumns(UIDL uidl) {
1329 if (uidl.hasVariable("collapsedcolumns")) {
1330 tHead.setColumnCollapsingAllowed(true);
1331 collapsedColumns = uidl
1332 .getStringArrayVariableAsSet("collapsedcolumns");
1333 } else {
1334 tHead.setColumnCollapsingAllowed(false);
1335 }
1336 }
1337
1338 private void updateColumnOrder(UIDL uidl) {
1339 if (uidl.hasVariable("columnorder")) {
1340 columnReordering = true;
1341 columnOrder = uidl.getStringArrayVariable("columnorder");
1342 } else {
1343 columnReordering = false;
1344 columnOrder = null;
1345 }
1346 }
1347
1348
1349 public boolean selectSelectedRows(UIDL uidl) {
1350 boolean keyboardSelectionOverRowFetchInProgress = false;
1351
1352 if (uidl.hasVariable("selected")) {
1353 final Set<String> selectedKeys = uidl
1354 .getStringArrayVariableAsSet("selected");
1355
1356
1357
1358 boolean rowsUnSelected = removeUnselectedRowKeys(selectedKeys);
1359 boolean updateFocus = rowsUnSelected || selectedRowKeys.isEmpty()
1360 || focusedRow == null;
1361 if (scrollBody != null) {
1362 for (Widget w : scrollBody) {
1363
1364
1365
1366
1367 VScrollTableRow row = (VScrollTableRow) w;
1368 boolean selected = selectedKeys.contains(row.getKey());
1369 if (!selected && unSyncedselectionsBeforeRowFetch != null
1370 && unSyncedselectionsBeforeRowFetch
1371 .contains(row.getKey())) {
1372 selected = true;
1373 keyboardSelectionOverRowFetchInProgress = true;
1374 }
1375 if (selected && selectedKeys.size() == 1 && updateFocus) {
1376
1377
1378
1379
1380 setRowFocus(row);
1381 }
1382
1383 if (selected != row.isSelected()) {
1384 row.toggleSelection();
1385
1386 if (!isSingleSelectMode() && !selected) {
1387
1388
1389 removeRowFromUnsentSelectionRanges(row);
1390 }
1391 }
1392 }
1393
1394 }
1395 }
1396 unSyncedselectionsBeforeRowFetch = null;
1397 return keyboardSelectionOverRowFetchInProgress;
1398 }
1399
1400 private boolean removeUnselectedRowKeys(final Set<String> selectedKeys) {
1401 List<String> unselectedKeys = new ArrayList<String>(0);
1402 for (String key : selectedRowKeys) {
1403 if (!selectedKeys.contains(key)) {
1404 unselectedKeys.add(key);
1405 }
1406 }
1407 return selectedRowKeys.removeAll(unselectedKeys);
1408 }
1409
1410
1411 public void updateSortingProperties(UIDL uidl) {
1412 oldSortColumn = sortColumn;
1413 if (uidl.hasVariable("sortascending")) {
1414 sortAscending = uidl.getBooleanVariable("sortascending");
1415 sortColumn = uidl.getStringVariable("sortcolumn");
1416 }
1417 }
1418
1419
1420 public void resizeSortedColumnForSortIndicator() {
1421
1422
1423 HeaderCell sortedHeader = tHead.getHeaderCell(sortColumn);
1424 if (sortedHeader != null) {
1425
1426
1427 sortedHeader.setSorted(true);
1428 tHead.resizeCaptionContainer(sortedHeader);
1429 }
1430
1431
1432 HeaderCell oldSortedHeader = tHead.getHeaderCell(oldSortColumn);
1433 if (oldSortedHeader != null) {
1434 tHead.resizeCaptionContainer(oldSortedHeader);
1435 }
1436 }
1437
1438 private boolean lazyScrollerIsActive;
1439
1440 private void disableLazyScroller() {
1441 lazyScrollerIsActive = false;
1442 scrollBodyPanel.getElement().getStyle().clearOverflowX();
1443 scrollBodyPanel.getElement().getStyle().clearOverflowY();
1444 }
1445
1446 private void enableLazyScroller() {
1447 Scheduler.get().scheduleDeferred(lazyScroller);
1448 lazyScrollerIsActive = true;
1449
1450 scrollBodyPanel.getElement().getStyle().setOverflowX(Overflow.HIDDEN);
1451 scrollBodyPanel.getElement().getStyle().setOverflowY(Overflow.HIDDEN);
1452 }
1453
1454 private boolean isLazyScrollerActive() {
1455 return lazyScrollerIsActive;
1456 }
1457
1458 private ScheduledCommand lazyScroller = new ScheduledCommand() {
1459
1460 @Override
1461 public void execute() {
1462 if (firstvisible >= 0) {
1463 firstRowInViewPort = firstvisible;
1464 if (firstvisibleOnLastPage > -1) {
1465 scrollBodyPanel.setScrollPosition(
1466 measureRowHeightOffset(firstvisibleOnLastPage));
1467 } else {
1468 scrollBodyPanel.setScrollPosition(
1469 measureRowHeightOffset(firstvisible));
1470 }
1471 }
1472 disableLazyScroller();
1473 }
1474 };
1475
1476
1477 public void updateFirstVisibleAndScrollIfNeeded(UIDL uidl) {
1478 firstvisible = uidl.hasVariable("firstvisible")
1479 ? uidl.getIntVariable("firstvisible")
1480 : 0;
1481 firstvisibleOnLastPage = uidl.hasVariable("firstvisibleonlastpage")
1482 ? uidl.getIntVariable("firstvisibleonlastpage")
1483 : -1;
1484 if (firstvisible != lastRequestedFirstvisible && scrollBody != null) {
1485
1486
1487
1488 lastRequestedFirstvisible = firstRowInViewPort;
1489
1490
1491
1492
1493 enableLazyScroller();
1494 }
1495 }
1496
1497 protected int measureRowHeightOffset(int rowIx) {
1498 return (int) (rowIx * scrollBody.getRowHeight());
1499 }
1500
1501
1502 public void updatePageLength(UIDL uidl) {
1503 int oldPageLength = pageLength;
1504 if (uidl.hasAttribute("pagelength")) {
1505 pageLength = uidl.getIntAttribute("pagelength");
1506 } else {
1507
1508 pageLength = totalRows;
1509 }
1510
1511 if (oldPageLength != pageLength && initializedAndAttached) {
1512
1513 sizeNeedsInit = true;
1514 }
1515 }
1516
1517
1518 public void updateSelectionProperties(UIDL uidl,
1519 AbstractComponentState state, boolean readOnly) {
1520 setMultiSelectMode(uidl.hasAttribute("multiselectmode")
1521 ? uidl.getIntAttribute("multiselectmode")
1522 : MULTISELECT_MODE_DEFAULT);
1523
1524 nullSelectionAllowed = uidl.hasAttribute("nsa")
1525 ? uidl.getBooleanAttribute("nsa")
1526 : true;
1527
1528 if (uidl.hasAttribute("selectmode")) {
1529 if (readOnly) {
1530 selectMode = SelectMode.NONE;
1531 } else if (uidl.getStringAttribute("selectmode").equals("multi")) {
1532 selectMode = SelectMode.MULTI;
1533 } else if (uidl.getStringAttribute("selectmode").equals("single")) {
1534 selectMode = SelectMode.SINGLE;
1535 } else {
1536 selectMode = SelectMode.NONE;
1537 }
1538 }
1539 }
1540
1541
1542 public void updateDragMode(UIDL uidl) {
1543 dragmode = uidl.hasAttribute("dragmode")
1544 ? uidl.getIntAttribute("dragmode")
1545 : 0;
1546 if (BrowserInfo.get().isIE()) {
1547 if (dragmode > 0) {
1548 getElement().setPropertyJSO("onselectstart",
1549 getPreventTextSelectionIEHack());
1550 } else {
1551 getElement().setPropertyJSO("onselectstart", null);
1552 }
1553 }
1554 }
1555
1556
1557 public void updateTotalRows(UIDL uidl) {
1558 int newTotalRows = uidl.getIntAttribute("totalrows");
1559 if (newTotalRows != getTotalRows()) {
1560 if (scrollBody != null) {
1561 if (getTotalRows() == 0) {
1562 tHead.clear();
1563 tFoot.clear();
1564 }
1565 initializedAndAttached = false;
1566 initialContentReceived = false;
1567 isNewBody = true;
1568 }
1569 setTotalRows(newTotalRows);
1570 }
1571 }
1572
1573 protected void setTotalRows(int newTotalRows) {
1574 totalRows = newTotalRows;
1575 }
1576
1577 public int getTotalRows() {
1578 return totalRows;
1579 }
1580
1581
1582
1583
1584
1585
1586
1587 private int getHeaderPadding() {
1588 return scrollBody.getCellExtraWidth();
1589 }
1590
1591
1592
1593
1594
1595
1596
1597
1598 protected int getHierarchyColumnIndex() {
1599 return -1;
1600 }
1601
1602
1603
1604
1605 public void updateMaxIndent() {
1606 int oldIndent = scrollBody.getMaxIndent();
1607 scrollBody.calculateMaxIndent();
1608 if (oldIndent != scrollBody.getMaxIndent()) {
1609
1610 triggerLazyColumnAdjustment(true);
1611 }
1612 }
1613
1614
1615 public void focusRowFromBody() {
1616 if (selectedRowKeys.size() == 1) {
1617
1618 String selectedRowKey = selectedRowKeys.iterator().next();
1619 if (selectedRowKey != null) {
1620 VScrollTableRow renderedRow = getRenderedRowByKey(
1621 selectedRowKey);
1622 if (renderedRow == null || !renderedRow.isInViewPort()) {
1623 setRowFocus(
1624 scrollBody.getRowByRowIndex(firstRowInViewPort));
1625 } else {
1626 setRowFocus(renderedRow);
1627 }
1628 }
1629 } else {
1630
1631 setRowFocus(scrollBody.getRowByRowIndex(firstRowInViewPort));
1632 }
1633 }
1634
1635 protected VScrollTableBody createScrollBody() {
1636 return new VScrollTableBody();
1637 }
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647 public void selectLastRenderedRowInViewPort(boolean focusOnly) {
1648 int index = firstRowInViewPort + getFullyVisibleRowCount();
1649 VScrollTableRow lastRowInViewport = scrollBody.getRowByRowIndex(index);
1650 if (lastRowInViewport == null) {
1651
1652
1653 lastRowInViewport = scrollBody
1654 .getRowByRowIndex(scrollBody.getLastRendered());
1655 if (lastRowInViewport == null) {
1656 return;
1657 }
1658 }
1659 setRowFocus(lastRowInViewport);
1660 if (!focusOnly) {
1661 selectFocusedRow(false, multiselectPending);
1662 sendSelectedRows();
1663 }
1664 }
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674 public void selectFirstRenderedRowInViewPort(boolean focusOnly) {
1675 int index = firstRowInViewPort;
1676 VScrollTableRow firstInViewport = scrollBody.getRowByRowIndex(index);
1677 if (firstInViewport == null) {
1678
1679 return;
1680 }
1681 setRowFocus(firstInViewport);
1682 if (!focusOnly) {
1683 selectFocusedRow(false, multiselectPending);
1684 sendSelectedRows();
1685 }
1686 }
1687
1688
1689 public void setCacheRateFromUIDL(UIDL uidl) {
1690 setCacheRate(uidl.hasAttribute("cr") ? uidl.getDoubleAttribute("cr")
1691 : CACHE_RATE_DEFAULT);
1692 }
1693
1694 private void setCacheRate(double d) {
1695 if (cacheRate != d) {
1696 cacheRate = d;
1697 cacheReactRate = 0.75 * d;
1698 }
1699 }
1700
1701
1702 public void updateActionMap(UIDL mainUidl) {
1703 UIDL actionsUidl = mainUidl.getChildByTagName("actions");
1704 if (actionsUidl == null) {
1705 return;
1706 }
1707
1708 for (final Object child : actionsUidl) {
1709 final UIDL action = (UIDL) child;
1710 final String key = action.getStringAttribute("key");
1711 final String caption = action.getStringAttribute("caption");
1712 actionMap.put(key + "_c", caption);
1713 if (action.hasAttribute("icon")) {
1714
1715 actionMap.put(key + "_i", action.getStringAttribute("icon"));
1716 } else {
1717 actionMap.remove(key + "_i");
1718 }
1719 }
1720
1721 }
1722
1723 public String getActionCaption(String actionKey) {
1724 return actionMap.get(actionKey + "_c");
1725 }
1726
1727 public String getActionIcon(String actionKey) {
1728 return client.translateVaadinUri(actionMap.get(actionKey + "_i"));
1729 }
1730
1731 private void updateHeader(String[] strings) {
1732 if (strings == null) {
1733 return;
1734 }
1735
1736 int visibleCols = strings.length;
1737 int colIndex = 0;
1738 if (showRowHeaders) {
1739 tHead.enableColumn(ROW_HEADER_COLUMN_KEY, colIndex);
1740 visibleCols++;
1741 visibleColOrder = new String[visibleCols];
1742 visibleColOrder[colIndex] = ROW_HEADER_COLUMN_KEY;
1743 colIndex++;
1744 } else {
1745 visibleColOrder = new String[visibleCols];
1746 tHead.removeCell(ROW_HEADER_COLUMN_KEY);
1747 }
1748
1749 for (final String cid : strings) {
1750 visibleColOrder[colIndex] = cid;
1751 tHead.enableColumn(cid, colIndex);
1752 colIndex++;
1753 }
1754
1755 tHead.setVisible(showColHeaders);
1756 setContainerHeight();
1757
1758 }
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768 private void updateFooter(String[] strings) {
1769 if (strings == null) {
1770 return;
1771 }
1772
1773
1774 int colIndex = 0;
1775 if (showRowHeaders) {
1776 tFoot.enableColumn(ROW_HEADER_COLUMN_KEY, colIndex);
1777 colIndex++;
1778 } else {
1779 tFoot.removeCell(ROW_HEADER_COLUMN_KEY);
1780 }
1781
1782 for (final String cid : strings) {
1783 tFoot.enableColumn(cid, colIndex);
1784 colIndex++;
1785 }
1786
1787 tFoot.setVisible(showColFooters);
1788 }
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800 public void updateBody(UIDL uidl, int firstRow, int reqRows) {
1801 int oldIndent = scrollBody.getMaxIndent();
1802 if (uidl == null || reqRows < 1) {
1803
1804 if (firstRow <= 0) {
1805 postponeSanityCheckForLastRendered = true;
1806 while (scrollBody.getLastRendered() > scrollBody
1807 .getFirstRendered()) {
1808 scrollBody.unlinkRow(false);
1809 }
1810 postponeSanityCheckForLastRendered = false;
1811 scrollBody.unlinkRow(false);
1812 }
1813 return;
1814 }
1815
1816 scrollBody.renderRows(uidl, firstRow, reqRows);
1817
1818 discardRowsOutsideCacheWindow();
1819 scrollBody.calculateMaxIndent();
1820 if (oldIndent != scrollBody.getMaxIndent()) {
1821
1822 headerChangedDuringUpdate = true;
1823 }
1824 }
1825
1826
1827 public void updateRowsInBody(UIDL partialRowUpdates) {
1828 if (partialRowUpdates == null) {
1829 return;
1830 }
1831 int firstRowIx = partialRowUpdates.getIntAttribute("firsturowix");
1832 int count = partialRowUpdates.getIntAttribute("numurows");
1833 scrollBody.unlinkRows(firstRowIx, count);
1834 scrollBody.insertRows(partialRowUpdates, firstRowIx, count);
1835 }
1836
1837
1838
1839
1840
1841 protected void discardRowsOutsideCacheWindow() {
1842 int firstRowToKeep = (int) (firstRowInViewPort
1843 - pageLength * cacheRate);
1844 int lastRowToKeep = (int) (firstRowInViewPort + pageLength
1845 + pageLength * cacheRate);
1846
1847 if (firstRowToKeep < 0) {
1848 firstRowToKeep = 0;
1849 }
1850 if (lastRowToKeep > totalRows) {
1851 lastRowToKeep = totalRows - 1;
1852 }
1853 debug("Client side calculated cache rows to keep: " + firstRowToKeep
1854 + "-" + lastRowToKeep);
1855
1856 if (serverCacheFirst != -1) {
1857 firstRowToKeep = serverCacheFirst;
1858 lastRowToKeep = serverCacheLast;
1859 debug("Server cache rows that override: " + serverCacheFirst + "-"
1860 + serverCacheLast);
1861 if (firstRowToKeep < scrollBody.getFirstRendered()
1862 || lastRowToKeep > scrollBody.getLastRendered()) {
1863 debug("*** Server wants us to keep " + serverCacheFirst + "-"
1864 + serverCacheLast + " but we only have rows "
1865 + scrollBody.getFirstRendered() + "-"
1866 + scrollBody.getLastRendered() + " rendered!");
1867 }
1868 }
1869 discardRowsOutsideOf(firstRowToKeep, lastRowToKeep);
1870
1871 scrollBody.fixSpacers();
1872
1873 scrollBody.restoreRowVisibility();
1874 }
1875
1876 private void discardRowsOutsideOf(int optimalFirstRow, int optimalLastRow) {
1877
1878
1879
1880
1881 int firstDiscarded = -1, lastDiscarded = -1;
1882 boolean cont = true;
1883 while (cont && scrollBody.getLastRendered() > optimalFirstRow
1884 && scrollBody.getFirstRendered() < optimalFirstRow) {
1885 if (firstDiscarded == -1) {
1886 firstDiscarded = scrollBody.getFirstRendered();
1887 }
1888
1889
1890 cont = scrollBody.unlinkRow(true);
1891 }
1892 if (firstDiscarded != -1) {
1893 lastDiscarded = scrollBody.getFirstRendered() - 1;
1894 debug("Discarded rows " + firstDiscarded + "-" + lastDiscarded);
1895 }
1896 firstDiscarded = lastDiscarded = -1;
1897
1898 cont = true;
1899 while (cont && scrollBody.getLastRendered() > optimalLastRow) {
1900 if (lastDiscarded == -1) {
1901 lastDiscarded = scrollBody.getLastRendered();
1902 }
1903
1904
1905 cont = scrollBody.unlinkRow(false);
1906 }
1907 if (lastDiscarded != -1) {
1908 firstDiscarded = scrollBody.getLastRendered() + 1;
1909 debug("Discarded rows " + firstDiscarded + "-" + lastDiscarded);
1910 }
1911
1912 debug("Now in cache: " + scrollBody.getFirstRendered() + "-"
1913 + scrollBody.getLastRendered());
1914 }
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925 public void addAndRemoveRows(UIDL partialRowAdditions) {
1926 if (partialRowAdditions == null) {
1927 return;
1928 }
1929 if (partialRowAdditions.hasAttribute("hide")) {
1930 scrollBody.unlinkAndReindexRows(
1931 partialRowAdditions.getIntAttribute("firstprowix"),
1932 partialRowAdditions.getIntAttribute("numprows"));
1933 scrollBody.ensureCacheFilled();
1934 } else {
1935 if (partialRowAdditions.hasAttribute("delbelow")) {
1936 scrollBody.insertRowsDeleteBelow(partialRowAdditions,
1937 partialRowAdditions.getIntAttribute("firstprowix"),
1938 partialRowAdditions.getIntAttribute("numprows"));
1939 } else {
1940 scrollBody.insertAndReindexRows(partialRowAdditions,
1941 partialRowAdditions.getIntAttribute("firstprowix"),
1942 partialRowAdditions.getIntAttribute("numprows"));
1943 }
1944 }
1945
1946 discardRowsOutsideCacheWindow();
1947 }
1948
1949
1950
1951
1952
1953
1954
1955 private int getColIndexByKey(String colKey) {
1956
1957 if (ROW_HEADER_COLUMN_KEY.equals(colKey)) {
1958 return 0;
1959 }
1960 for (int i = 0; i < visibleColOrder.length; i++) {
1961 if (visibleColOrder[i].equals(colKey)) {
1962 return i;
1963 }
1964 }
1965 return -1;
1966 }
1967
1968 protected boolean isMultiSelectModeSimple() {
1969 return selectMode == SelectMode.MULTI
1970 && multiselectmode == MULTISELECT_MODE_SIMPLE;
1971 }
1972
1973 protected boolean isSingleSelectMode() {
1974 return selectMode == SelectMode.SINGLE;
1975 }
1976
1977 protected boolean isMultiSelectModeAny() {
1978 return selectMode == SelectMode.MULTI;
1979 }
1980
1981 protected boolean isMultiSelectModeDefault() {
1982 return selectMode == SelectMode.MULTI
1983 && multiselectmode == MULTISELECT_MODE_DEFAULT;
1984 }
1985
1986 protected void setMultiSelectMode(int multiselectmode) {
1987 if (BrowserInfo.get().isTouchDevice()) {
1988
1989
1990 this.multiselectmode = MULTISELECT_MODE_SIMPLE;
1991 } else {
1992 this.multiselectmode = multiselectmode;
1993 }
1994
1995 }
1996
1997
1998 public boolean isSelectable() {
1999 return selectMode.getId() > SelectMode.NONE.getId();
2000 }
2001
2002 private boolean isCollapsedColumn(String colKey) {
2003 if (collapsedColumns == null) {
2004 return false;
2005 }
2006 if (collapsedColumns.contains(colKey)) {
2007 return true;
2008 }
2009 return false;
2010 }
2011
2012 private String getColKeyByIndex(int index) {
2013 return tHead.getHeaderCell(index).getColKey();
2014 }
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030 protected void setColWidth(int colIndex, int w, boolean isDefinedWidth) {
2031 final HeaderCell hcell = tHead.getHeaderCell(colIndex);
2032
2033
2034
2035
2036 int minWidth = hcell.getMinWidth(false, false);
2037 if (w < minWidth) {
2038 w = minWidth;
2039 }
2040
2041
2042 hcell.setWidth(w, isDefinedWidth);
2043
2044
2045 FooterCell fcell = tFoot.getFooterCell(colIndex);
2046 fcell.setWidth(w, isDefinedWidth);
2047
2048
2049 tHead.resizeCaptionContainer(hcell);
2050
2051
2052
2053
2054 minWidth = hcell.getMinWidth(true, false);
2055 if (w < minWidth) {
2056 w = minWidth;
2057 }
2058
2059
2060 scrollBody.setColWidth(colIndex, w);
2061 }
2062
2063 private int getColWidth(String colKey) {
2064 return tHead.getHeaderCell(colKey).getWidthWithIndent();
2065 }
2066
2067
2068
2069
2070
2071
2072
2073
2074 public VScrollTableRow getRenderedRowByKey(String key) {
2075 if (scrollBody != null) {
2076 for (Widget w : scrollBody) {
2077 VScrollTableRow r = (VScrollTableRow) w;
2078 if (r.getKey().equals(key)) {
2079 return r;
2080 }
2081 }
2082 }
2083 return null;
2084 }
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094 private VScrollTableRow getNextRow(VScrollTableRow row, int offset) {
2095 final Iterator<Widget> it = scrollBody.iterator();
2096 while (it.hasNext()) {
2097 VScrollTableRow r = (VScrollTableRow) it.next();
2098 if (r == row) {
2099 r = null;
2100 while (offset >= 0 && it.hasNext()) {
2101 r = (VScrollTableRow) it.next();
2102 offset--;
2103 }
2104 return r;
2105 }
2106 }
2107
2108 return null;
2109 }
2110
2111
2112
2113
2114
2115
2116
2117
2118 private VScrollTableRow getPreviousRow(VScrollTableRow row, int offset) {
2119 final Iterator<Widget> it = scrollBody.iterator();
2120 final Iterator<Widget> offsetIt = scrollBody.iterator();
2121 VScrollTableRow prev = null;
2122 while (it.hasNext()) {
2123 VScrollTableRow r = (VScrollTableRow) it.next();
2124 if (offset < 0) {
2125 prev = (VScrollTableRow) offsetIt.next();
2126 }
2127 if (r == row) {
2128 return prev;
2129 }
2130 offset--;
2131 }
2132
2133 return null;
2134 }
2135
2136 protected void reOrderColumn(String columnKey, int newIndex) {
2137
2138 final int oldIndex = getColIndexByKey(columnKey);
2139
2140
2141 tHead.moveCell(oldIndex, newIndex);
2142
2143
2144 scrollBody.moveCol(oldIndex, newIndex);
2145
2146
2147 tFoot.moveCell(oldIndex, newIndex);
2148
2149
2150
2151
2152
2153
2154
2155
2156 final String oldKeyOnNewIndex = visibleColOrder[newIndex];
2157 if (showRowHeaders) {
2158 newIndex--;
2159 }
2160
2161 for (String order : columnOrder) {
2162 if (order.equals(oldKeyOnNewIndex)) {
2163 break;
2164 }
2165 if (isCollapsedColumn(order)) {
2166 newIndex++;
2167 }
2168 }
2169
2170 final String[] newOrder = new String[columnOrder.length];
2171 for (int i = 0, j = 0; j < newOrder.length; i++) {
2172 if (j == newIndex) {
2173 newOrder[j] = columnKey;
2174 j++;
2175 }
2176 if (i == columnOrder.length) {
2177 break;
2178 }
2179 if (columnOrder[i].equals(columnKey)) {
2180 continue;
2181 }
2182 newOrder[j] = columnOrder[i];
2183 j++;
2184 }
2185 columnOrder = newOrder;
2186
2187 int i = showRowHeaders ? 1 : 0;
2188 for (int j = 0; j < newOrder.length; j++) {
2189 final String cid = newOrder[j];
2190 if (!isCollapsedColumn(cid)) {
2191 visibleColOrder[i++] = cid;
2192 }
2193 }
2194 client.updateVariable(paintableId, "columnorder", columnOrder, false);
2195 if (client.hasEventListeners(this,
2196 TableConstants.COLUMN_REORDER_EVENT_ID)) {
2197 client.sendPendingVariableChanges();
2198 }
2199 }
2200
2201 @Override
2202 protected void onDetach() {
2203 detachedScrollPosition = scrollBodyPanel.getScrollPosition();
2204 rowRequestHandler.cancel();
2205 super.onDetach();
2206
2207 if (scrollPositionElement != null) {
2208 final Element parent = DOM.getParent(scrollPositionElement);
2209 if (parent != null) {
2210 DOM.removeChild(parent, scrollPositionElement);
2211 }
2212 }
2213 }
2214
2215 @Override
2216 public void onAttach() {
2217 super.onAttach();
2218
2219 }
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233 public void sizeInit() {
2234 if (!isVisibleInHierarchy()) {
2235 return;
2236 }
2237
2238 sizeNeedsInit = false;
2239
2240 scrollBody.setContainerHeight();
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250 Iterator<Widget> headCells = tHead.iterator();
2251 Iterator<Widget> footCells = tFoot.iterator();
2252 int i = 0;
2253 int totalExplicitColumnsWidths = 0;
2254 int total = 0;
2255 float expandRatioDivider = 0;
2256
2257 final int[] widths = new int[tHead.visibleCells.size()];
2258
2259 tHead.enableBrowserIntelligence();
2260 tFoot.enableBrowserIntelligence();
2261
2262 int hierarchyColumnIndent = scrollBody != null
2263 ? scrollBody.getMaxIndent()
2264 : 0;
2265 HeaderCell hierarchyHeaderWithExpandRatio = null;
2266
2267
2268 while (headCells.hasNext()) {
2269 final HeaderCell hCell = (HeaderCell) headCells.next();
2270 final FooterCell fCell = (FooterCell) footCells.next();
2271 boolean needsIndent = hierarchyColumnIndent > 0
2272 && hCell.isHierarchyColumn();
2273 hCell.saveNaturalColumnWidthIfNotSaved(i);
2274 fCell.saveNaturalColumnWidthIfNotSaved(i);
2275 int w = hCell.getWidth();
2276 if (hCell.isDefinedWidth()) {
2277
2278 if (needsIndent && w < hierarchyColumnIndent) {
2279
2280 w = hierarchyColumnIndent;
2281 }
2282 totalExplicitColumnsWidths += w;
2283 } else {
2284 if (hCell.getExpandRatio() > 0) {
2285 expandRatioDivider += hCell.getExpandRatio();
2286 w = 0;
2287 if (needsIndent && w < hierarchyColumnIndent) {
2288 hierarchyHeaderWithExpandRatio = hCell;
2289
2290
2291 }
2292 } else {
2293
2294
2295
2296 int headerWidth = hCell.getNaturalColumnWidth(i);
2297 int footerWidth = fCell.getNaturalColumnWidth(i);
2298 w = headerWidth > footerWidth ? headerWidth : footerWidth;
2299 }
2300 if (w != 0) {
2301 hCell.setNaturalMinimumColumnWidth(w);
2302 fCell.setNaturalMinimumColumnWidth(w);
2303 }
2304 }
2305 widths[i] = w;
2306 total += w;
2307 i++;
2308 }
2309 if (hierarchyHeaderWithExpandRatio != null) {
2310 total += hierarchyColumnIndent;
2311 }
2312
2313 tHead.disableBrowserIntelligence();
2314 tFoot.disableBrowserIntelligence();
2315
2316 boolean willHaveScrollbarz = willHaveScrollbars();
2317
2318
2319 if (isDynamicWidth()) {
2320 int w = total;
2321 w += scrollBody.getCellExtraWidth() * visibleColOrder.length;
2322 if (willHaveScrollbarz) {
2323 w += WidgetUtil.getNativeScrollbarSize();
2324 }
2325 setContentWidth(w);
2326 }
2327
2328 int availW = scrollBody.getAvailableWidth();
2329 if (BrowserInfo.get().isIE()) {
2330
2331 availW = scrollBody.getAvailableWidth();
2332 }
2333 availW -= scrollBody.getCellExtraWidth() * visibleColOrder.length;
2334
2335 if (willHaveScrollbarz) {
2336 availW -= WidgetUtil.getNativeScrollbarSize();
2337 }
2338
2339
2340
2341 if (availW > total) {
2342
2343 int extraSpace = availW - total;
2344 if (hierarchyHeaderWithExpandRatio != null) {
2345
2346
2347
2348
2349
2350
2351
2352 extraSpace += hierarchyColumnIndent;
2353 }
2354 final int totalWidthR = total - totalExplicitColumnsWidths;
2355 int checksum = 0;
2356
2357 if (extraSpace == 1) {
2358
2359
2360
2361 headCells = tHead.iterator();
2362 i = 0;
2363 checksum = availW;
2364 while (headCells.hasNext()) {
2365 HeaderCell hc = (HeaderCell) headCells.next();
2366 if (!hc.isDefinedWidth()) {
2367 widths[i]++;
2368 break;
2369 }
2370 i++;
2371 }
2372
2373 } else if (expandRatioDivider > 0) {
2374 boolean setIndentToHierarchyHeader = false;
2375 if (hierarchyHeaderWithExpandRatio != null) {
2376
2377
2378 final int newSpace = Math.round((extraSpace
2379 * (hierarchyHeaderWithExpandRatio.getExpandRatio()
2380 / expandRatioDivider)));
2381 if (newSpace < hierarchyColumnIndent) {
2382
2383
2384
2385 setIndentToHierarchyHeader = true;
2386 extraSpace -= hierarchyColumnIndent;
2387 }
2388 }
2389
2390
2391
2392 headCells = tHead.iterator();
2393 i = 0;
2394 while (headCells.hasNext()) {
2395 HeaderCell hCell = (HeaderCell) headCells.next();
2396 if (hCell.getExpandRatio() > 0) {
2397 int w = widths[i];
2398 if (setIndentToHierarchyHeader
2399 && hierarchyHeaderWithExpandRatio
2400 .equals(hCell)) {
2401
2402
2403 w += hierarchyColumnIndent;
2404 } else {
2405 final int newSpace = Math
2406 .round((extraSpace * (hCell.getExpandRatio()
2407 / expandRatioDivider)));
2408 w += newSpace;
2409 }
2410 widths[i] = w;
2411 }
2412 checksum += widths[i];
2413 i++;
2414 }
2415 } else if (totalWidthR > 0) {
2416
2417
2418
2419
2420 headCells = tHead.iterator();
2421 i = 0;
2422 while (headCells.hasNext()) {
2423 HeaderCell hCell = (HeaderCell) headCells.next();
2424 if (!hCell.isDefinedWidth()) {
2425 int w = widths[i];
2426 final int newSpace = Math.round(
2427 (float) extraSpace * (float) w / totalWidthR);
2428 w += newSpace;
2429 widths[i] = w;
2430 }
2431 checksum += widths[i];
2432 i++;
2433 }
2434 }
2435
2436 if (extraSpace > 0 && checksum != availW) {
2437
2438
2439
2440
2441
2442 headCells = tHead.iterator();
2443 i = 0;
2444 while (headCells.hasNext()) {
2445 HeaderCell hc = (HeaderCell) headCells.next();
2446 if (!hc.isDefinedWidth()) {
2447 widths[i] += availW - checksum;
2448 break;
2449 }
2450 i++;
2451 }
2452 }
2453
2454 } else {
2455
2456 }
2457
2458
2459 i = 0;
2460 headCells = tHead.iterator();
2461 while (headCells.hasNext()) {
2462 final HeaderCell hCell = (HeaderCell) headCells.next();
2463
2464 final int w = widths[i];
2465 setColWidth(i, w, false);
2466
2467 i++;
2468 }
2469
2470 initializedAndAttached = true;
2471
2472 updatePageLength();
2473
2474
2475
2476
2477
2478 if (isDynamicHeight()) {
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489 if (pageLength == totalRows && pageLength == 0) {
2490 scrollBody.setHeight("0px");
2491 }
2492
2493 int bodyHeight;
2494 if (pageLength == totalRows) {
2495
2496
2497
2498
2499
2500
2501
2502 bodyHeight = scrollBody.getRequiredHeight();
2503 } else {
2504 bodyHeight = (int) Math
2505 .round(scrollBody.getRowHeight(true) * pageLength);
2506 }
2507 boolean needsSpaceForHorizontalSrollbar = (total > availW);
2508 if (needsSpaceForHorizontalSrollbar) {
2509 bodyHeight += WidgetUtil.getNativeScrollbarSize();
2510 }
2511 scrollBodyPanel.setHeight(bodyHeight + "px");
2512 WidgetUtil.runWebkitOverflowAutoFix(scrollBodyPanel.getElement());
2513 }
2514
2515 isNewBody = false;
2516
2517 if (firstvisible > 0) {
2518 enableLazyScroller();
2519 }
2520
2521 if (enabled) {
2522
2523 if (scrollBody.getLastRendered() + 1 < firstRowInViewPort
2524 + pageLength + (int) cacheReactRate * pageLength) {
2525 if (totalRows - 1 > scrollBody.getLastRendered()) {
2526
2527 int firstInNewSet = scrollBody.getLastRendered() + 1;
2528 int lastInNewSet = (int) (firstRowInViewPort + pageLength
2529 + cacheRate * pageLength);
2530 if (lastInNewSet > totalRows - 1) {
2531 lastInNewSet = totalRows - 1;
2532 }
2533 rowRequestHandler.triggerRowFetch(firstInNewSet,
2534 lastInNewSet - firstInNewSet + 1, 1);
2535 }
2536 }
2537 }
2538
2539
2540
2541
2542
2543 WidgetUtil
2544 .runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement());
2545
2546 hadScrollBars = willHaveScrollbarz;
2547 }
2548
2549
2550
2551
2552
2553
2554
2555 protected boolean willHaveScrollbars() {
2556 if (isDynamicHeight()) {
2557 if (pageLength < totalRows) {
2558 return true;
2559 }
2560 } else {
2561 int fakeheight = (int) Math
2562 .round(scrollBody.getRowHeight() * totalRows);
2563 int availableHeight = scrollBodyPanel.getElement()
2564 .getPropertyInt("clientHeight");
2565 if (fakeheight > availableHeight) {
2566 return true;
2567 }
2568 }
2569 return false;
2570 }
2571
2572 private void announceScrollPosition() {
2573 if (scrollPositionElement == null) {
2574 scrollPositionElement = DOM.createDiv();
2575 scrollPositionElement
2576 .setClassName(getStylePrimaryName() + "-scrollposition");
2577 scrollPositionElement.getStyle().setPosition(Position.ABSOLUTE);
2578 scrollPositionElement.getStyle().setDisplay(Display.NONE);
2579 getElement().appendChild(scrollPositionElement);
2580 }
2581
2582 Style style = scrollPositionElement.getStyle();
2583 style.setMarginLeft(getElement().getOffsetWidth() / 2 - 80, Unit.PX);
2584 style.setMarginTop(-scrollBodyPanel.getOffsetHeight(), Unit.PX);
2585
2586
2587 int last = (firstRowInViewPort + pageLength);
2588 if (last > totalRows) {
2589 last = totalRows;
2590 }
2591 scrollPositionElement.setInnerHTML("<span>" + (firstRowInViewPort + 1)
2592 + " – " + (last) + "..." + "</span>");
2593 style.setDisplay(Display.BLOCK);
2594 }
2595
2596
2597 public void hideScrollPositionAnnotation() {
2598 if (scrollPositionElement != null) {
2599 scrollPositionElement.getStyle().setDisplay(Display.NONE);
2600 }
2601 }
2602
2603
2604 public boolean isScrollPositionVisible() {
2605 return scrollPositionElement != null && !scrollPositionElement
2606 .getStyle().getDisplay().equals(Display.NONE.toString());
2607 }
2608
2609
2610 public class RowRequestHandler extends Timer {
2611
2612 private int reqFirstRow = 0;
2613 private int reqRows = 0;
2614 private boolean isRequestHandlerRunning = false;
2615
2616 public void triggerRowFetch(int first, int rows) {
2617 setReqFirstRow(first);
2618 setReqRows(rows);
2619 deferRowFetch();
2620 }
2621
2622 public void triggerRowFetch(int first, int rows, int delay) {
2623 setReqFirstRow(first);
2624 setReqRows(rows);
2625 deferRowFetch(delay);
2626 }
2627
2628 public void deferRowFetch() {
2629 deferRowFetch(250);
2630 }
2631
2632 public boolean isRequestHandlerRunning() {
2633 return isRequestHandlerRunning;
2634 }
2635
2636 public void deferRowFetch(int msec) {
2637 isRequestHandlerRunning = true;
2638 if (reqRows > 0 && reqFirstRow < totalRows) {
2639 schedule(msec);
2640
2641
2642
2643 if (totalRows > pageLength && ((firstRowInViewPort
2644 + pageLength > scrollBody.getLastRendered())
2645 || (firstRowInViewPort < scrollBody
2646 .getFirstRendered()))) {
2647 announceScrollPosition();
2648 } else {
2649 hideScrollPositionAnnotation();
2650 }
2651 }
2652 }
2653
2654 public int getReqFirstRow() {
2655 return reqFirstRow;
2656 }
2657
2658 public void setReqFirstRow(int reqFirstRow) {
2659 if (reqFirstRow < 0) {
2660 this.reqFirstRow = 0;
2661 } else if (reqFirstRow >= totalRows) {
2662 this.reqFirstRow = totalRows - 1;
2663 } else {
2664 this.reqFirstRow = reqFirstRow;
2665 }
2666 }
2667
2668 public void setReqRows(int reqRows) {
2669 if (reqRows < 0) {
2670 this.reqRows = 0;
2671 } else if (reqFirstRow + reqRows > totalRows) {
2672 this.reqRows = totalRows - reqFirstRow;
2673 } else {
2674 this.reqRows = reqRows;
2675 }
2676 }
2677
2678 @Override
2679 public void run() {
2680
2681 if (client.getMessageSender().hasActiveRequest() || navKeyDown) {
2682
2683 getLogger().info("Postponed rowfetch");
2684 schedule(250);
2685 } else if (allRenderedRowsAreNew() && !updatedReqRows) {
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697 setReqFirstRow(
2698 (firstRowInViewPort - (int) (pageLength * cacheRate)));
2699 int last = firstRowInViewPort + (int) (cacheRate * pageLength)
2700 + pageLength - 1;
2701 if (last >= totalRows) {
2702 last = totalRows - 1;
2703 }
2704 setReqRows(last - getReqFirstRow() + 1);
2705 updatedReqRows = true;
2706 schedule(250);
2707
2708 } else {
2709
2710 int firstRendered = scrollBody.getFirstRendered();
2711 int lastRendered = scrollBody.getLastRendered();
2712 if (lastRendered > totalRows) {
2713 lastRendered = totalRows - 1;
2714 }
2715 boolean rendered = firstRendered >= 0 && lastRendered >= 0;
2716
2717 int firstToBeRendered = firstRendered;
2718
2719 if (reqFirstRow < firstToBeRendered) {
2720 firstToBeRendered = reqFirstRow;
2721 } else if (firstRowInViewPort
2722 - (int) (cacheRate * pageLength) > firstToBeRendered) {
2723 firstToBeRendered = firstRowInViewPort
2724 - (int) (cacheRate * pageLength);
2725 if (firstToBeRendered < 0) {
2726 firstToBeRendered = 0;
2727 }
2728 } else if (rendered && firstRendered + 1 < reqFirstRow
2729 && lastRendered + 1 < reqFirstRow) {
2730
2731
2732 firstToBeRendered = reqFirstRow;
2733 }
2734 if (firstToBeRendered + reqRows < firstRendered) {
2735
2736
2737
2738 setReqRows(firstRendered - firstToBeRendered);
2739 }
2740
2741 int lastToBeRendered = lastRendered;
2742 int lastReqRow = reqFirstRow + reqRows - 1;
2743
2744 if (lastReqRow > lastToBeRendered) {
2745 lastToBeRendered = lastReqRow;
2746 } else if (firstRowInViewPort + pageLength
2747 + pageLength * cacheRate < lastToBeRendered) {
2748 lastToBeRendered = (firstRowInViewPort + pageLength
2749 + (int) (pageLength * cacheRate));
2750 if (lastToBeRendered >= totalRows) {
2751 lastToBeRendered = totalRows - 1;
2752 }
2753
2754
2755 if (lastReqRow > lastToBeRendered) {
2756 setReqRows(lastToBeRendered - reqFirstRow);
2757 }
2758 } else if (rendered && lastRendered - 1 > lastReqRow
2759 && firstRendered - 1 > lastReqRow) {
2760
2761
2762 lastToBeRendered = lastReqRow;
2763 }
2764
2765 if (lastToBeRendered > totalRows) {
2766 lastToBeRendered = totalRows - 1;
2767 }
2768 if (reqFirstRow < firstToBeRendered
2769 || (reqFirstRow > firstToBeRendered
2770 && (reqFirstRow < firstRendered
2771 || reqFirstRow > lastRendered + 1))) {
2772 setReqFirstRow(firstToBeRendered);
2773 }
2774 if (lastRendered < lastToBeRendered
2775 && lastRendered + reqRows < lastToBeRendered) {
2776
2777
2778
2779 setReqRows(lastToBeRendered - lastRendered);
2780 } else if (lastToBeRendered >= firstRendered
2781 && reqFirstRow + reqRows < firstRendered) {
2782 setReqRows(lastToBeRendered - lastRendered);
2783 }
2784
2785 client.updateVariable(paintableId, "firstToBeRendered",
2786 firstToBeRendered, false);
2787 client.updateVariable(paintableId, "lastToBeRendered",
2788 lastToBeRendered, false);
2789
2790
2791
2792 if (firstRowInViewPort != firstvisible) {
2793
2794
2795 lastRequestedFirstvisible = firstRowInViewPort;
2796 client.updateVariable(paintableId, "firstvisible",
2797 firstRowInViewPort, false);
2798 }
2799
2800 client.updateVariable(paintableId, "reqfirstrow", reqFirstRow,
2801 false);
2802 client.updateVariable(paintableId, "reqrows", reqRows, true);
2803
2804 if (selectionChanged) {
2805 unSyncedselectionsBeforeRowFetch = new HashSet<Object>(
2806 selectedRowKeys);
2807 }
2808 isRequestHandlerRunning = false;
2809 }
2810 }
2811
2812
2813
2814
2815 public void refreshContent() {
2816 isRequestHandlerRunning = true;
2817 int first = (int) (firstRowInViewPort - pageLength * cacheRate);
2818 int reqRows = (int) (2 * pageLength * cacheRate + pageLength);
2819 if (first < 0) {
2820 reqRows = reqRows + first;
2821 first = 0;
2822 }
2823 setReqFirstRow(first);
2824 setReqRows(reqRows);
2825 run();
2826 }
2827 }
2828
2829 public class HeaderCell extends Widget {
2830
2831 Element td = DOM.createTD();
2832
2833 protected Element captionContainer = DOM.createDiv();
2834
2835 Element sortIndicator = DOM.createDiv();
2836
2837 Element colResizeWidget = DOM.createDiv();
2838
2839 Element floatingCopyOfHeaderCell;
2840
2841 private boolean sortable = false;
2842 private final String cid;
2843
2844 private boolean dragging;
2845 private Integer currentDragX = null;
2846
2847 private int dragStartX;
2848 private int colIndex;
2849 private int originalWidth;
2850
2851 private boolean isResizing;
2852
2853 private int headerX;
2854
2855 private boolean moved;
2856
2857 private int closestSlot;
2858
2859 private int width = -1;
2860
2861 private int naturalWidth = -1;
2862
2863 private char align = ALIGN_LEFT;
2864
2865 boolean definedWidth = false;
2866
2867 private float expandRatio = 0;
2868
2869 private boolean sorted;
2870
2871 public void setSortable(boolean b) {
2872 sortable = b;
2873
2874
2875
2876
2877
2878
2879 }
2880
2881
2882
2883
2884
2885
2886 public void resizeCaptionContainer(int rightSpacing) {
2887 int captionContainerWidth = width - colResizeWidget.getOffsetWidth()
2888 - rightSpacing;
2889
2890 if (td.getClassName().contains("-asc")
2891 || td.getClassName().contains("-desc")) {
2892
2893 captionContainerWidth -= sortIndicator.getOffsetWidth();
2894 }
2895
2896 if (captionContainerWidth < 0) {
2897 rightSpacing += captionContainerWidth;
2898 captionContainerWidth = 0;
2899 }
2900
2901 captionContainer.getStyle().setPropertyPx("width",
2902 captionContainerWidth);
2903
2904
2905 if (rightSpacing > 0) {
2906 colResizeWidget.getStyle().setMarginLeft(rightSpacing, Unit.PX);
2907 } else {
2908 colResizeWidget.getStyle().clearMarginLeft();
2909 }
2910 }
2911
2912 public void setNaturalMinimumColumnWidth(int w) {
2913 naturalWidth = w;
2914 }
2915
2916 public HeaderCell(String colId, String headerText) {
2917 cid = colId;
2918
2919 setText(headerText);
2920
2921 td.appendChild(colResizeWidget);
2922
2923
2924 captionContainer.getStyle().setOverflow(Overflow.VISIBLE);
2925
2926 td.appendChild(sortIndicator);
2927 td.appendChild(captionContainer);
2928
2929 DOM.sinkEvents(td, Event.MOUSEEVENTS | Event.ONDBLCLICK
2930 | Event.ONCONTEXTMENU | Event.TOUCHEVENTS);
2931
2932 setElement(td);
2933
2934 setAlign(ALIGN_LEFT);
2935 }
2936
2937 protected void updateStyleNames(String primaryStyleName) {
2938 colResizeWidget.setClassName(primaryStyleName + "-resizer");
2939 sortIndicator.setClassName(primaryStyleName + "-sort-indicator");
2940 captionContainer
2941 .setClassName(primaryStyleName + "-caption-container");
2942 if (sorted) {
2943 if (sortAscending) {
2944 setStyleName(primaryStyleName + "-header-cell-asc");
2945 } else {
2946 setStyleName(primaryStyleName + "-header-cell-desc");
2947 }
2948 } else {
2949 setStyleName(primaryStyleName + "-header-cell");
2950 }
2951
2952 if (sortable) {
2953 addStyleName(primaryStyleName + "-header-sortable");
2954 }
2955
2956 final String alignPrefix = primaryStyleName
2957 + "-caption-container-align-";
2958
2959 switch (align) {
2960 case ALIGN_CENTER:
2961 captionContainer.addClassName(alignPrefix + "center");
2962 break;
2963 case ALIGN_RIGHT:
2964 captionContainer.addClassName(alignPrefix + "right");
2965 break;
2966 default:
2967 captionContainer.addClassName(alignPrefix + "left");
2968 break;
2969 }
2970
2971 }
2972
2973 public void disableAutoWidthCalculation() {
2974 definedWidth = true;
2975 expandRatio = 0;
2976 }
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988 public void setWidth(int w, boolean ensureDefinedWidth) {
2989 if (ensureDefinedWidth) {
2990 definedWidth = true;
2991
2992 expandRatio = 0;
2993 }
2994 if (width == -1) {
2995
2996 captionContainer.getStyle().clearOverflow();
2997 }
2998 width = w;
2999 if (w == -1) {
3000 captionContainer.getStyle().clearWidth();
3001 setWidth("");
3002 } else {
3003 tHead.resizeCaptionContainer(this);
3004
3005
3006
3007
3008
3009
3010 if (scrollBody != null) {
3011 int maxIndent = scrollBody.getMaxIndent();
3012 if (w < maxIndent && isHierarchyColumn()) {
3013 w = maxIndent;
3014 }
3015 int tdWidth = w + scrollBody.getCellExtraWidth();
3016 setWidth(tdWidth + "px");
3017 } else {
3018 Scheduler.get().scheduleDeferred(new Command() {
3019
3020 @Override
3021 public void execute() {
3022 int maxIndent = scrollBody.getMaxIndent();
3023 int tdWidth = width;
3024 if (tdWidth < maxIndent && isHierarchyColumn()) {
3025 tdWidth = maxIndent;
3026 }
3027 tdWidth += scrollBody.getCellExtraWidth();
3028 setWidth(tdWidth + "px");
3029 }
3030 });
3031 }
3032 }
3033 }
3034
3035 public void setUndefinedWidth() {
3036 definedWidth = false;
3037 if (!isResizing) {
3038 setWidth(-1, false);
3039 }
3040 }
3041
3042 private void setUndefinedWidthFlagOnly() {
3043 definedWidth = false;
3044 }
3045
3046
3047
3048
3049
3050
3051
3052 public boolean isDefinedWidth() {
3053 return definedWidth && width >= 0;
3054 }
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064 protected int getWidthWithIndent() {
3065 if (scrollBody != null && isHierarchyColumn()) {
3066 int maxIndent = scrollBody.getMaxIndent();
3067 if (maxIndent > width) {
3068 return maxIndent;
3069 }
3070 }
3071 return width;
3072 }
3073
3074
3075
3076
3077
3078
3079 public int getWidth() {
3080 return width;
3081 }
3082
3083
3084
3085
3086
3087
3088
3089 private boolean isHierarchyColumn() {
3090 int hierarchyColumnIndex = getHierarchyColumnIndex();
3091 return hierarchyColumnIndex >= 0
3092 && tHead.visibleCells.indexOf(this) == hierarchyColumnIndex;
3093 }
3094
3095 public void setText(String headerText) {
3096 DOM.setInnerHTML(captionContainer, headerText);
3097 }
3098
3099 public String getColKey() {
3100 return cid;
3101 }
3102
3103 protected void setSorted(boolean sorted) {
3104 this.sorted = sorted;
3105 updateStyleNames(VScrollTablePatched.this.getStylePrimaryName());
3106 }
3107
3108
3109
3110
3111
3112 @Override
3113 public void onBrowserEvent(Event event) {
3114 if (enabled && event != null) {
3115 if (isResizing
3116 || event.getEventTarget().cast() == colResizeWidget) {
3117 if (dragging && (event.getTypeInt() == Event.ONMOUSEUP
3118 || event.getTypeInt() == Event.ONTOUCHEND)) {
3119
3120 handleCaptionEvent(event);
3121 } else {
3122 onResizeEvent(event);
3123 }
3124 } else {
3125
3126
3127
3128
3129
3130
3131 if (event.getTypeInt() == Event.ONMOUSEDOWN
3132 || event.getTypeInt() == Event.ONTOUCHSTART) {
3133 scrollBodyPanel.setFocus(true);
3134 }
3135 handleCaptionEvent(event);
3136 boolean stopPropagation = true;
3137 if (event.getTypeInt() == Event.ONCONTEXTMENU
3138 && !client.hasEventListeners(VScrollTablePatched.this,
3139 TableConstants.HEADER_CLICK_EVENT_ID)) {
3140
3141
3142 stopPropagation = false;
3143 }
3144 if (stopPropagation) {
3145 event.stopPropagation();
3146 event.preventDefault();
3147 }
3148 }
3149 }
3150 }
3151
3152 private void createFloatingCopy() {
3153 floatingCopyOfHeaderCell = DOM.createDiv();
3154 DOM.setInnerHTML(floatingCopyOfHeaderCell, DOM.getInnerHTML(td));
3155 floatingCopyOfHeaderCell = DOM.getChild(floatingCopyOfHeaderCell,
3156 2);
3157
3158
3159
3160 String stylePrimaryName = VScrollTablePatched.this.getStylePrimaryName();
3161 StringBuilder sb = new StringBuilder();
3162 for (String s : VScrollTablePatched.this.getStyleName().split(" ")) {
3163 if (!s.equals(StyleConstants.UI_WIDGET)) {
3164 sb.append(s);
3165 if (s.equals(stylePrimaryName)) {
3166 sb.append("-header-drag ");
3167 } else {
3168 sb.append(' ');
3169 }
3170 }
3171 }
3172 floatingCopyOfHeaderCell.setClassName(sb.toString().trim());
3173
3174 floatingCopyOfHeaderCell.getStyle().setProperty("width", "auto");
3175 updateFloatingCopysPosition(DOM.getAbsoluteLeft(td),
3176 DOM.getAbsoluteTop(td));
3177 DOM.appendChild(VOverlay.getOverlayContainer(client),
3178 floatingCopyOfHeaderCell);
3179 }
3180
3181 private void updateFloatingCopysPosition(int x, int y) {
3182 x -= DOM.getElementPropertyInt(floatingCopyOfHeaderCell,
3183 "offsetWidth") / 2;
3184 floatingCopyOfHeaderCell.getStyle().setLeft(x, Unit.PX);
3185 if (y > 0) {
3186 floatingCopyOfHeaderCell.getStyle().setTop(y + 7, Unit.PX);
3187 }
3188 }
3189
3190 private void hideFloatingCopy() {
3191 floatingCopyOfHeaderCell.removeFromParent();
3192 floatingCopyOfHeaderCell = null;
3193 }
3194
3195
3196
3197
3198
3199
3200
3201
3202 private void fireHeaderClickedEvent(Event event) {
3203 if (client.hasEventListeners(VScrollTablePatched.this,
3204 TableConstants.HEADER_CLICK_EVENT_ID)) {
3205 MouseEventDetails details = MouseEventDetailsBuilder
3206 .buildMouseEventDetails(event);
3207 client.updateVariable(paintableId, "headerClickEvent",
3208 details.toString(), false);
3209 client.updateVariable(paintableId, "headerClickCID", cid, true);
3210 }
3211 }
3212
3213 protected void handleCaptionEvent(Event event) {
3214 switch (DOM.eventGetType(event)) {
3215 case Event.ONTOUCHSTART:
3216 case Event.ONMOUSEDOWN:
3217 if (columnReordering
3218 && WidgetUtil.isTouchEventOrLeftMouseButton(event)) {
3219 if (event.getTypeInt() == Event.ONTOUCHSTART) {
3220
3221
3222
3223 event.stopPropagation();
3224 }
3225 dragging = true;
3226 currentDragX = WidgetUtil.getTouchOrMouseClientX(event);
3227 moved = false;
3228 colIndex = getColIndexByKey(cid);
3229 DOM.setCapture(getElement());
3230 headerX = tHead.getAbsoluteLeft();
3231
3232
3233 event.preventDefault();
3234 }
3235 break;
3236 case Event.ONMOUSEUP:
3237 case Event.ONTOUCHEND:
3238 case Event.ONTOUCHCANCEL:
3239 if (columnReordering
3240 && WidgetUtil.isTouchEventOrLeftMouseButton(event)) {
3241 dragging = false;
3242 currentDragX = null;
3243 DOM.releaseCapture(getElement());
3244
3245 if (WidgetUtil.isTouchEvent(event)) {
3246
3247
3248
3249
3250 event.preventDefault();
3251 event.stopPropagation();
3252 }
3253 if (moved) {
3254 hideFloatingCopy();
3255 tHead.removeSlotFocus();
3256 if (closestSlot != colIndex
3257 && closestSlot != (colIndex + 1)) {
3258 if (closestSlot > colIndex) {
3259 reOrderColumn(cid, closestSlot - 1);
3260 } else {
3261 reOrderColumn(cid, closestSlot);
3262 }
3263 }
3264 moved = false;
3265 break;
3266 }
3267 }
3268
3269 if (!moved) {
3270
3271 if (sortable && WidgetUtil
3272 .isTouchEventOrLeftMouseButton(event)) {
3273 if (sortColumn.equals(cid)) {
3274
3275 client.updateVariable(paintableId, "sortascending",
3276 !sortAscending, false);
3277 } else {
3278
3279 client.updateVariable(paintableId, "sortcolumn",
3280 cid, false);
3281 }
3282
3283 scrollBodyPanel.setScrollPosition(0);
3284 firstvisible = 0;
3285 rowRequestHandler.setReqFirstRow(0);
3286 rowRequestHandler
3287 .setReqRows((int) (2 * pageLength * cacheRate
3288 + pageLength));
3289
3290
3291 rowRequestHandler.deferRowFetch();
3292 rowRequestHandler.cancel();
3293
3294 rowRequestHandler.run();
3295 }
3296 fireHeaderClickedEvent(event);
3297 if (WidgetUtil.isTouchEvent(event)) {
3298
3299
3300
3301
3302 event.preventDefault();
3303 event.stopPropagation();
3304 }
3305 break;
3306 }
3307 break;
3308 case Event.ONDBLCLICK:
3309 fireHeaderClickedEvent(event);
3310 break;
3311 case Event.ONTOUCHMOVE:
3312 case Event.ONMOUSEMOVE:
3313
3314
3315 int currentX = WidgetUtil.getTouchOrMouseClientX(event);
3316
3317 if (currentDragX == null || Math.abs(currentDragX
3318 - currentX) > VDragAndDropManager.MINIMUM_DISTANCE_TO_START_DRAG) {
3319 if (dragging && WidgetUtil
3320 .isTouchEventOrLeftMouseButton(event)) {
3321 if (event.getTypeInt() == Event.ONTOUCHMOVE) {
3322
3323
3324
3325 event.stopPropagation();
3326 }
3327 if (!moved) {
3328 createFloatingCopy();
3329 moved = true;
3330 }
3331
3332 final int clientX = WidgetUtil
3333 .getTouchOrMouseClientX(event);
3334 final int x = clientX
3335 + tHead.hTableWrapper.getScrollLeft();
3336 int slotX = headerX;
3337 closestSlot = colIndex;
3338 int closestDistance = -1;
3339 int start = 0;
3340 if (showRowHeaders) {
3341 start++;
3342 }
3343 final int visibleCellCount = tHead
3344 .getVisibleCellCount();
3345 for (int i = start; i <= visibleCellCount; i++) {
3346 if (i > 0) {
3347 final String colKey = getColKeyByIndex(i - 1);
3348
3349
3350
3351 slotX += getColWidth(colKey)
3352 + scrollBody.getCellExtraWidth();
3353 }
3354 final int dist = Math.abs(x - slotX);
3355 if (closestDistance == -1
3356 || dist < closestDistance) {
3357 closestDistance = dist;
3358 closestSlot = i;
3359 }
3360 }
3361 tHead.focusSlot(closestSlot);
3362
3363 updateFloatingCopysPosition(clientX, -1);
3364 }
3365 }
3366 break;
3367 default:
3368 break;
3369 }
3370 }
3371
3372 private void onResizeEvent(Event event) {
3373 switch (DOM.eventGetType(event)) {
3374 case Event.ONMOUSEDOWN:
3375 if (!WidgetUtil.isTouchEventOrLeftMouseButton(event)) {
3376 return;
3377 }
3378 isResizing = true;
3379 DOM.setCapture(getElement());
3380 dragStartX = DOM.eventGetClientX(event);
3381 colIndex = getColIndexByKey(cid);
3382 originalWidth = getWidthWithIndent();
3383 DOM.eventPreventDefault(event);
3384 break;
3385 case Event.ONMOUSEUP:
3386 if (!WidgetUtil.isTouchEventOrLeftMouseButton(event)) {
3387 return;
3388 }
3389 isResizing = false;
3390 DOM.releaseCapture(getElement());
3391 tHead.disableAutoColumnWidthCalculation(this);
3392
3393
3394
3395 HeaderCell lastCell = tHead
3396 .getHeaderCell(tHead.getVisibleCellCount() - 1);
3397 tHead.resizeCaptionContainer(lastCell);
3398 triggerLazyColumnAdjustment(true);
3399
3400 fireColumnResizeEvent(cid, originalWidth, getColWidth(cid));
3401 break;
3402 case Event.ONMOUSEMOVE:
3403 if (!WidgetUtil.isTouchEventOrLeftMouseButton(event)) {
3404 return;
3405 }
3406 if (isResizing) {
3407 final int deltaX = DOM.eventGetClientX(event) - dragStartX;
3408 if (deltaX == 0) {
3409 return;
3410 }
3411 tHead.disableAutoColumnWidthCalculation(this);
3412
3413 int newWidth = originalWidth + deltaX;
3414
3415 int minWidth = getMinWidth(true, false);
3416 if (newWidth < minWidth) {
3417
3418 newWidth = minWidth;
3419 }
3420 setColWidth(colIndex, newWidth, true);
3421 triggerLazyColumnAdjustment(false);
3422 forceRealignColumnHeaders();
3423 }
3424 break;
3425 default:
3426 break;
3427 }
3428 }
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440 private int getMinWidth(boolean includeIndent,
3441 boolean includeCellExtraWidth) {
3442 int minWidth = sortIndicator.getOffsetWidth();
3443 if (scrollBody != null) {
3444
3445 if (includeIndent && isHierarchyColumn()) {
3446 int maxIndent = scrollBody.getMaxIndent();
3447 if (minWidth < maxIndent) {
3448 minWidth = maxIndent;
3449 }
3450 }
3451 if (includeCellExtraWidth) {
3452 minWidth += scrollBody.getCellExtraWidth();
3453 }
3454 }
3455 return minWidth;
3456 }
3457
3458 public int getMinWidth() {
3459
3460 return getMinWidth(false, true);
3461 }
3462
3463 public String getCaption() {
3464 return DOM.getInnerText(captionContainer);
3465 }
3466
3467 public boolean isEnabled() {
3468 return getParent() != null;
3469 }
3470
3471 public void setAlign(char c) {
3472 align = c;
3473 updateStyleNames(VScrollTablePatched.this.getStylePrimaryName());
3474 }
3475
3476 public char getAlign() {
3477 return align;
3478 }
3479
3480
3481
3482
3483
3484
3485
3486 protected void saveNaturalColumnWidthIfNotSaved(int columnIndex) {
3487 if (naturalWidth < 0) {
3488
3489
3490
3491 int hw = captionContainer.getOffsetWidth() + getHeaderPadding();
3492 if (BrowserInfo.get().isGecko()) {
3493 hw += sortIndicator.getOffsetWidth();
3494 }
3495 if (columnIndex < 0) {
3496 columnIndex = 0;
3497 for (Widget w : tHead) {
3498 if (w == this) {
3499 break;
3500 }
3501 columnIndex++;
3502 }
3503 }
3504 final int cw = scrollBody.getColWidth(columnIndex);
3505 naturalWidth = (hw > cw ? hw : cw);
3506 }
3507 }
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519 public int getNaturalColumnWidth(int columnIndex) {
3520 final int iw = columnIndex == getHierarchyColumnIndex()
3521 ? scrollBody.getMaxIndent()
3522 : 0;
3523 saveNaturalColumnWidthIfNotSaved(columnIndex);
3524 if (isDefinedWidth()) {
3525 if (iw > width) {
3526 return iw;
3527 }
3528 return width;
3529 } else {
3530 if (iw > naturalWidth) {
3531
3532
3533 return iw;
3534 } else {
3535 return naturalWidth;
3536 }
3537 }
3538 }
3539
3540 public void setExpandRatio(float floatAttribute) {
3541 if (floatAttribute != expandRatio) {
3542 triggerLazyColumnAdjustment(false);
3543 }
3544 expandRatio = floatAttribute;
3545 }
3546
3547 public float getExpandRatio() {
3548 return expandRatio;
3549 }
3550
3551 public boolean isSorted() {
3552 return sorted;
3553 }
3554 }
3555
3556
3557
3558
3559
3560
3561 public class RowHeadersHeaderCell extends HeaderCell {
3562
3563 RowHeadersHeaderCell() {
3564 super(ROW_HEADER_COLUMN_KEY, "");
3565 updateStyleNames(VScrollTablePatched.this.getStylePrimaryName());
3566 }
3567
3568 @Override
3569 protected void updateStyleNames(String primaryStyleName) {
3570 super.updateStyleNames(primaryStyleName);
3571 setStyleName(primaryStyleName + "-header-cell-rowheader");
3572 }
3573
3574 @Override
3575 protected void handleCaptionEvent(Event event) {
3576
3577
3578 }
3579 }
3580
3581 public class TableHead extends Panel implements ActionOwner {
3582
3583 private static final int WRAPPER_WIDTH = 900000;
3584
3585 List<Widget> visibleCells = new ArrayList<Widget>();
3586
3587 Map<String, HeaderCell> availableCells = new HashMap<String, HeaderCell>();
3588
3589 protected Element div = DOM.createDiv();
3590 Element hTableWrapper = DOM.createDiv();
3591 Element hTableContainer = DOM.createDiv();
3592 Element table = DOM.createTable();
3593 Element headerTableBody = DOM.createTBody();
3594 Element tr = DOM.createTR();
3595
3596 private final Element columnSelector = DOM.createDiv();
3597
3598 private int focusedSlot = -1;
3599
3600 public TableHead() {
3601 if (BrowserInfo.get().isIE()) {
3602 table.setPropertyInt("cellSpacing", 0);
3603 }
3604
3605 hTableWrapper.getStyle().setOverflow(Overflow.HIDDEN);
3606 columnSelector.getStyle().setDisplay(Display.NONE);
3607
3608 DOM.appendChild(table, headerTableBody);
3609 DOM.appendChild(headerTableBody, tr);
3610 DOM.appendChild(hTableContainer, table);
3611 DOM.appendChild(hTableWrapper, hTableContainer);
3612 DOM.appendChild(div, hTableWrapper);
3613 DOM.appendChild(div, columnSelector);
3614 setElement(div);
3615
3616 DOM.sinkEvents(columnSelector, Event.ONCLICK);
3617
3618 availableCells.put(ROW_HEADER_COLUMN_KEY,
3619 new RowHeadersHeaderCell());
3620 }
3621
3622 protected void updateStyleNames(String primaryStyleName) {
3623 hTableWrapper.setClassName(primaryStyleName + "-header");
3624 columnSelector.setClassName(primaryStyleName + "-column-selector");
3625 setStyleName(primaryStyleName + "-header-wrap");
3626 for (HeaderCell c : availableCells.values()) {
3627 c.updateStyleNames(primaryStyleName);
3628 }
3629 }
3630
3631 public void resizeCaptionContainer(HeaderCell cell) {
3632 HeaderCell lastcell = getHeaderCell(visibleCells.size() - 1);
3633 int columnSelectorOffset = columnSelector.getOffsetWidth();
3634
3635 if (cell == lastcell && columnSelectorOffset > 0
3636 && !hasVerticalScrollbar()) {
3637
3638
3639 int columnTotalWidth = 0;
3640 for (Widget w : visibleCells) {
3641 int cellExtraWidth = w.getOffsetWidth();
3642 if (scrollBody != null
3643 && visibleCells
3644 .indexOf(w) == getHierarchyColumnIndex()
3645 && cellExtraWidth < scrollBody.getMaxIndent()) {
3646
3647
3648 columnTotalWidth += scrollBody.getMaxIndent();
3649 } else {
3650 columnTotalWidth += cellExtraWidth;
3651 }
3652 }
3653
3654 int divOffset = div.getOffsetWidth();
3655 if (columnTotalWidth >= divOffset - columnSelectorOffset) {
3656
3657
3658
3659
3660
3661 int offset = 0;
3662 int diff = divOffset - columnTotalWidth;
3663 if (diff < columnSelectorOffset && diff > 0) {
3664
3665
3666
3667
3668 offset = columnSelectorOffset - diff;
3669 } else {
3670
3671 offset = columnSelectorOffset;
3672 }
3673 lastcell.resizeCaptionContainer(offset);
3674 } else {
3675 cell.resizeCaptionContainer(0);
3676 }
3677 } else {
3678 cell.resizeCaptionContainer(0);
3679 }
3680 }
3681
3682 @Override
3683 public void clear() {
3684 for (String cid : availableCells.keySet()) {
3685 removeCell(cid);
3686 }
3687 availableCells.clear();
3688 availableCells.put(ROW_HEADER_COLUMN_KEY,
3689 new RowHeadersHeaderCell());
3690 }
3691
3692 public void updateCellsFromUIDL(UIDL uidl) {
3693 HashSet<String> updated = new HashSet<String>();
3694 boolean refreshContentWidths = initializedAndAttached
3695 && hadScrollBars != willHaveScrollbars();
3696 for (Object child : uidl) {
3697 final UIDL col = (UIDL) child;
3698 final String cid = col.getStringAttribute("cid");
3699 updated.add(cid);
3700
3701 String caption = buildCaptionHtmlSnippet(col);
3702 HeaderCell c = getHeaderCell(cid);
3703 if (c == null) {
3704 c = createHeaderCell(cid, caption);
3705 availableCells.put(cid, c);
3706 if (initializedAndAttached) {
3707
3708 initializedAndAttached = false;
3709 initialContentReceived = false;
3710 isNewBody = true;
3711 }
3712 } else {
3713 c.setText(caption);
3714
3715 }
3716
3717 c.setSorted(false);
3718 if (col.hasAttribute("sortable")) {
3719 c.setSortable(true);
3720 } else {
3721 c.setSortable(false);
3722 }
3723
3724
3725
3726 if (col.hasAttribute("align")) {
3727 c.setAlign(col.getStringAttribute("align").charAt(0));
3728 } else {
3729 c.setAlign(ALIGN_LEFT);
3730
3731 }
3732 if (col.hasAttribute("width") && !c.isResizing) {
3733
3734
3735 int width = col.getIntAttribute("width");
3736 int widthWithoutAddedIndent = width;
3737
3738
3739 int minWidth = c.getMinWidth(true, false);
3740 if (width < minWidth) {
3741 width = minWidth;
3742 }
3743 if (scrollBody != null && width != c.getWidthWithIndent()) {
3744
3745
3746
3747 final int newWidth = width;
3748 Scheduler.get().scheduleFinally(new ScheduledCommand() {
3749
3750 @Override
3751 public void execute() {
3752 final int colIx = getColIndexByKey(cid);
3753 setColWidth(colIx, newWidth, true);
3754 }
3755 });
3756 refreshContentWidths = true;
3757 } else {
3758
3759 minWidth = c.getMinWidth(false, false);
3760 if (widthWithoutAddedIndent < minWidth) {
3761 widthWithoutAddedIndent = minWidth;
3762 }
3763
3764 c.setWidth(widthWithoutAddedIndent, true);
3765 }
3766 } else if (col.hasAttribute("er")) {
3767 c.setExpandRatio(col.getFloatAttribute("er"));
3768 c.setUndefinedWidthFlagOnly();
3769 } else if (recalcWidths) {
3770 c.setUndefinedWidth();
3771
3772 } else {
3773 boolean hadExpandRatio = c.getExpandRatio() > 0;
3774 boolean hadDefinedWidth = c.isDefinedWidth();
3775 if (hadExpandRatio || hadDefinedWidth) {
3776
3777
3778
3779 c.setUndefinedWidth();
3780 c.setExpandRatio(0);
3781 refreshContentWidths = true;
3782 }
3783 }
3784
3785 if (col.hasAttribute("collapsed")) {
3786
3787
3788 if (c.isAttached()) {
3789 c.removeFromParent();
3790 headerChangedDuringUpdate = true;
3791 }
3792 }
3793 }
3794
3795 if (refreshContentWidths) {
3796
3797 Scheduler.get().scheduleFinally(new ScheduledCommand() {
3798
3799 @Override
3800 public void execute() {
3801 triggerLazyColumnAdjustment(true);
3802 }
3803 });
3804 }
3805
3806
3807 for (Iterator<String> cit = availableCells.keySet().iterator(); cit
3808 .hasNext();) {
3809 String cid = cit.next();
3810 if (!updated.contains(cid)) {
3811 removeCell(cid);
3812 cit.remove();
3813
3814
3815 initializedAndAttached = false;
3816 initialContentReceived = false;
3817 isNewBody = true;
3818 }
3819 }
3820 }
3821
3822 public void enableColumn(String cid, int index) {
3823 final HeaderCell c = getHeaderCell(cid);
3824 if (!c.isEnabled() || getHeaderCell(index) != c) {
3825 setHeaderCell(index, c);
3826 if (initializedAndAttached) {
3827 headerChangedDuringUpdate = true;
3828 }
3829 }
3830 }
3831
3832 public int getVisibleCellCount() {
3833 return visibleCells.size();
3834 }
3835
3836 public void setHorizontalScrollPosition(int scrollLeft) {
3837 hTableWrapper.setScrollLeft(scrollLeft);
3838 }
3839
3840 public void setColumnCollapsingAllowed(boolean cc) {
3841 if (cc) {
3842 columnSelector.getStyle().setDisplay(Display.BLOCK);
3843 } else {
3844 columnSelector.getStyle().setDisplay(Display.NONE);
3845 }
3846 }
3847
3848 public void disableBrowserIntelligence() {
3849 hTableContainer.getStyle().setWidth(WRAPPER_WIDTH, Unit.PX);
3850 }
3851
3852 public void enableBrowserIntelligence() {
3853 hTableContainer.getStyle().clearWidth();
3854 }
3855
3856 public void setHeaderCell(int index, HeaderCell cell) {
3857 if (cell.isEnabled()) {
3858
3859 DOM.removeChild(tr, cell.getElement());
3860 orphan(cell);
3861 visibleCells.remove(cell);
3862 }
3863 if (index < visibleCells.size()) {
3864
3865 DOM.insertChild(tr, cell.getElement(), index);
3866 adopt(cell);
3867 visibleCells.add(index, cell);
3868 } else if (index == visibleCells.size()) {
3869
3870 DOM.appendChild(tr, cell.getElement());
3871 adopt(cell);
3872 visibleCells.add(cell);
3873 } else {
3874 throw new RuntimeException(
3875 "Header cells must be appended in order");
3876 }
3877 }
3878
3879 public HeaderCell getHeaderCell(int index) {
3880 if (index >= 0 && index < visibleCells.size()) {
3881 return (HeaderCell) visibleCells.get(index);
3882 } else {
3883 return null;
3884 }
3885 }
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896 public HeaderCell getHeaderCell(String cid) {
3897 return availableCells.get(cid);
3898 }
3899
3900 public void moveCell(int oldIndex, int newIndex) {
3901 final HeaderCell hCell = getHeaderCell(oldIndex);
3902 final Element cell = hCell.getElement();
3903
3904 visibleCells.remove(oldIndex);
3905 DOM.removeChild(tr, cell);
3906
3907 DOM.insertChild(tr, cell, newIndex);
3908 visibleCells.add(newIndex, hCell);
3909 }
3910
3911 @Override
3912 public Iterator<Widget> iterator() {
3913 return visibleCells.iterator();
3914 }
3915
3916 @Override
3917 public boolean remove(Widget w) {
3918 if (visibleCells.contains(w)) {
3919 visibleCells.remove(w);
3920 orphan(w);
3921 DOM.removeChild(DOM.getParent(w.getElement()), w.getElement());
3922 return true;
3923 }
3924 return false;
3925 }
3926
3927 public void removeCell(String colKey) {
3928 final HeaderCell c = getHeaderCell(colKey);
3929 remove(c);
3930 }
3931
3932 private void focusSlot(int index) {
3933 removeSlotFocus();
3934 if (index > 0) {
3935 Element child = tr.getChild(index - 1).getFirstChild().cast();
3936 child.setClassName(
3937 VScrollTablePatched.this.getStylePrimaryName() + "-resizer");
3938 child.addClassName(VScrollTablePatched.this.getStylePrimaryName()
3939 + "-focus-slot-right");
3940 } else {
3941 Element child = tr.getChild(index).getFirstChild().cast();
3942 child.setClassName(
3943 VScrollTablePatched.this.getStylePrimaryName() + "-resizer");
3944 child.addClassName(VScrollTablePatched.this.getStylePrimaryName()
3945 + "-focus-slot-left");
3946 }
3947 focusedSlot = index;
3948 }
3949
3950 private void removeSlotFocus() {
3951 if (focusedSlot < 0) {
3952 return;
3953 }
3954 if (focusedSlot == 0) {
3955 Element child = tr.getChild(focusedSlot).getFirstChild().cast();
3956 child.setClassName(
3957 VScrollTablePatched.this.getStylePrimaryName() + "-resizer");
3958 } else if (focusedSlot > 0) {
3959 Element child = tr.getChild(focusedSlot - 1).getFirstChild()
3960 .cast();
3961 child.setClassName(
3962 VScrollTablePatched.this.getStylePrimaryName() + "-resizer");
3963 }
3964 focusedSlot = -1;
3965 }
3966
3967 @Override
3968 public void onBrowserEvent(Event event) {
3969 if (enabled) {
3970 if (event.getEventTarget().cast() == columnSelector) {
3971 final int left = DOM.getAbsoluteLeft(columnSelector);
3972 final int top = DOM.getAbsoluteTop(columnSelector)
3973 + DOM.getElementPropertyInt(columnSelector,
3974 "offsetHeight");
3975 client.getContextMenu().showAt(this, left, top);
3976 }
3977 }
3978 }
3979
3980 @Override
3981 protected void onDetach() {
3982 super.onDetach();
3983 if (client != null) {
3984 client.getContextMenu().ensureHidden(this);
3985 }
3986 }
3987
3988 class VisibleColumnAction extends Action {
3989
3990 String colKey;
3991 private boolean collapsed;
3992 private boolean noncollapsible = false;
3993 private VScrollTableRow currentlyFocusedRow;
3994
3995 public VisibleColumnAction(String colKey) {
3996 super(VScrollTablePatched.TableHead.this);
3997 this.colKey = colKey;
3998 caption = tHead.getHeaderCell(colKey).getCaption();
3999 currentlyFocusedRow = focusedRow;
4000 }
4001
4002 @Override
4003 public void execute() {
4004 if (noncollapsible) {
4005 return;
4006 }
4007 client.getContextMenu().hide();
4008
4009 if (collapsedColumns.contains(colKey)) {
4010 collapsedColumns.remove(colKey);
4011 } else {
4012 tHead.removeCell(colKey);
4013 collapsedColumns.add(colKey);
4014 triggerLazyColumnAdjustment(true);
4015 }
4016
4017
4018 client.updateVariable(
4019 paintableId, "collapsedcolumns", collapsedColumns
4020 .toArray(new String[collapsedColumns.size()]),
4021 false);
4022
4023 rowRequestHandler.refreshContent();
4024 lazyRevertFocusToRow(currentlyFocusedRow);
4025 }
4026
4027 public void setCollapsed(boolean b) {
4028 collapsed = b;
4029 }
4030
4031 public void setNoncollapsible(boolean b) {
4032 noncollapsible = b;
4033 }
4034
4035
4036
4037
4038
4039 @Override
4040 public String getHTML() {
4041 final StringBuilder buf = new StringBuilder();
4042 buf.append("<span class=\"");
4043 if (collapsed) {
4044 buf.append("v-off");
4045 } else {
4046 buf.append("v-on");
4047 }
4048 if (noncollapsible) {
4049 buf.append(" v-disabled");
4050 }
4051 buf.append("\">");
4052
4053 buf.append(super.getHTML());
4054 buf.append("</span>");
4055
4056 return buf.toString();
4057 }
4058
4059 }
4060
4061
4062
4063
4064
4065 @Override
4066 public Action[] getActions() {
4067 Object[] cols;
4068 if (columnReordering && columnOrder != null) {
4069 cols = columnOrder;
4070 } else {
4071
4072
4073 cols = visibleColOrder;
4074 cols = new Object[visibleColOrder.length
4075 + collapsedColumns.size()];
4076 int i;
4077 for (i = 0; i < visibleColOrder.length; i++) {
4078 cols[i] = visibleColOrder[i];
4079 }
4080 for (final String column : collapsedColumns) {
4081 cols[i++] = column;
4082 }
4083 }
4084 List<Action> actions = new ArrayList<Action>(cols.length);
4085
4086 for (Object col : cols) {
4087 final String cid = (String) col;
4088 boolean noncollapsible = noncollapsibleColumns.contains(cid);
4089
4090 if (noncollapsible
4091 && collapsibleMenuContent == CollapseMenuContent.COLLAPSIBLE_COLUMNS) {
4092 continue;
4093 }
4094
4095 final HeaderCell c = getHeaderCell(cid);
4096 final VisibleColumnAction a = new VisibleColumnAction(
4097 c.getColKey());
4098 a.setCaption(c.getCaption());
4099 if (!c.isEnabled()) {
4100 a.setCollapsed(true);
4101 }
4102 if (noncollapsible) {
4103 a.setNoncollapsible(true);
4104 }
4105 actions.add(a);
4106 }
4107 return actions.toArray(new Action[actions.size()]);
4108 }
4109
4110 @Override
4111 public ApplicationConnection getClient() {
4112 return client;
4113 }
4114
4115 @Override
4116 public String getPaintableId() {
4117 return paintableId;
4118 }
4119
4120
4121
4122
4123 public char[] getColumnAlignments() {
4124 final char[] aligns = new char[visibleCells.size()];
4125 int colIndex = 0;
4126 for (Widget w : visibleCells) {
4127 aligns[colIndex++] = ((HeaderCell) w).getAlign();
4128 }
4129 return aligns;
4130 }
4131
4132
4133
4134
4135
4136 public void disableAutoColumnWidthCalculation(HeaderCell source) {
4137 for (HeaderCell cell : availableCells.values()) {
4138 cell.disableAutoWidthCalculation();
4139 }
4140
4141
4142 List<HeaderCell> columns = new ArrayList<HeaderCell>(
4143 availableCells.values());
4144 columns.remove(source);
4145 sendColumnWidthUpdates(columns);
4146 forceRealignColumnHeaders();
4147 }
4148 }
4149
4150
4151
4152
4153 public class FooterCell extends Widget {
4154 private final Element td = DOM.createTD();
4155 private final Element captionContainer = DOM.createDiv();
4156 private char align = ALIGN_LEFT;
4157 private int width = -1;
4158 private float expandRatio = 0;
4159 private final String cid;
4160 boolean definedWidth = false;
4161 private int naturalWidth = -1;
4162
4163 public FooterCell(String colId, String headerText) {
4164 cid = colId;
4165
4166 setText(headerText);
4167
4168
4169 captionContainer.getStyle().setOverflow(Overflow.VISIBLE);
4170
4171 DOM.sinkEvents(captionContainer, Event.MOUSEEVENTS);
4172
4173 DOM.appendChild(td, captionContainer);
4174
4175 DOM.sinkEvents(td,
4176 Event.MOUSEEVENTS | Event.ONDBLCLICK | Event.ONCONTEXTMENU);
4177
4178 setElement(td);
4179
4180 updateStyleNames(VScrollTablePatched.this.getStylePrimaryName());
4181 }
4182
4183 protected void updateStyleNames(String primaryStyleName) {
4184 captionContainer
4185 .setClassName(primaryStyleName + "-footer-container");
4186 }
4187
4188
4189
4190
4191
4192
4193
4194 public void setText(String footerText) {
4195 if (footerText == null || footerText.equals("")) {
4196 footerText = " ";
4197 }
4198
4199 DOM.setInnerHTML(captionContainer, footerText);
4200 }
4201
4202
4203
4204
4205
4206
4207
4208
4209 public void setAlign(char c) {
4210 if (align != c) {
4211 switch (c) {
4212 case ALIGN_CENTER:
4213 captionContainer.getStyle().setTextAlign(TextAlign.CENTER);
4214 break;
4215 case ALIGN_RIGHT:
4216 captionContainer.getStyle().setTextAlign(TextAlign.RIGHT);
4217 break;
4218 default:
4219 captionContainer.getStyle().setTextAlign(TextAlign.LEFT);
4220 break;
4221 }
4222 }
4223 align = c;
4224 }
4225
4226
4227
4228
4229
4230
4231 public char getAlign() {
4232 return align;
4233 }
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245 public void setWidth(int w, boolean ensureDefinedWidth) {
4246
4247 if (ensureDefinedWidth) {
4248 definedWidth = true;
4249
4250 expandRatio = 0;
4251 }
4252 if (width == w) {
4253 return;
4254 }
4255 if (width == -1) {
4256
4257 captionContainer.getStyle().clearOverflow();
4258 }
4259 width = w;
4260 if (w == -1) {
4261 captionContainer.getStyle().clearWidth();
4262 setWidth("");
4263 } else {
4264
4265
4266
4267
4268 final int borderWidths = 1;
4269
4270
4271 captionContainer.getStyle().setPropertyPx("width",
4272 Math.max(w - borderWidths, 0));
4273
4274
4275
4276
4277
4278
4279 if (scrollBody != null) {
4280 int maxIndent = scrollBody.getMaxIndent();
4281 if (w < maxIndent && tFoot.visibleCells
4282 .indexOf(this) == getHierarchyColumnIndex()) {
4283
4284 w = maxIndent;
4285 }
4286 int tdWidth = w + scrollBody.getCellExtraWidth()
4287 - borderWidths;
4288 setWidth(Math.max(tdWidth, 0) + "px");
4289 } else {
4290 Scheduler.get().scheduleDeferred(new Command() {
4291
4292 @Override
4293 public void execute() {
4294 int tdWidth = width;
4295 int maxIndent = scrollBody.getMaxIndent();
4296 if (tdWidth < maxIndent
4297 && tFoot.visibleCells.indexOf(
4298 this) == getHierarchyColumnIndex()) {
4299
4300 tdWidth = maxIndent;
4301 }
4302 tdWidth += scrollBody.getCellExtraWidth()
4303 - borderWidths;
4304 setWidth(Math.max(tdWidth, 0) + "px");
4305 }
4306 });
4307 }
4308 }
4309 }
4310
4311
4312
4313
4314 public void setUndefinedWidth() {
4315 definedWidth = false;
4316 setWidth(-1, false);
4317 }
4318
4319
4320
4321
4322
4323
4324
4325 public boolean isDefinedWidth() {
4326 return definedWidth && width >= 0;
4327 }
4328
4329
4330
4331
4332
4333
4334 public int getWidth() {
4335 return width;
4336 }
4337
4338
4339
4340
4341
4342
4343
4344 public void setExpandRatio(float floatAttribute) {
4345 expandRatio = floatAttribute;
4346 }
4347
4348
4349
4350
4351
4352
4353 public float getExpandRatio() {
4354 return expandRatio;
4355 }
4356
4357
4358
4359
4360
4361
4362 public boolean isEnabled() {
4363 return getParent() != null;
4364 }
4365
4366
4367
4368
4369
4370 @Override
4371 public void onBrowserEvent(Event event) {
4372 if (enabled && event != null) {
4373 handleCaptionEvent(event);
4374
4375 if (DOM.eventGetType(event) == Event.ONMOUSEUP) {
4376 scrollBodyPanel.setFocus(true);
4377 }
4378 boolean stopPropagation = true;
4379 if (event.getTypeInt() == Event.ONCONTEXTMENU
4380 && !client.hasEventListeners(VScrollTablePatched.this,
4381 TableConstants.FOOTER_CLICK_EVENT_ID)) {
4382
4383
4384 stopPropagation = false;
4385 }
4386 if (stopPropagation) {
4387 event.stopPropagation();
4388 event.preventDefault();
4389 }
4390 }
4391 }
4392
4393
4394
4395
4396
4397
4398
4399 protected void handleCaptionEvent(Event event) {
4400 if (event.getTypeInt() == Event.ONMOUSEUP
4401 || event.getTypeInt() == Event.ONDBLCLICK) {
4402 fireFooterClickedEvent(event);
4403 }
4404 }
4405
4406
4407
4408
4409
4410
4411
4412
4413 private void fireFooterClickedEvent(Event event) {
4414 if (client.hasEventListeners(VScrollTablePatched.this,
4415 TableConstants.FOOTER_CLICK_EVENT_ID)) {
4416 MouseEventDetails details = MouseEventDetailsBuilder
4417 .buildMouseEventDetails(event);
4418 client.updateVariable(paintableId, "footerClickEvent",
4419 details.toString(), false);
4420 client.updateVariable(paintableId, "footerClickCID", cid, true);
4421 }
4422 }
4423
4424
4425
4426
4427
4428
4429 public String getColKey() {
4430 return cid;
4431 }
4432
4433
4434
4435
4436
4437
4438
4439 protected void saveNaturalColumnWidthIfNotSaved(int columnIndex) {
4440 if (naturalWidth < 0) {
4441
4442
4443
4444 final int hw = ((Element) getElement().getLastChild())
4445 .getOffsetWidth() + getHeaderPadding();
4446 if (columnIndex < 0) {
4447 columnIndex = 0;
4448 for (Widget widget : tHead) {
4449 if (widget == this) {
4450 break;
4451 }
4452 columnIndex++;
4453 }
4454 }
4455 final int cw = scrollBody.getColWidth(columnIndex);
4456 naturalWidth = (hw > cw ? hw : cw);
4457 }
4458 }
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470 public int getNaturalColumnWidth(int columnIndex) {
4471 final int iw = columnIndex == getHierarchyColumnIndex()
4472 ? scrollBody.getMaxIndent()
4473 : 0;
4474 saveNaturalColumnWidthIfNotSaved(columnIndex);
4475 if (isDefinedWidth()) {
4476 if (iw > width) {
4477 return iw;
4478 }
4479 return width;
4480 } else {
4481 if (iw > naturalWidth) {
4482 return iw;
4483 } else {
4484 return naturalWidth;
4485 }
4486 }
4487 }
4488
4489 public void setNaturalMinimumColumnWidth(int w) {
4490 naturalWidth = w;
4491 }
4492 }
4493
4494
4495
4496
4497
4498
4499 public class RowHeadersFooterCell extends FooterCell {
4500
4501 RowHeadersFooterCell() {
4502 super(ROW_HEADER_COLUMN_KEY, "");
4503 }
4504
4505 @Override
4506 protected void handleCaptionEvent(Event event) {
4507
4508
4509 }
4510 }
4511
4512
4513
4514
4515 public class TableFooter extends Panel {
4516
4517 private static final int WRAPPER_WIDTH = 900000;
4518
4519 List<Widget> visibleCells = new ArrayList<Widget>();
4520 Map<String, FooterCell> availableCells = new HashMap<String, FooterCell>();
4521
4522 Element div = DOM.createDiv();
4523 Element hTableWrapper = DOM.createDiv();
4524 Element hTableContainer = DOM.createDiv();
4525 Element table = DOM.createTable();
4526 Element headerTableBody = DOM.createTBody();
4527 Element tr = DOM.createTR();
4528
4529 public TableFooter() {
4530
4531 hTableWrapper.getStyle().setOverflow(Overflow.HIDDEN);
4532
4533 DOM.appendChild(table, headerTableBody);
4534 DOM.appendChild(headerTableBody, tr);
4535 DOM.appendChild(hTableContainer, table);
4536 DOM.appendChild(hTableWrapper, hTableContainer);
4537 DOM.appendChild(div, hTableWrapper);
4538 setElement(div);
4539
4540 availableCells.put(ROW_HEADER_COLUMN_KEY,
4541 new RowHeadersFooterCell());
4542
4543 updateStyleNames(VScrollTablePatched.this.getStylePrimaryName());
4544 }
4545
4546 protected void updateStyleNames(String primaryStyleName) {
4547 hTableWrapper.setClassName(primaryStyleName + "-footer");
4548 setStyleName(primaryStyleName + "-footer-wrap");
4549 for (FooterCell c : availableCells.values()) {
4550 c.updateStyleNames(primaryStyleName);
4551 }
4552 }
4553
4554 @Override
4555 public void clear() {
4556 for (String cid : availableCells.keySet()) {
4557 removeCell(cid);
4558 }
4559 availableCells.clear();
4560 availableCells.put(ROW_HEADER_COLUMN_KEY,
4561 new RowHeadersFooterCell());
4562 }
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572 @Override
4573 public boolean remove(Widget w) {
4574 if (visibleCells.contains(w)) {
4575 visibleCells.remove(w);
4576 orphan(w);
4577 DOM.removeChild(DOM.getParent(w.getElement()), w.getElement());
4578 return true;
4579 }
4580 return false;
4581 }
4582
4583
4584
4585
4586
4587
4588
4589 @Override
4590 public Iterator<Widget> iterator() {
4591 return visibleCells.iterator();
4592 }
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602 public FooterCell getFooterCell(String cid) {
4603 return availableCells.get(cid);
4604 }
4605
4606
4607
4608
4609
4610
4611
4612
4613 public FooterCell getFooterCell(int index) {
4614 if (index < visibleCells.size()) {
4615 return (FooterCell) visibleCells.get(index);
4616 } else {
4617 return null;
4618 }
4619 }
4620
4621
4622
4623
4624
4625
4626
4627 public void updateCellsFromUIDL(UIDL uidl) {
4628 HashSet<String> updated = new HashSet<String>();
4629 for (Object child : uidl) {
4630 final UIDL col = (UIDL) child;
4631 final String cid = col.getStringAttribute("cid");
4632 updated.add(cid);
4633
4634 String caption = col.hasAttribute("fcaption")
4635 ? col.getStringAttribute("fcaption")
4636 : "";
4637 FooterCell c = getFooterCell(cid);
4638 if (c == null) {
4639 c = new FooterCell(cid, caption);
4640 availableCells.put(cid, c);
4641 if (initializedAndAttached) {
4642
4643 initializedAndAttached = false;
4644 initialContentReceived = false;
4645 isNewBody = true;
4646 }
4647 } else {
4648 c.setText(caption);
4649 }
4650
4651 if (col.hasAttribute("align")) {
4652 c.setAlign(col.getStringAttribute("align").charAt(0));
4653 } else {
4654 c.setAlign(ALIGN_LEFT);
4655
4656 }
4657 if (col.hasAttribute("width")) {
4658 if (scrollBody == null || isNewBody) {
4659
4660
4661
4662 final int width = col.getIntAttribute("width");
4663 c.setWidth(width, true);
4664 }
4665 } else if (recalcWidths) {
4666 c.setUndefinedWidth();
4667 }
4668 if (col.hasAttribute("er")) {
4669 c.setExpandRatio(col.getFloatAttribute("er"));
4670 }
4671 if (col.hasAttribute("collapsed")) {
4672
4673
4674 if (c.isAttached()) {
4675 c.removeFromParent();
4676 headerChangedDuringUpdate = true;
4677 }
4678 }
4679 }
4680
4681
4682 for (Iterator<String> cit = availableCells.keySet().iterator(); cit
4683 .hasNext();) {
4684 String cid = cit.next();
4685 if (!updated.contains(cid)) {
4686 removeCell(cid);
4687 cit.remove();
4688 }
4689 }
4690 }
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700 public void setFooterCell(int index, FooterCell cell) {
4701 if (cell.isEnabled()) {
4702
4703 DOM.removeChild(tr, cell.getElement());
4704 orphan(cell);
4705 visibleCells.remove(cell);
4706 }
4707 if (index < visibleCells.size()) {
4708
4709 DOM.insertChild(tr, cell.getElement(), index);
4710 adopt(cell);
4711 visibleCells.add(index, cell);
4712 } else if (index == visibleCells.size()) {
4713
4714 DOM.appendChild(tr, cell.getElement());
4715 adopt(cell);
4716 visibleCells.add(cell);
4717 } else {
4718 throw new RuntimeException(
4719 "Header cells must be appended in order");
4720 }
4721 }
4722
4723
4724
4725
4726
4727
4728
4729 public void removeCell(String colKey) {
4730 final FooterCell c = getFooterCell(colKey);
4731 remove(c);
4732 }
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742 public void enableColumn(String cid, int index) {
4743 final FooterCell c = getFooterCell(cid);
4744 if (!c.isEnabled() || getFooterCell(index) != c) {
4745 setFooterCell(index, c);
4746 if (initializedAndAttached) {
4747 headerChangedDuringUpdate = true;
4748 }
4749 }
4750 }
4751
4752
4753
4754
4755 public void disableBrowserIntelligence() {
4756 hTableContainer.getStyle().setWidth(WRAPPER_WIDTH, Unit.PX);
4757 }
4758
4759
4760
4761
4762 public void enableBrowserIntelligence() {
4763 hTableContainer.getStyle().clearWidth();
4764 }
4765
4766
4767
4768
4769
4770
4771
4772
4773 public void setHorizontalScrollPosition(int scrollLeft) {
4774 hTableWrapper.setScrollLeft(scrollLeft);
4775 }
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785 public void moveCell(int oldIndex, int newIndex) {
4786 final FooterCell hCell = getFooterCell(oldIndex);
4787 final Element cell = hCell.getElement();
4788
4789 visibleCells.remove(oldIndex);
4790 DOM.removeChild(tr, cell);
4791
4792 DOM.insertChild(tr, cell, newIndex);
4793 visibleCells.add(newIndex, hCell);
4794 }
4795 }
4796
4797
4798
4799
4800
4801
4802
4803 public class VScrollTableBody extends Panel {
4804 protected VScrollTableRow createScrollTableRow(UIDL uidl, char[] aligns) {
4805 return new VScrollTableRow(uidl, aligns);
4806 }
4807
4808 protected VScrollTableRow createScrollTableRow() {
4809 return new VScrollTableRow();
4810 }
4811
4812 public static final int DEFAULT_ROW_HEIGHT = 24;
4813
4814 private double rowHeight = -1;
4815
4816 public final LinkedList<Widget> renderedRows = new LinkedList<Widget>();
4817
4818
4819
4820
4821
4822
4823 private boolean tBodyMeasurementsDone = false;
4824
4825 Element preSpacer = DOM.createDiv();
4826 Element postSpacer = DOM.createDiv();
4827
4828 Element container = DOM.createDiv();
4829
4830 TableSectionElement tBodyElement = Document.get().createTBodyElement();
4831 Element table = DOM.createTable();
4832
4833 private int firstRendered;
4834 private int lastRendered;
4835
4836 private char[] aligns;
4837
4838 protected VScrollTableBody() {
4839 constructDOM();
4840 setElement(container);
4841 }
4842
4843 public void setLastRendered(int lastRendered) {
4844 if (totalRows >= 0 && lastRendered > totalRows) {
4845 getLogger().info("setLastRendered: " + this.lastRendered + " -> "
4846 + lastRendered);
4847 this.lastRendered = totalRows - 1;
4848 } else {
4849 this.lastRendered = lastRendered;
4850 }
4851 }
4852
4853 public int getLastRendered() {
4854
4855 return lastRendered;
4856 }
4857
4858 public int getFirstRendered() {
4859
4860 return firstRendered;
4861 }
4862
4863 public VScrollTableRow getRowByRowIndex(int indexInTable) {
4864 int internalIndex = indexInTable - firstRendered;
4865 if (internalIndex >= 0 && internalIndex < renderedRows.size()) {
4866 return (VScrollTableRow) renderedRows.get(internalIndex);
4867 } else {
4868 return null;
4869 }
4870 }
4871
4872
4873
4874
4875 public int getRequiredHeight() {
4876 return preSpacer.getOffsetHeight() + postSpacer.getOffsetHeight()
4877 + WidgetUtil.getRequiredHeight(table);
4878 }
4879
4880 private void constructDOM() {
4881 if (BrowserInfo.get().isIE()) {
4882 table.setPropertyInt("cellSpacing", 0);
4883 }
4884
4885 table.appendChild(tBodyElement);
4886 DOM.appendChild(container, preSpacer);
4887 DOM.appendChild(container, table);
4888 DOM.appendChild(container, postSpacer);
4889 if (BrowserInfo.get().requiresTouchScrollDelegate()) {
4890 NodeList<Node> childNodes = container.getChildNodes();
4891 for (int i = 0; i < childNodes.getLength(); i++) {
4892 Element item = (Element) childNodes.getItem(i);
4893 item.getStyle().setProperty("webkitTransform",
4894 "translate3d(0,0,0)");
4895 }
4896 }
4897 updateStyleNames(VScrollTablePatched.this.getStylePrimaryName());
4898 }
4899
4900 protected void updateStyleNames(String primaryStyleName) {
4901 table.setClassName(primaryStyleName + "-table");
4902 preSpacer.setClassName(primaryStyleName + "-row-spacer");
4903 postSpacer.setClassName(primaryStyleName + "-row-spacer");
4904 for (Widget w : renderedRows) {
4905 VScrollTableRow row = (VScrollTableRow) w;
4906 row.updateStyleNames(primaryStyleName);
4907 }
4908 }
4909
4910 public int getAvailableWidth() {
4911 int availW = scrollBodyPanel.getOffsetWidth() - getBorderWidth();
4912 return availW;
4913 }
4914
4915 public void renderInitialRows(UIDL rowData, int firstIndex, int rows) {
4916 firstRendered = firstIndex;
4917 setLastRendered(firstIndex + rows - 1);
4918 aligns = tHead.getColumnAlignments();
4919 for (final Object child : rowData) {
4920 final VScrollTableRow row = createRow((UIDL) child, aligns);
4921 addRow(row);
4922 }
4923 if (isAttached()) {
4924 fixSpacers();
4925 }
4926 }
4927
4928 public void renderRows(UIDL rowData, int firstIndex, int rows) {
4929
4930 aligns = tHead.getColumnAlignments();
4931 final Iterator<?> it = rowData.iterator();
4932 if (firstIndex == lastRendered + 1) {
4933 while (it.hasNext()) {
4934 final VScrollTableRow row = prepareRow((UIDL) it.next());
4935 addRow(row);
4936 setLastRendered(lastRendered + 1);
4937 }
4938 fixSpacers();
4939 } else if (firstIndex + rows == firstRendered) {
4940 final VScrollTableRow[] rowArray = new VScrollTableRow[rows];
4941 int i = rows;
4942
4943 while (it.hasNext()) {
4944 i--;
4945 rowArray[i] = prepareRow((UIDL) it.next());
4946 }
4947
4948 for (i = 0; i < rows; i++) {
4949 addRowBeforeFirstRendered(rowArray[i]);
4950 firstRendered--;
4951 }
4952 } else {
4953
4954
4955
4956
4957
4958
4959 boolean temp = postponeSanityCheckForLastRendered;
4960 postponeSanityCheckForLastRendered = true;
4961 while (lastRendered + 1 > firstRendered) {
4962 unlinkRow(false);
4963 }
4964 postponeSanityCheckForLastRendered = temp;
4965 VScrollTableRow row = prepareRow((UIDL) it.next());
4966 firstRendered = firstIndex;
4967 setLastRendered(firstIndex - 1);
4968 addRow(row);
4969 setLastRendered(lastRendered + 1);
4970 setContainerHeight();
4971 fixSpacers();
4972
4973 while (it.hasNext()) {
4974 addRow(prepareRow((UIDL) it.next()));
4975 setLastRendered(lastRendered + 1);
4976 }
4977
4978 fixSpacers();
4979 }
4980
4981
4982
4983 ensureCacheFilled();
4984 }
4985
4986
4987
4988
4989
4990
4991 protected void ensureCacheFilled() {
4992
4993
4994
4995
4996 if (isLazyScrollerActive()) {
4997 return;
4998 }
4999
5000 int reactFirstRow = (int) (firstRowInViewPort
5001 - pageLength * cacheReactRate);
5002 int reactLastRow = (int) (firstRowInViewPort + pageLength
5003 + pageLength * cacheReactRate);
5004 if (reactFirstRow < 0) {
5005 reactFirstRow = 0;
5006 }
5007 if (reactLastRow >= totalRows) {
5008 reactLastRow = totalRows - 1;
5009 }
5010 if (lastRendered < reactFirstRow || firstRendered > reactLastRow) {
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020 rowRequestHandler.triggerRowFetch(reactFirstRow,
5021 reactLastRow - reactFirstRow + 1, 1);
5022 } else if (lastRendered < reactLastRow) {
5023
5024 rowRequestHandler.triggerRowFetch(lastRendered + 1,
5025 reactLastRow - lastRendered, 1);
5026 } else if (firstRendered > reactFirstRow) {
5027
5028
5029
5030
5031
5032
5033
5034
5035 rowRequestHandler.triggerRowFetch(reactFirstRow,
5036 firstRendered - reactFirstRow, 1);
5037 }
5038 }
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049 protected List<VScrollTableRow> insertRows(UIDL rowData, int firstIndex,
5050 int rows) {
5051 aligns = tHead.getColumnAlignments();
5052 final Iterator<?> it = rowData.iterator();
5053 List<VScrollTableRow> insertedRows = new ArrayList<VScrollTableRow>();
5054
5055 if (firstIndex == lastRendered + 1) {
5056 while (it.hasNext()) {
5057 final VScrollTableRow row = prepareRow((UIDL) it.next());
5058 addRow(row);
5059 insertedRows.add(row);
5060 if (postponeSanityCheckForLastRendered) {
5061 lastRendered++;
5062 } else {
5063 setLastRendered(lastRendered + 1);
5064 }
5065 }
5066 fixSpacers();
5067 } else if (firstIndex + rows == firstRendered) {
5068 final VScrollTableRow[] rowArray = new VScrollTableRow[rows];
5069 int i = rows;
5070 while (it.hasNext()) {
5071 i--;
5072 rowArray[i] = prepareRow((UIDL) it.next());
5073 }
5074 for (i = 0; i < rows; i++) {
5075 addRowBeforeFirstRendered(rowArray[i]);
5076 insertedRows.add(rowArray[i]);
5077 firstRendered--;
5078 }
5079 } else {
5080
5081 int ix = firstIndex;
5082 while (it.hasNext()) {
5083 VScrollTableRow row = prepareRow((UIDL) it.next());
5084 insertRowAt(row, ix);
5085 insertedRows.add(row);
5086 if (postponeSanityCheckForLastRendered) {
5087 lastRendered++;
5088 } else {
5089 setLastRendered(lastRendered + 1);
5090 }
5091 ix++;
5092 }
5093 fixSpacers();
5094 }
5095 return insertedRows;
5096 }
5097
5098 protected List<VScrollTableRow> insertAndReindexRows(UIDL rowData,
5099 int firstIndex, int rows) {
5100 List<VScrollTableRow> inserted = insertRows(rowData, firstIndex,
5101 rows);
5102 int actualIxOfFirstRowAfterInserted = firstIndex + rows
5103 - firstRendered;
5104 for (int ix = actualIxOfFirstRowAfterInserted; ix < renderedRows
5105 .size(); ix++) {
5106 VScrollTableRow r = (VScrollTableRow) renderedRows.get(ix);
5107 r.setIndex(r.getIndex() + rows);
5108 }
5109 setContainerHeight();
5110 return inserted;
5111 }
5112
5113 protected void insertRowsDeleteBelow(UIDL rowData, int firstIndex,
5114 int rows) {
5115 unlinkAllRowsStartingAt(firstIndex);
5116 insertRows(rowData, firstIndex, rows);
5117 setContainerHeight();
5118 }
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129 private VScrollTableRow prepareRow(UIDL uidl) {
5130 final VScrollTableRow row = createRow(uidl, aligns);
5131 row.initCellWidths();
5132 return row;
5133 }
5134
5135 protected VScrollTableRow createRow(UIDL uidl, char[] aligns2) {
5136 if (uidl.hasAttribute("gen_html")) {
5137
5138 return new VScrollTableGeneratedRow(uidl, aligns2);
5139 }
5140 return createScrollTableRow(uidl, aligns2);
5141 }
5142
5143 private void addRowBeforeFirstRendered(VScrollTableRow row) {
5144 row.setIndex(firstRendered - 1);
5145 if (row.isSelected()) {
5146 row.addStyleName("v-selected");
5147 }
5148 tBodyElement.insertBefore(row.getElement(),
5149 tBodyElement.getFirstChild());
5150 adopt(row);
5151 renderedRows.add(0, row);
5152 }
5153
5154 private void addRow(VScrollTableRow row) {
5155 row.setIndex(firstRendered + renderedRows.size());
5156 if (row.isSelected()) {
5157 row.addStyleName("v-selected");
5158 }
5159 tBodyElement.appendChild(row.getElement());
5160
5161
5162 renderedRows.add(row);
5163 adopt(row);
5164 }
5165
5166 private void insertRowAt(VScrollTableRow row, int index) {
5167 row.setIndex(index);
5168 if (row.isSelected()) {
5169 row.addStyleName("v-selected");
5170 }
5171 if (index > 0) {
5172 VScrollTableRow sibling = getRowByRowIndex(index - 1);
5173 tBodyElement.insertAfter(row.getElement(),
5174 sibling.getElement());
5175 } else {
5176 VScrollTableRow sibling = getRowByRowIndex(index);
5177 tBodyElement.insertBefore(row.getElement(),
5178 sibling.getElement());
5179 }
5180 adopt(row);
5181 int actualIx = index - firstRendered;
5182 renderedRows.add(actualIx, row);
5183 }
5184
5185 @Override
5186 public Iterator<Widget> iterator() {
5187 return renderedRows.iterator();
5188 }
5189
5190
5191
5192
5193 protected boolean unlinkRow(boolean fromBeginning) {
5194 if (lastRendered - firstRendered < 0) {
5195 return false;
5196 }
5197 int actualIx;
5198 if (fromBeginning) {
5199 actualIx = 0;
5200 firstRendered++;
5201 } else {
5202 actualIx = renderedRows.size() - 1;
5203 if (postponeSanityCheckForLastRendered) {
5204 --lastRendered;
5205 } else {
5206 setLastRendered(lastRendered - 1);
5207 }
5208 }
5209 if (actualIx >= 0) {
5210 unlinkRowAtActualIndex(actualIx);
5211 fixSpacers();
5212 return true;
5213 }
5214 return false;
5215 }
5216
5217 protected void unlinkRows(int firstIndex, int count) {
5218 if (count < 1) {
5219 return;
5220 }
5221 if (firstRendered > firstIndex
5222 && firstRendered < firstIndex + count) {
5223 count = count - (firstRendered - firstIndex);
5224 firstIndex = firstRendered;
5225 }
5226 int lastIndex = firstIndex + count - 1;
5227 if (lastRendered < lastIndex) {
5228 lastIndex = lastRendered;
5229 }
5230 for (int ix = lastIndex; ix >= firstIndex; ix--) {
5231 unlinkRowAtActualIndex(actualIndex(ix));
5232 if (postponeSanityCheckForLastRendered) {
5233
5234 lastRendered--;
5235 } else {
5236 setLastRendered(lastRendered - 1);
5237 }
5238 }
5239 fixSpacers();
5240 }
5241
5242 protected void unlinkAndReindexRows(int firstIndex, int count) {
5243 unlinkRows(firstIndex, count);
5244 int actualFirstIx = firstIndex - firstRendered;
5245 for (int ix = actualFirstIx; ix < renderedRows.size(); ix++) {
5246 VScrollTableRow r = (VScrollTableRow) renderedRows.get(ix);
5247 r.setIndex(r.getIndex() - count);
5248 }
5249 setContainerHeight();
5250 }
5251
5252 protected void unlinkAllRowsStartingAt(int index) {
5253 if (firstRendered > index) {
5254 index = firstRendered;
5255 }
5256 for (int ix = renderedRows.size() - 1; ix >= index; ix--) {
5257 unlinkRowAtActualIndex(actualIndex(ix));
5258 setLastRendered(lastRendered - 1);
5259 }
5260 fixSpacers();
5261 }
5262
5263 private int actualIndex(int index) {
5264 return index - firstRendered;
5265 }
5266
5267 private void unlinkRowAtActualIndex(int index) {
5268 final VScrollTableRow toBeRemoved = (VScrollTableRow) renderedRows
5269 .get(index);
5270 tBodyElement.removeChild(toBeRemoved.getElement());
5271 orphan(toBeRemoved);
5272 renderedRows.remove(index);
5273 }
5274
5275 @Override
5276 public boolean remove(Widget w) {
5277 throw new UnsupportedOperationException();
5278 }
5279
5280
5281
5282
5283
5284 private void setContainerHeight() {
5285 fixSpacers();
5286 container.getStyle().setHeight(measureRowHeightOffset(totalRows),
5287 Unit.PX);
5288 }
5289
5290 private void fixSpacers() {
5291 int prepx = measureRowHeightOffset(firstRendered);
5292 if (prepx < 0) {
5293 prepx = 0;
5294 }
5295 preSpacer.getStyle().setPropertyPx("height", prepx);
5296 int postpx;
5297 if (pageLength == 0 && totalRows == pageLength) {
5298
5299
5300
5301
5302
5303
5304 postpx = measureRowHeightOffset(1);
5305 } else {
5306 postpx = measureRowHeightOffset(totalRows - 1)
5307 - measureRowHeightOffset(lastRendered);
5308 }
5309
5310 if (postpx < 0) {
5311 postpx = 0;
5312 }
5313 postSpacer.getStyle().setPropertyPx("height", postpx);
5314 }
5315
5316 public double getRowHeight() {
5317 return getRowHeight(false);
5318 }
5319
5320 public double getRowHeight(boolean forceUpdate) {
5321 if (tBodyMeasurementsDone && !forceUpdate) {
5322 return rowHeight;
5323 } else {
5324 if (tBodyElement.getRows().getLength() > 0 && getTableHeight() > 0) {
5325 int tableHeight = getTableHeight();
5326 int rowCount = tBodyElement.getRows().getLength();
5327 rowHeight = tableHeight / (double) rowCount;
5328 } else {
5329
5330 if (!Double.isNaN(lastKnownRowHeight)) {
5331
5332 if (BrowserInfo.get().isIE()) {
5333
5334
5335
5336
5337
5338
5339
5340
5341 getTableHeight();
5342 }
5343 rowHeight = lastKnownRowHeight;
5344 } else if (isAttached()) {
5345
5346 VScrollTableRow scrollTableRow = createScrollTableRow();
5347 tBodyElement.appendChild(scrollTableRow.getElement());
5348 getRowHeight(forceUpdate);
5349 tBodyElement.removeChild(scrollTableRow.getElement());
5350 } else {
5351
5352 return DEFAULT_ROW_HEIGHT;
5353 }
5354 }
5355 lastKnownRowHeight = rowHeight;
5356 tBodyMeasurementsDone = true;
5357 return rowHeight;
5358 }
5359 }
5360
5361 public int getTableHeight() {
5362 return table.getOffsetHeight();
5363 }
5364
5365
5366
5367
5368
5369
5370
5371 public int getColWidth(int columnIndex) {
5372 if (tBodyMeasurementsDone) {
5373 if (renderedRows.isEmpty()) {
5374
5375 return 0;
5376 }
5377 for (Widget row : renderedRows) {
5378 if (!(row instanceof VScrollTableGeneratedRow)) {
5379 TableRowElement tr = row.getElement().cast();
5380
5381 if (columnIndex < tr.getChildCount()) {
5382 Element wrapperdiv = tr.getCells()
5383 .getItem(columnIndex).getFirstChildElement()
5384 .cast();
5385 return wrapperdiv.getOffsetWidth();
5386 }
5387 }
5388 }
5389 return 0;
5390 } else {
5391 return 0;
5392 }
5393 }
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410 public void setColWidth(int colIndex, int w) {
5411 for (Widget row : renderedRows) {
5412 ((VScrollTableRow) row).setCellWidth(colIndex, w);
5413 }
5414 }
5415
5416 private int cellExtraWidth = -1;
5417
5418
5419
5420
5421 private int getCellExtraWidth() {
5422 if (!isVisibleInHierarchy()) {
5423 return 0;
5424 }
5425 if (cellExtraWidth < 0) {
5426 detectExtrawidth();
5427 }
5428 return cellExtraWidth;
5429 }
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441 protected int getMaxIndent() {
5442 return 0;
5443 }
5444
5445
5446
5447
5448
5449
5450
5451 protected void calculateMaxIndent() {
5452
5453 }
5454
5455 private void detectExtrawidth() {
5456 NodeList<TableRowElement> rows = tBodyElement.getRows();
5457 if (rows.getLength() == 0) {
5458
5459 VScrollTableRow scrollTableRow = createScrollTableRow();
5460 scrollTableRow.updateStyleNames(
5461 VScrollTablePatched.this.getStylePrimaryName());
5462 tBodyElement.appendChild(scrollTableRow.getElement());
5463 detectExtrawidth();
5464 tBodyElement.removeChild(scrollTableRow.getElement());
5465 } else {
5466 boolean noCells = false;
5467 TableRowElement item = rows.getItem(0);
5468 TableCellElement firstTD = item.getCells().getItem(0);
5469 if (firstTD == null) {
5470
5471
5472 noCells = true;
5473 VScrollTableRow next = (VScrollTableRow) iterator().next();
5474 boolean sorted = tHead.getHeaderCell(0) != null
5475 ? tHead.getHeaderCell(0).isSorted()
5476 : false;
5477 next.addCell(null, "", ALIGN_LEFT, "", true, sorted);
5478 firstTD = item.getCells().getItem(0);
5479 }
5480 com.google.gwt.dom.client.Element wrapper = firstTD
5481 .getFirstChildElement();
5482 cellExtraWidth = firstTD.getOffsetWidth()
5483 - wrapper.getOffsetWidth();
5484 if (noCells) {
5485 firstTD.getParentElement().removeChild(firstTD);
5486 }
5487 }
5488 }
5489
5490 public void moveCol(int oldIndex, int newIndex) {
5491
5492
5493 for (Widget w : this) {
5494 final VScrollTableRow row = (VScrollTableRow) w;
5495
5496 final Element td = DOM.getChild(row.getElement(), oldIndex);
5497 if (td != null) {
5498 DOM.removeChild(row.getElement(), td);
5499
5500 DOM.insertChild(row.getElement(), td, newIndex);
5501 }
5502 }
5503
5504 }
5505
5506
5507
5508
5509
5510 private void restoreRowVisibility() {
5511 for (Widget row : renderedRows) {
5512 row.getElement().getStyle().setProperty("visibility", "");
5513 }
5514 }
5515
5516 public int indexOf(Widget row) {
5517 int relIx = -1;
5518 for (int ix = 0; ix < renderedRows.size(); ix++) {
5519 if (renderedRows.get(ix) == row) {
5520 relIx = ix;
5521 break;
5522 }
5523 }
5524 if (relIx >= 0) {
5525 return firstRendered + relIx;
5526 }
5527 return -1;
5528 }
5529
5530 public class VScrollTableRow extends Panel
5531 implements ActionOwner, ContextMenuOwner {
5532
5533 private static final int TOUCHSCROLL_TIMEOUT = 500;
5534 private static final int DRAGMODE_MULTIROW = 2;
5535 protected List<Widget> childWidgets = new ArrayList<Widget>();
5536 private boolean selected = false;
5537 protected final int rowKey;
5538
5539 private String[] actionKeys = null;
5540 private final TableRowElement rowElement;
5541 private int index;
5542 private Event touchStart;
5543
5544 private static final int TOUCH_CONTEXT_MENU_TIMEOUT = 500;
5545 private Timer contextTouchTimeout;
5546 private Timer dragTouchTimeout;
5547 private int touchStartY;
5548 private int touchStartX;
5549
5550 private TouchContextProvider touchContextProvider = new TouchContextProvider(
5551 this);
5552
5553 private TooltipInfo tooltipInfo = null;
5554 private Map<TableCellElement, TooltipInfo> cellToolTips = new HashMap<TableCellElement, TooltipInfo>();
5555 private boolean isDragging = false;
5556 protected String rowStyle = null;
5557 protected boolean applyZeroWidthFix = true;
5558
5559 private VScrollTableRow(int rowKey) {
5560 this.rowKey = rowKey;
5561 rowElement = Document.get().createTRElement();
5562 setElement(rowElement);
5563 DOM.sinkEvents(getElement(),
5564 Event.MOUSEEVENTS | Event.TOUCHEVENTS | Event.ONDBLCLICK
5565 | Event.ONCONTEXTMENU
5566 | VTooltip.TOOLTIP_EVENTS);
5567 }
5568
5569 public VScrollTableRow(UIDL uidl, char[] aligns) {
5570 this(uidl.getIntAttribute("key"));
5571
5572
5573
5574
5575
5576 getElement().getStyle().setProperty("visibility", "hidden");
5577
5578 rowStyle = uidl.getStringAttribute("rowstyle");
5579 updateStyleNames(VScrollTablePatched.this.getStylePrimaryName());
5580
5581 String rowDescription = uidl.getStringAttribute("rowdescr");
5582 if (rowDescription != null && !rowDescription.equals("")) {
5583 tooltipInfo = new TooltipInfo(rowDescription, null, this);
5584 } else {
5585 tooltipInfo = null;
5586 }
5587
5588 tHead.getColumnAlignments();
5589 int col = 0;
5590 int visibleColumnIndex = -1;
5591
5592
5593 if (showRowHeaders) {
5594 boolean sorted = tHead.getHeaderCell(col).isSorted();
5595 addCell(uidl, buildCaptionHtmlSnippet(uidl), aligns[col++],
5596 "rowheader", true, sorted);
5597 visibleColumnIndex++;
5598 }
5599
5600 if (uidl.hasAttribute("al")) {
5601 actionKeys = uidl.getStringArrayAttribute("al");
5602 }
5603
5604 addCellsFromUIDL(uidl, aligns, col, visibleColumnIndex);
5605
5606 if (uidl.hasAttribute("selected") && !isSelected()) {
5607 toggleSelection();
5608 }
5609 }
5610
5611 protected void updateStyleNames(String primaryStyleName) {
5612
5613 if (getStylePrimaryName().contains("odd")) {
5614 setStyleName(primaryStyleName + "-row-odd");
5615 } else {
5616 setStyleName(primaryStyleName + "-row");
5617 }
5618
5619 if (rowStyle != null) {
5620 addStyleName(primaryStyleName + "-row-" + rowStyle);
5621 }
5622
5623 for (int i = 0; i < rowElement.getChildCount(); i++) {
5624 TableCellElement cell = (TableCellElement) rowElement
5625 .getChild(i);
5626 updateCellStyleNames(cell, primaryStyleName);
5627 }
5628 }
5629
5630 public TooltipInfo getTooltipInfo() {
5631 return tooltipInfo;
5632 }
5633
5634
5635
5636
5637 public VScrollTableRow() {
5638 this(0);
5639 addCell(null, "_", 'b', "", true, false);
5640 }
5641
5642 protected void initCellWidths() {
5643 final int cells = tHead.getVisibleCellCount();
5644 for (int i = 0; i < cells; i++) {
5645 int w = VScrollTablePatched.this.getColWidth(getColKeyByIndex(i));
5646 if (w < 0) {
5647 w = 0;
5648 }
5649 setCellWidth(i, w);
5650 }
5651 }
5652
5653 protected void setCellWidth(int cellIx, int width) {
5654 final Element cell = DOM.getChild(getElement(), cellIx);
5655 Style wrapperStyle = cell.getFirstChildElement().getStyle();
5656 int wrapperWidth = width;
5657 if (BrowserInfo.get().isSafariOrIOS()
5658 || BrowserInfo.get().isOpera10()) {
5659
5660
5661
5662
5663
5664 if (applyZeroWidthFix && width == 0) {
5665 wrapperWidth = 1;
5666 wrapperStyle.setMarginRight(-1, Unit.PX);
5667 } else {
5668 wrapperStyle.clearMarginRight();
5669 }
5670 }
5671 wrapperStyle.setPropertyPx("width", wrapperWidth);
5672 cell.getStyle().setPropertyPx("width", width);
5673 }
5674
5675 protected void addCellsFromUIDL(UIDL uidl, char[] aligns, int col,
5676 int visibleColumnIndex) {
5677 for (final Object cell : uidl) {
5678 visibleColumnIndex++;
5679
5680 String columnId = visibleColOrder[visibleColumnIndex];
5681
5682 String style = "";
5683 if (uidl.hasAttribute("style-" + columnId)) {
5684 style = uidl.getStringAttribute("style-" + columnId);
5685 }
5686
5687 String description = null;
5688 if (uidl.hasAttribute("descr-" + columnId)) {
5689 description = uidl
5690 .getStringAttribute("descr-" + columnId);
5691 }
5692
5693 boolean sorted = tHead.getHeaderCell(col).isSorted();
5694 if (cell instanceof String) {
5695 addCell(uidl, cell.toString(), aligns[col++], style,
5696 isRenderHtmlInCells(), sorted, description);
5697 } else {
5698 final ComponentConnector cellContent = client
5699 .getPaintable((UIDL) cell);
5700
5701 addCell(uidl, cellContent.getWidget(), aligns[col++],
5702 style, sorted, description);
5703 }
5704 }
5705 }
5706
5707
5708
5709
5710
5711
5712
5713 protected boolean isRenderHtmlInCells() {
5714 return false;
5715 }
5716
5717
5718
5719
5720
5721
5722 public boolean isInViewPort() {
5723 int absoluteTop = getAbsoluteTop();
5724 int absoluteBottom = absoluteTop + getOffsetHeight();
5725 int viewPortTop = scrollBodyPanel.getAbsoluteTop();
5726 int viewPortBottom = viewPortTop
5727 + scrollBodyPanel.getOffsetHeight();
5728 return absoluteBottom > viewPortTop
5729 && absoluteTop < viewPortBottom;
5730 }
5731
5732
5733
5734
5735
5736
5737
5738
5739 public boolean isBefore(VScrollTableRow row1) {
5740 return getIndex() < row1.getIndex();
5741 }
5742
5743
5744
5745
5746
5747
5748
5749 private void setIndex(int indexInWholeTable) {
5750 index = indexInWholeTable;
5751 boolean isOdd = indexInWholeTable % 2 == 0;
5752
5753
5754
5755
5756
5757
5758 String primaryStyleName = getStylePrimaryName();
5759 if (primaryStyleName != null && !primaryStyleName.equals("")) {
5760 removeStyleName(getStylePrimaryName());
5761 }
5762 if (!isOdd) {
5763 addStyleName(VScrollTablePatched.this.getStylePrimaryName()
5764 + "-row-odd");
5765 } else {
5766 addStyleName(
5767 VScrollTablePatched.this.getStylePrimaryName() + "-row");
5768 }
5769 }
5770
5771 public int getIndex() {
5772 return index;
5773 }
5774
5775 @Override
5776 protected void onDetach() {
5777 super.onDetach();
5778 client.getContextMenu().ensureHidden(this);
5779 }
5780
5781 public String getKey() {
5782 return String.valueOf(rowKey);
5783 }
5784
5785 public void addCell(UIDL rowUidl, String text, char align,
5786 String style, boolean textIsHTML, boolean sorted) {
5787 addCell(rowUidl, text, align, style, textIsHTML, sorted, null);
5788 }
5789
5790 public void addCell(UIDL rowUidl, String text, char align,
5791 String style, boolean textIsHTML, boolean sorted,
5792 String description) {
5793
5794 final TableCellElement td = DOM.createTD().cast();
5795 initCellWithText(text, align, style, textIsHTML, sorted,
5796 description, td);
5797 }
5798
5799 protected void initCellWithText(String text, char align,
5800 String style, boolean textIsHTML, boolean sorted,
5801 String description, final TableCellElement td) {
5802 final Element container = DOM.createDiv();
5803 container.setClassName(VScrollTablePatched.this.getStylePrimaryName()
5804 + "-cell-wrapper");
5805
5806 td.setClassName(VScrollTablePatched.this.getStylePrimaryName()
5807 + "-cell-content");
5808
5809 if (style != null && !style.equals("")) {
5810 td.addClassName(VScrollTablePatched.this.getStylePrimaryName()
5811 + "-cell-content-" + style);
5812 }
5813
5814 if (sorted) {
5815 td.addClassName(VScrollTablePatched.this.getStylePrimaryName()
5816 + "-cell-content-sorted");
5817 }
5818
5819 if (textIsHTML) {
5820 container.setInnerHTML(text);
5821 } else {
5822 container.setInnerText(text);
5823 }
5824 setAlign(align, container);
5825 setTooltip(td, description);
5826
5827 td.appendChild(container);
5828 getElement().appendChild(td);
5829 }
5830
5831 protected void updateCellStyleNames(TableCellElement td,
5832 String primaryStyleName) {
5833 Element container = td.getFirstChild().cast();
5834 container.setClassName(primaryStyleName + "-cell-wrapper");
5835
5836
5837
5838
5839 String className = td.getClassName();
5840 String oldPrimaryName = className.split("-cell-content")[0];
5841 td.setClassName(
5842 className.replaceAll(oldPrimaryName, primaryStyleName));
5843 }
5844
5845 public void addCell(UIDL rowUidl, Widget w, char align,
5846 String style, boolean sorted, String description) {
5847 final TableCellElement td = DOM.createTD().cast();
5848 initCellWithWidget(w, align, style, sorted, td);
5849 setTooltip(td, description);
5850 }
5851
5852 private void setTooltip(TableCellElement td, String description) {
5853 if (description != null && !description.equals("")) {
5854 TooltipInfo info = new TooltipInfo(description, null, this);
5855 cellToolTips.put(td, info);
5856 } else {
5857 cellToolTips.remove(td);
5858 }
5859
5860 }
5861
5862 private void setAlign(char align, final Element container) {
5863 switch (align) {
5864 case ALIGN_CENTER:
5865 container.getStyle().setProperty("textAlign", "center");
5866 break;
5867 case ALIGN_LEFT:
5868 container.getStyle().setProperty("textAlign", "left");
5869 break;
5870 case ALIGN_RIGHT:
5871 default:
5872 container.getStyle().setProperty("textAlign", "right");
5873 break;
5874 }
5875 }
5876
5877 protected void initCellWithWidget(Widget w, char align,
5878 String style, boolean sorted, final TableCellElement td) {
5879 final Element container = DOM.createDiv();
5880 String className = VScrollTablePatched.this.getStylePrimaryName()
5881 + "-cell-content";
5882 if (style != null && !style.equals("")) {
5883 className += " " + VScrollTablePatched.this.getStylePrimaryName()
5884 + "-cell-content-" + style;
5885 }
5886 if (sorted) {
5887 className += " " + VScrollTablePatched.this.getStylePrimaryName()
5888 + "-cell-content-sorted";
5889 }
5890 td.setClassName(className);
5891 container.setClassName(VScrollTablePatched.this.getStylePrimaryName()
5892 + "-cell-wrapper");
5893 setAlign(align, container);
5894 td.appendChild(container);
5895 getElement().appendChild(td);
5896
5897
5898 w.removeFromParent();
5899 container.appendChild(w.getElement());
5900 adopt(w);
5901 childWidgets.add(w);
5902 }
5903
5904 @Override
5905 public Iterator<Widget> iterator() {
5906 return childWidgets.iterator();
5907 }
5908
5909 @Override
5910 public boolean remove(Widget w) {
5911 if (childWidgets.contains(w)) {
5912 orphan(w);
5913 DOM.removeChild(DOM.getParent(w.getElement()),
5914 w.getElement());
5915 childWidgets.remove(w);
5916 return true;
5917 } else {
5918 return false;
5919 }
5920 }
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932 private boolean handleClickEvent(Event event, Element targetTdOrTr,
5933 boolean immediate) {
5934 if (!client.hasEventListeners(VScrollTablePatched.this,
5935 TableConstants.ITEM_CLICK_EVENT_ID)) {
5936
5937 return false;
5938 }
5939
5940
5941 client.updateVariable(paintableId, "clickedKey", "" + rowKey,
5942 false);
5943
5944 if (getElement() == targetTdOrTr.getParentElement()) {
5945
5946 int childIndex = DOM.getChildIndex(getElement(),
5947 targetTdOrTr);
5948 String colKey = null;
5949 colKey = tHead.getHeaderCell(childIndex).getColKey();
5950 client.updateVariable(paintableId, "clickedColKey", colKey,
5951 false);
5952 }
5953
5954 MouseEventDetails details = MouseEventDetailsBuilder
5955 .buildMouseEventDetails(event);
5956
5957 client.updateVariable(paintableId, "clickEvent",
5958 details.toString(), immediate);
5959
5960 return true;
5961 }
5962
5963 public TooltipInfo getTooltip(
5964 com.google.gwt.dom.client.Element target) {
5965
5966 TooltipInfo info = null;
5967 final Element targetTdOrTr = getTdOrTr(target);
5968 if (targetTdOrTr != null && "td".equals(
5969 targetTdOrTr.getTagName().toLowerCase(Locale.ROOT))) {
5970 TableCellElement td = (TableCellElement) targetTdOrTr
5971 .cast();
5972 info = cellToolTips.get(td);
5973 }
5974
5975 if (info == null) {
5976 info = tooltipInfo;
5977 }
5978
5979 return info;
5980 }
5981
5982 private Element getTdOrTr(Element target) {
5983 Element thisTrElement = getElement();
5984 if (target == thisTrElement) {
5985
5986 return target;
5987 }
5988
5989
5990 Element element = target;
5991 while (element != null
5992 && element.getParentElement() != thisTrElement) {
5993 element = element.getParentElement();
5994 }
5995 return element;
5996 }
5997
5998
5999
6000
6001
6002
6003 private boolean handleTouchEvent(final Event event) {
6004
6005 boolean touchEventHandled = false;
6006
6007 if (enabled && HAS_NATIVE_TOUCH_SCROLLLING) {
6008 touchContextProvider.handleTouchEvent(event);
6009
6010 final Element targetTdOrTr = getEventTargetTdOrTr(event);
6011 final int type = event.getTypeInt();
6012
6013 switch (type) {
6014 case Event.ONTOUCHSTART:
6015 touchEventHandled = true;
6016 touchStart = event;
6017 isDragging = false;
6018 Touch touch = event.getChangedTouches().get(0);
6019
6020
6021 touchStartX = touch.getClientX();
6022 touchStartY = touch.getClientY();
6023
6024 if (dragmode != 0) {
6025 if (dragTouchTimeout == null) {
6026 dragTouchTimeout = new Timer() {
6027
6028 @Override
6029 public void run() {
6030 if (touchStart != null) {
6031
6032
6033 isDragging = true;
6034 }
6035 }
6036 };
6037 }
6038 dragTouchTimeout.schedule(TOUCHSCROLL_TIMEOUT);
6039 }
6040
6041 if (actionKeys != null) {
6042 if (contextTouchTimeout == null) {
6043 contextTouchTimeout = new Timer() {
6044
6045 @Override
6046 public void run() {
6047 if (touchStart != null) {
6048
6049
6050 showContextMenu(touchStart);
6051 event.preventDefault();
6052 touchStart = null;
6053 }
6054 }
6055 };
6056 }
6057 contextTouchTimeout
6058 .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
6059 event.stopPropagation();
6060 }
6061 break;
6062 case Event.ONTOUCHMOVE:
6063 touchEventHandled = true;
6064 if (isSignificantMove(event)) {
6065 if (contextTouchTimeout != null) {
6066
6067
6068
6069 contextTouchTimeout.cancel();
6070 contextTouchTimeout = null;
6071 }
6072 if (!isDragging && dragTouchTimeout != null) {
6073
6074
6075 dragTouchTimeout.cancel();
6076 dragTouchTimeout = null;
6077 }
6078
6079 if (dragmode != 0 && touchStart != null
6080 && isDragging) {
6081 event.preventDefault();
6082 event.stopPropagation();
6083 startRowDrag(touchStart, type, targetTdOrTr);
6084 }
6085 touchStart = null;
6086 }
6087 break;
6088 case Event.ONTOUCHEND:
6089 case Event.ONTOUCHCANCEL:
6090 touchEventHandled = true;
6091 if (contextTouchTimeout != null) {
6092 contextTouchTimeout.cancel();
6093 }
6094 if (dragTouchTimeout != null) {
6095 dragTouchTimeout.cancel();
6096 }
6097 if (touchStart != null) {
6098 if (!BrowserInfo.get().isAndroid()) {
6099 event.preventDefault();
6100 WidgetUtil.simulateClickFromTouchEvent(
6101 touchStart, this);
6102 }
6103 event.stopPropagation();
6104 touchStart = null;
6105 }
6106 isDragging = false;
6107 break;
6108 }
6109 }
6110 return touchEventHandled;
6111 }
6112
6113
6114
6115
6116
6117 @Override
6118 public void onBrowserEvent(final Event event) {
6119
6120 final boolean touchEventHandled = handleTouchEvent(event);
6121
6122 if (enabled && !touchEventHandled) {
6123 final int type = event.getTypeInt();
6124 final Element targetTdOrTr = getEventTargetTdOrTr(event);
6125 if (type == Event.ONCONTEXTMENU) {
6126 showContextMenu(event);
6127 if (enabled && (actionKeys != null
6128 || client.hasEventListeners(VScrollTablePatched.this,
6129 TableConstants.ITEM_CLICK_EVENT_ID))) {
6130
6131
6132
6133
6134
6135 event.stopPropagation();
6136 event.preventDefault();
6137 }
6138 return;
6139 }
6140
6141 boolean targetCellOrRowFound = targetTdOrTr != null;
6142
6143 switch (type) {
6144 case Event.ONDBLCLICK:
6145 if (targetCellOrRowFound) {
6146 handleClickEvent(event, targetTdOrTr, true);
6147 }
6148 break;
6149 case Event.ONMOUSEUP:
6150
6151
6152
6153
6154
6155
6156
6157 if (mouseUpPreviewMatched && lastMouseDownTarget != null
6158 && lastMouseDownTarget == getElementTdOrTr(
6159 WidgetUtil
6160 .getElementUnderMouse(event))) {
6161
6162
6163 if (targetCellOrRowFound) {
6164
6165
6166
6167
6168 boolean clickEventSent = handleClickEvent(event,
6169 targetTdOrTr, false);
6170
6171 if (event.getButton() == Event.BUTTON_LEFT
6172 && isSelectable()) {
6173
6174
6175 if ((event.getCtrlKey()
6176 || event.getMetaKey())
6177 && event.getShiftKey()
6178 && isMultiSelectModeDefault()) {
6179 toggleShiftSelection(false);
6180 setRowFocus(this);
6181
6182
6183 } else if ((event.getCtrlKey()
6184 || event.getMetaKey())
6185 && isMultiSelectModeDefault()) {
6186 boolean wasSelected = isSelected();
6187 toggleSelection();
6188 setRowFocus(this);
6189
6190
6191
6192
6193 selectionRangeStart = this;
6194 if (wasSelected) {
6195 removeRowFromUnsentSelectionRanges(
6196 this);
6197 }
6198
6199 } else if ((event.getCtrlKey()
6200 || event.getMetaKey())
6201 && isSingleSelectMode()) {
6202
6203
6204 if (!isSelected() || (isSelected()
6205 && nullSelectionAllowed)) {
6206
6207 if (!isSelected()) {
6208 deselectAll();
6209 }
6210
6211 toggleSelection();
6212 setRowFocus(this);
6213 }
6214
6215 } else if (event.getShiftKey()
6216 && isMultiSelectModeDefault()) {
6217
6218 toggleShiftSelection(true);
6219
6220 } else {
6221
6222 boolean currentlyJustThisRowSelected = selectedRowKeys
6223 .size() == 1
6224 && selectedRowKeys
6225 .contains(getKey());
6226
6227 if (!currentlyJustThisRowSelected) {
6228 if (isSingleSelectMode()
6229 || isMultiSelectModeDefault()) {
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241 deselectAll();
6242 }
6243 toggleSelection();
6244 } else if ((isSingleSelectMode()
6245 || isMultiSelectModeSimple())
6246 && nullSelectionAllowed) {
6247 toggleSelection();
6248 }
6249
6250
6251
6252
6253
6254 selectionRangeStart = this;
6255 setRowFocus(this);
6256 }
6257
6258
6259 if (BrowserInfo.get().isIE()) {
6260 ((Element) event.getEventTarget()
6261 .cast()).setPropertyJSO(
6262 "onselectstart", null);
6263 }
6264
6265 sendSelectedRows(false);
6266 }
6267
6268
6269
6270
6271
6272
6273 if (immediate || clickEventSent) {
6274 client.sendPendingVariableChanges();
6275 }
6276 }
6277 }
6278 mouseUpPreviewMatched = false;
6279 lastMouseDownTarget = null;
6280 break;
6281 case Event.ONTOUCHEND:
6282 case Event.ONTOUCHCANCEL:
6283 if (touchStart != null) {
6284
6285
6286
6287
6288 WidgetUtil.simulateClickFromTouchEvent(touchStart,
6289 this);
6290 touchStart = null;
6291 }
6292 touchContextProvider.cancel();
6293 break;
6294 case Event.ONTOUCHMOVE:
6295 if (isSignificantMove(event)) {
6296
6297
6298
6299
6300
6301 if (dragmode != 0 && touchStart != null
6302 && (TouchScrollDelegate
6303 .getActiveScrollDelegate() == null)) {
6304 startRowDrag(touchStart, type, targetTdOrTr);
6305 }
6306 touchContextProvider.cancel();
6307
6308
6309
6310
6311 touchStart = null;
6312 }
6313
6314 break;
6315 case Event.ONTOUCHSTART:
6316 touchStart = event;
6317 Touch touch = event.getChangedTouches().get(0);
6318
6319
6320 touchStartX = touch.getClientX();
6321 touchStartY = touch.getClientY();
6322
6323
6324
6325 touchStart.preventDefault();
6326 if (dragmode != 0 || actionKeys != null) {
6327 new Timer() {
6328
6329 @Override
6330 public void run() {
6331 TouchScrollDelegate activeScrollDelegate = TouchScrollDelegate
6332 .getActiveScrollDelegate();
6333
6334
6335
6336
6337
6338
6339
6340 if (activeScrollDelegate != null) {
6341 if (activeScrollDelegate.isMoved()) {
6342
6343
6344
6345
6346
6347
6348
6349 touchStart = null;
6350 } else {
6351
6352
6353
6354
6355
6356
6357 activeScrollDelegate
6358 .stopScrolling();
6359 }
6360 }
6361 }
6362 }.schedule(TOUCHSCROLL_TIMEOUT);
6363
6364 if (contextTouchTimeout == null
6365 && actionKeys != null) {
6366 contextTouchTimeout = new Timer() {
6367
6368 @Override
6369 public void run() {
6370 if (touchStart != null) {
6371 showContextMenu(touchStart);
6372 touchStart = null;
6373 }
6374 }
6375 };
6376 }
6377 if (contextTouchTimeout != null) {
6378 contextTouchTimeout.cancel();
6379 contextTouchTimeout
6380 .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
6381 }
6382 }
6383 break;
6384 case Event.ONMOUSEDOWN:
6385
6386
6387
6388
6389
6390 lastMouseDownTarget = getElementTdOrTr(
6391 WidgetUtil.getElementUnderMouse(event));
6392 mouseUpPreviewMatched = false;
6393 mouseUpEventPreviewRegistration = Event
6394 .addNativePreviewHandler(mouseUpPreviewHandler);
6395
6396 if (targetCellOrRowFound) {
6397 setRowFocus(this);
6398 ensureFocus();
6399 if (dragmode != 0 && (event
6400 .getButton() == NativeEvent.BUTTON_LEFT)) {
6401 startRowDrag(event, type, targetTdOrTr);
6402
6403 } else if (event.getCtrlKey() || event.getShiftKey()
6404 || event.getMetaKey()
6405 && isMultiSelectModeDefault()) {
6406
6407
6408 event.preventDefault();
6409
6410
6411 if (BrowserInfo.get().isIE()) {
6412 ((Element) event.getEventTarget().cast())
6413 .setPropertyJSO("onselectstart",
6414 getPreventTextSelectionIEHack());
6415 }
6416
6417 event.stopPropagation();
6418 }
6419 }
6420 break;
6421 case Event.ONMOUSEOUT:
6422 break;
6423 default:
6424 break;
6425 }
6426 }
6427 super.onBrowserEvent(event);
6428 }
6429
6430 private boolean isSignificantMove(Event event) {
6431 if (touchStart == null) {
6432
6433 return false;
6434 }
6435
6436
6437
6438
6439 Touch touch = event.getChangedTouches().get(0);
6440 if (Math.abs(touch.getClientX()
6441 - touchStartX) > TouchScrollDelegate.SIGNIFICANT_MOVE_THRESHOLD) {
6442 return true;
6443 }
6444 if (Math.abs(touch.getClientY()
6445 - touchStartY) > TouchScrollDelegate.SIGNIFICANT_MOVE_THRESHOLD) {
6446 return true;
6447 }
6448 return false;
6449 }
6450
6451
6452
6453
6454
6455
6456
6457 private boolean rowKeyIsSelected(int rowKey) {
6458
6459 if (selectedRowKeys.contains("" + rowKey)) {
6460 return true;
6461 }
6462
6463
6464 for (SelectionRange r : selectedRowRanges) {
6465 if (r.inRange(getRenderedRowByKey("" + rowKey))) {
6466 return true;
6467 }
6468 }
6469 return false;
6470 }
6471
6472 protected void startRowDrag(Event event, final int type,
6473 Element targetTdOrTr) {
6474 VTransferable transferable = new VTransferable();
6475 transferable.setDragSource(ConnectorMap.get(client)
6476 .getConnector(VScrollTablePatched.this));
6477 transferable.setData("itemId", "" + rowKey);
6478 NodeList<TableCellElement> cells = rowElement.getCells();
6479 for (int i = 0; i < cells.getLength(); i++) {
6480 if (cells.getItem(i).isOrHasChild(targetTdOrTr)) {
6481 HeaderCell headerCell = tHead.getHeaderCell(i);
6482 transferable.setData("propertyId", headerCell.cid);
6483 break;
6484 }
6485 }
6486
6487 VDragEvent ev = VDragAndDropManager.get()
6488 .startDrag(transferable, event, true);
6489 if (dragmode == DRAGMODE_MULTIROW && isMultiSelectModeAny()
6490 && rowKeyIsSelected(rowKey)) {
6491
6492
6493 ev.createDragImage(scrollBody.tBodyElement, true);
6494
6495
6496 Element dragImage = ev.getDragImage();
6497 int i = 0;
6498 for (Widget next : scrollBody) {
6499 Element child = (Element) dragImage.getChild(i++);
6500
6501 if (!rowKeyIsSelected(
6502 ((VScrollTableRow) next).rowKey)) {
6503 child.getStyle().setVisibility(Visibility.HIDDEN);
6504 }
6505 }
6506 } else {
6507 ev.createDragImage(getElement(), true);
6508 }
6509 if (type == Event.ONMOUSEDOWN) {
6510 event.preventDefault();
6511 }
6512 event.stopPropagation();
6513 }
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525 private Element getEventTargetTdOrTr(Event event) {
6526 final Element eventTarget = event.getEventTarget().cast();
6527 return getElementTdOrTr(eventTarget);
6528 }
6529
6530 private Element getElementTdOrTr(Element element) {
6531
6532 Widget widget = WidgetUtil.findWidget(element, null);
6533
6534 if (widget != this) {
6535
6536
6537
6538
6539
6540
6541 while (widget != null && widget.getParent() != this) {
6542 widget = widget.getParent();
6543 }
6544
6545 if (!(widget instanceof VLabel)
6546 && !(widget instanceof VEmbedded)
6547 && !(widget instanceof VTextField
6548 && ((VTextField) widget).isReadOnly())) {
6549 return null;
6550 }
6551 }
6552 return getTdOrTr(element);
6553 }
6554
6555 @Override
6556 public void showContextMenu(Event event) {
6557 if (enabled && actionKeys != null) {
6558
6559 int left = WidgetUtil.getTouchOrMouseClientX(event)
6560 + Window.getScrollLeft();
6561 int top = WidgetUtil.getTouchOrMouseClientY(event)
6562 + Window.getScrollTop();
6563 showContextMenu(left, top);
6564 }
6565 }
6566
6567 public void showContextMenu(int left, int top) {
6568 VContextMenu menu = client.getContextMenu();
6569 contextMenu = new ContextMenuDetails(menu, getKey(), left, top);
6570 menu.showAt(this, left, top);
6571 }
6572
6573
6574
6575
6576
6577
6578 public boolean isSelected() {
6579 return selected;
6580 }
6581
6582
6583
6584
6585 public void toggleSelection() {
6586 selected = !selected;
6587 selectionChanged = true;
6588 if (selected) {
6589 selectedRowKeys.add(String.valueOf(rowKey));
6590 addStyleName("v-selected");
6591 } else {
6592 removeStyleName("v-selected");
6593 selectedRowKeys.remove(String.valueOf(rowKey));
6594 }
6595 }
6596
6597
6598
6599
6600
6601
6602
6603
6604 private void toggleShiftSelection(boolean deselectPrevious) {
6605
6606
6607
6608
6609
6610 if (isSingleSelectMode()) {
6611
6612 deselectAll();
6613 toggleSelection();
6614 return;
6615 }
6616
6617
6618 VScrollTableRow endRow = this;
6619 VScrollTableRow startRow = selectionRangeStart;
6620 if (startRow == null) {
6621 startRow = focusedRow;
6622 selectionRangeStart = focusedRow;
6623
6624
6625 if (startRow == null) {
6626 startRow = (VScrollTableRow) scrollBody.iterator()
6627 .next();
6628 setRowFocus(endRow);
6629 }
6630 } else if (!startRow.isSelected()) {
6631
6632
6633 startRow = (VScrollTableRow) scrollBody.iterator().next();
6634 setRowFocus(endRow);
6635 }
6636
6637
6638 if (deselectPrevious) {
6639 deselectAll();
6640 }
6641
6642
6643
6644 if (!startRow.isBefore(endRow)) {
6645 VScrollTableRow tmp = startRow;
6646 startRow = endRow;
6647 endRow = tmp;
6648 }
6649 SelectionRange range = new SelectionRange(startRow, endRow);
6650
6651 for (Widget w : scrollBody) {
6652 VScrollTableRow row = (VScrollTableRow) w;
6653 if (range.inRange(row)) {
6654 if (!row.isSelected()) {
6655 row.toggleSelection();
6656 }
6657 selectedRowKeys.add(row.getKey());
6658 }
6659 }
6660
6661
6662 if (startRow != endRow) {
6663 selectedRowRanges.add(range);
6664 }
6665 }
6666
6667
6668
6669
6670
6671
6672
6673 @Override
6674 public Action[] getActions() {
6675 if (actionKeys == null) {
6676 return new Action[] {};
6677 }
6678 final Action[] actions = new Action[actionKeys.length];
6679 for (int i = 0; i < actions.length; i++) {
6680 final String actionKey = actionKeys[i];
6681 final TreeAction a = new TreeAction(this,
6682 String.valueOf(rowKey), actionKey) {
6683
6684 @Override
6685 public void execute() {
6686 super.execute();
6687 lazyRevertFocusToRow(VScrollTableRow.this);
6688 }
6689 };
6690 a.setCaption(getActionCaption(actionKey));
6691 a.setIconUrl(getActionIcon(actionKey));
6692 actions[i] = a;
6693 }
6694 return actions;
6695 }
6696
6697 @Override
6698 public ApplicationConnection getClient() {
6699 return client;
6700 }
6701
6702 @Override
6703 public String getPaintableId() {
6704 return paintableId;
6705 }
6706
6707 public Widget getWidgetForPaintable() {
6708 return this;
6709 }
6710 }
6711
6712 protected class VScrollTableGeneratedRow extends VScrollTableRow {
6713
6714 private boolean spanColumns;
6715 private boolean htmlContentAllowed;
6716
6717 public VScrollTableGeneratedRow(UIDL uidl, char[] aligns) {
6718 super(uidl, aligns);
6719 addStyleName("v-table-generated-row");
6720 }
6721
6722 public boolean isSpanColumns() {
6723 return spanColumns;
6724 }
6725
6726 @Override
6727 protected void initCellWidths() {
6728 if (spanColumns) {
6729 setSpannedColumnWidthAfterDOMFullyInited();
6730 } else {
6731 super.initCellWidths();
6732 }
6733 }
6734
6735 private void setSpannedColumnWidthAfterDOMFullyInited() {
6736
6737
6738
6739 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
6740
6741 @Override
6742 public void execute() {
6743 if (showRowHeaders) {
6744 setCellWidth(0, tHead.getHeaderCell(0)
6745 .getWidthWithIndent());
6746 calcAndSetSpanWidthOnCell(1);
6747 } else {
6748 calcAndSetSpanWidthOnCell(0);
6749 }
6750 }
6751 });
6752 }
6753
6754 @Override
6755 protected boolean isRenderHtmlInCells() {
6756 return htmlContentAllowed;
6757 }
6758
6759 @Override
6760 protected void addCellsFromUIDL(UIDL uidl, char[] aligns, int col,
6761 int visibleColumnIndex) {
6762 htmlContentAllowed = uidl.getBooleanAttribute("gen_html");
6763 spanColumns = uidl.getBooleanAttribute("gen_span");
6764
6765 if (spanColumns) {
6766 int colCount = uidl.getChildCount();
6767
6768 for (final Object cell : uidl) {
6769 if (cell instanceof String) {
6770 addSpannedCell(uidl, cell.toString(), aligns[0], "",
6771 htmlContentAllowed, false, null, colCount);
6772 } else {
6773 addSpannedCell(uidl, (Widget) cell, aligns[0], "",
6774 false, colCount);
6775 }
6776 break;
6777 }
6778 } else {
6779 super.addCellsFromUIDL(uidl, aligns, col,
6780 visibleColumnIndex);
6781 }
6782 }
6783
6784 private void addSpannedCell(UIDL rowUidl, Widget w, char align,
6785 String style, boolean sorted, int colCount) {
6786 TableCellElement td = DOM.createTD().cast();
6787 td.setColSpan(colCount);
6788 initCellWithWidget(w, align, style, sorted, td);
6789 }
6790
6791 private void addSpannedCell(UIDL rowUidl, String text, char align,
6792 String style, boolean textIsHTML, boolean sorted,
6793 String description, int colCount) {
6794
6795 final TableCellElement td = DOM.createTD().cast();
6796 td.setColSpan(colCount);
6797 initCellWithText(text, align, style, textIsHTML, sorted,
6798 description, td);
6799 }
6800
6801 @Override
6802 protected void setCellWidth(int cellIx, int width) {
6803 if (isSpanColumns()) {
6804 if (showRowHeaders) {
6805 if (cellIx == 0) {
6806 super.setCellWidth(0, width);
6807 } else {
6808
6809
6810 calcAndSetSpanWidthOnCell(1);
6811 }
6812 } else {
6813
6814 calcAndSetSpanWidthOnCell(0);
6815 }
6816 } else {
6817 super.setCellWidth(cellIx, width);
6818 }
6819 }
6820
6821 private void calcAndSetSpanWidthOnCell(final int cellIx) {
6822 int spanWidth = 0;
6823 for (int ix = (showRowHeaders ? 1 : 0); ix < tHead
6824 .getVisibleCellCount(); ix++) {
6825 spanWidth += tHead.getHeaderCell(ix).getOffsetWidth();
6826 }
6827 WidgetUtil.setWidthExcludingPaddingAndBorder(
6828 (Element) getElement().getChild(cellIx), spanWidth, 13,
6829 false);
6830 }
6831 }
6832
6833
6834
6835
6836
6837
6838
6839
6840
6841 protected void ensureFocus() {
6842 if (!hasFocus()) {
6843 scrollBodyPanel.setFocus(true);
6844 }
6845
6846 }
6847
6848 }
6849
6850
6851
6852
6853 public void deselectAll() {
6854 for (Widget w : scrollBody) {
6855 VScrollTableRow row = (VScrollTableRow) w;
6856 if (row.isSelected()) {
6857 row.toggleSelection();
6858 }
6859 }
6860
6861 selectedRowKeys.clear();
6862 selectedRowRanges.clear();
6863
6864
6865 instructServerToForgetPreviousSelections();
6866 }
6867
6868
6869
6870
6871
6872 private void instructServerToForgetPreviousSelections() {
6873 client.updateVariable(paintableId, "clearSelections", true, false);
6874 }
6875
6876
6877
6878
6879 public void updatePageLength() {
6880
6881 if (!isVisibleInHierarchy() || !enabled) {
6882 return;
6883 }
6884
6885 if (scrollBody == null) {
6886 return;
6887 }
6888
6889 if (isDynamicHeight()) {
6890 return;
6891 }
6892
6893 int rowHeight = (int) Math.round(scrollBody.getRowHeight());
6894 int bodyH = scrollBodyPanel.getOffsetHeight();
6895 int rowsAtOnce = bodyH / rowHeight;
6896 boolean anotherPartlyVisible = ((bodyH % rowHeight) != 0);
6897 if (anotherPartlyVisible) {
6898 rowsAtOnce++;
6899 }
6900 if (pageLength != rowsAtOnce) {
6901 pageLength = rowsAtOnce;
6902 client.updateVariable(paintableId, "pagelength", pageLength, false);
6903
6904 if (!rendering) {
6905 int currentlyVisible = scrollBody.getLastRendered()
6906 - scrollBody.getFirstRendered();
6907 if (currentlyVisible < pageLength
6908 && currentlyVisible < totalRows) {
6909
6910 scrollBodyPanel.setScrollPosition(scrollTop + 1);
6911 scrollBodyPanel.setScrollPosition(scrollTop - 1);
6912 }
6913
6914 sizeNeedsInit = true;
6915 }
6916 }
6917
6918 }
6919
6920
6921 public void updateWidth() {
6922 if (!isVisibleInHierarchy()) {
6923
6924
6925
6926
6927
6928 return;
6929 }
6930
6931 if (!isDynamicWidth()) {
6932 int innerPixels = getOffsetWidth() - getBorderWidth();
6933 if (innerPixels < 0) {
6934 innerPixels = 0;
6935 }
6936 setContentWidth(innerPixels);
6937
6938
6939 triggerLazyColumnAdjustment(false);
6940
6941 } else {
6942
6943 sizeNeedsInit = true;
6944
6945
6946 triggerLazyColumnAdjustment(false);
6947 }
6948
6949
6950
6951
6952
6953 setProperTabIndex();
6954 }
6955
6956 private static final int LAZY_COLUMN_ADJUST_TIMEOUT = 300;
6957
6958 private final Timer lazyAdjustColumnWidths = new Timer() {
6959
6960 private int formerWidth;
6961 private boolean formerScrolling;
6962
6963
6964
6965
6966
6967
6968
6969 @Override
6970 public void run() {
6971 if (scrollBody == null || !isVisibleInHierarchy()) {
6972
6973
6974 triggerLazyColumnAdjustment(false);
6975 return;
6976 }
6977
6978
6979 int width = scrollBody.getAvailableWidth();
6980 boolean scrolling = willHaveScrollbars();
6981 if (width != formerWidth || scrolling != formerScrolling) {
6982 formerWidth = width;
6983 formerScrolling = scrolling;
6984 } else {
6985 return;
6986 }
6987
6988 int usedMinimumWidth = 0;
6989 int totalExplicitColumnsWidths = 0;
6990 float expandRatioDivider = 0;
6991 int colIndex = 0;
6992
6993 int hierarchyColumnIndent = scrollBody.getMaxIndent();
6994 int hierarchyColumnIndex = getHierarchyColumnIndex();
6995 HeaderCell hierarchyHeaderInNeedOfFurtherHandling = null;
6996
6997 for (Widget widget : tHead) {
6998 final HeaderCell hCell = (HeaderCell) widget;
6999 boolean hasIndent = hierarchyColumnIndent > 0
7000 && hCell.isHierarchyColumn();
7001 if (hCell.isDefinedWidth()) {
7002
7003
7004 int w = hCell.getWidth();
7005 if (hasIndent && w < hierarchyColumnIndent) {
7006
7007 w = hierarchyColumnIndent;
7008 hierarchyHeaderInNeedOfFurtherHandling = hCell;
7009 }
7010 totalExplicitColumnsWidths += w;
7011 usedMinimumWidth += w;
7012 } else {
7013
7014 int naturalColumnWidth = hCell
7015 .getNaturalColumnWidth(colIndex);
7016
7017
7018
7019
7020
7021 if (hCell.getExpandRatio() > 0) {
7022 naturalColumnWidth = 0;
7023 }
7024 usedMinimumWidth += naturalColumnWidth;
7025 expandRatioDivider += hCell.getExpandRatio();
7026 if (hasIndent) {
7027 hierarchyHeaderInNeedOfFurtherHandling = hCell;
7028 }
7029 }
7030 colIndex++;
7031 }
7032
7033 int availW = scrollBody.getAvailableWidth();
7034
7035 availW = scrollBody.getAvailableWidth();
7036 int visibleCellCount = tHead.getVisibleCellCount();
7037 int totalExtraWidth = scrollBody.getCellExtraWidth()
7038 * visibleCellCount;
7039 if (willHaveScrollbars()) {
7040 totalExtraWidth += WidgetUtil.getNativeScrollbarSize();
7041
7042 scrollBodyPanel.getElement().getStyle().clearOverflowY();
7043 } else {
7044
7045
7046
7047 scrollBodyPanel.getElement().getStyle()
7048 .setOverflowY(Overflow.HIDDEN);
7049 }
7050
7051 availW -= totalExtraWidth;
7052 int forceScrollBodyWidth = -1;
7053
7054 int extraSpace = availW - usedMinimumWidth;
7055 if (extraSpace < 0) {
7056 if (getTotalRows() == 0) {
7057
7058
7059
7060
7061
7062
7063
7064
7065 forceScrollBodyWidth = usedMinimumWidth + totalExtraWidth;
7066 }
7067 extraSpace = 0;
7068
7069 scrollBodyPanel.getElement().getStyle().clearOverflowX();
7070 } else {
7071
7072
7073
7074
7075 scrollBodyPanel.getElement().getStyle()
7076 .setOverflowX(Overflow.HIDDEN);
7077 }
7078
7079 if (forceScrollBodyWidth > 0) {
7080 scrollBody.container.getStyle().setWidth(forceScrollBodyWidth,
7081 Unit.PX);
7082 } else {
7083
7084
7085 scrollBody.container.getStyle().clearWidth();
7086 }
7087
7088 int totalUndefinedNaturalWidths = usedMinimumWidth
7089 - totalExplicitColumnsWidths;
7090
7091 if (hierarchyHeaderInNeedOfFurtherHandling != null
7092 && !hierarchyHeaderInNeedOfFurtherHandling
7093 .isDefinedWidth()) {
7094
7095 int w = hierarchyHeaderInNeedOfFurtherHandling
7096 .getNaturalColumnWidth(hierarchyColumnIndex);
7097 int newSpace = Math.round(w + (float) extraSpace * (float) w
7098 / totalUndefinedNaturalWidths);
7099 if (newSpace >= hierarchyColumnIndent) {
7100
7101 hierarchyHeaderInNeedOfFurtherHandling = null;
7102 } else {
7103
7104 totalExplicitColumnsWidths += hierarchyColumnIndent;
7105 usedMinimumWidth -= w - hierarchyColumnIndent;
7106 totalUndefinedNaturalWidths = usedMinimumWidth
7107 - totalExplicitColumnsWidths;
7108 expandRatioDivider += hierarchyHeaderInNeedOfFurtherHandling
7109 .getExpandRatio();
7110 extraSpace = Math.max(availW - usedMinimumWidth, 0);
7111 }
7112 }
7113
7114
7115 colIndex = 0;
7116 int checksum = 0;
7117 for (Widget widget : tHead) {
7118 HeaderCell hCell = (HeaderCell) widget;
7119 if (hCell.isResizing) {
7120 continue;
7121 }
7122 if (!hCell.isDefinedWidth()) {
7123 int w = hCell.getNaturalColumnWidth(colIndex);
7124 int newSpace;
7125 if (expandRatioDivider > 0) {
7126
7127 if (hCell.getExpandRatio() > 0) {
7128 w = 0;
7129 }
7130 newSpace = Math.round((w + extraSpace
7131 * hCell.getExpandRatio() / expandRatioDivider));
7132 } else {
7133 if (hierarchyHeaderInNeedOfFurtherHandling == hCell) {
7134
7135 newSpace = hierarchyColumnIndent;
7136 } else if (totalUndefinedNaturalWidths != 0) {
7137
7138 newSpace = Math.round(w + (float) extraSpace
7139 * (float) w / totalUndefinedNaturalWidths);
7140 } else {
7141 newSpace = w;
7142 }
7143 }
7144 checksum += newSpace;
7145 setColWidth(colIndex, newSpace, false);
7146
7147 } else {
7148 if (hierarchyHeaderInNeedOfFurtherHandling == hCell) {
7149
7150 checksum += hierarchyColumnIndent;
7151 setColWidth(colIndex, hierarchyColumnIndent, false);
7152 } else {
7153 int cellWidth = hCell.getWidthWithIndent();
7154 checksum += cellWidth;
7155 if (hCell.isHierarchyColumn()) {
7156
7157
7158 setColWidth(colIndex, cellWidth, true);
7159 }
7160 }
7161 }
7162 colIndex++;
7163 }
7164
7165 if (extraSpace > 0 && checksum != availW) {
7166
7167
7168
7169
7170
7171 colIndex = 0;
7172 for (Widget widget : tHead) {
7173 HeaderCell hc = (HeaderCell) widget;
7174 if (!hc.isResizing && !hc.isDefinedWidth()) {
7175 setColWidth(colIndex,
7176 hc.getWidthWithIndent() + availW - checksum,
7177 false);
7178 break;
7179 }
7180 colIndex++;
7181 }
7182 }
7183
7184 if (isDynamicHeight() && totalRows == pageLength) {
7185
7186
7187 int bodyHeight = scrollBody.getRequiredHeight();
7188 boolean needsSpaceForHorizontalScrollbar = (availW < usedMinimumWidth);
7189 if (needsSpaceForHorizontalScrollbar) {
7190 bodyHeight += WidgetUtil.getNativeScrollbarSize();
7191 }
7192 int heightBefore = getOffsetHeight();
7193 scrollBodyPanel.setHeight(bodyHeight + "px");
7194
7195 if (heightBefore != getOffsetHeight()) {
7196 Util.notifyParentOfSizeChange(VScrollTablePatched.this, false);
7197 }
7198 }
7199
7200 forceRealignColumnHeaders();
7201 }
7202
7203 };
7204
7205 private void forceRealignColumnHeaders() {
7206 if (BrowserInfo.get().isIE()) {
7207
7208
7209
7210
7211
7212
7213 onScroll(null);
7214 }
7215 }
7216
7217
7218
7219
7220
7221
7222 private void setContentWidth(int pixels) {
7223 tHead.setWidth(pixels + "px");
7224 scrollBodyPanel.setWidth(pixels + "px");
7225 tFoot.setWidth(pixels + "px");
7226 }
7227
7228 private int borderWidth = -1;
7229
7230
7231
7232
7233 private int getBorderWidth() {
7234 if (borderWidth < 0) {
7235 borderWidth = WidgetUtil.measureHorizontalPaddingAndBorder(
7236 scrollBodyPanel.getElement(), 2);
7237 if (borderWidth < 0) {
7238 borderWidth = 0;
7239 }
7240 }
7241 return borderWidth;
7242 }
7243
7244
7245
7246
7247
7248 private int containerHeight;
7249
7250 private void setContainerHeight() {
7251 if (!isDynamicHeight()) {
7252
7253
7254
7255
7256
7257
7258
7259 if (BrowserInfo.get().isAndroid23()) {
7260 getElement().getStyle().setDisplay(Display.BLOCK);
7261 }
7262
7263 containerHeight = getOffsetHeight();
7264 containerHeight -= showColHeaders ? tHead.getOffsetHeight() : 0;
7265 containerHeight -= tFoot.getOffsetHeight();
7266 containerHeight -= getContentAreaBorderHeight();
7267 if (containerHeight < 0) {
7268 containerHeight = 0;
7269 }
7270
7271 scrollBodyPanel.setHeight(containerHeight + "px");
7272
7273 if (BrowserInfo.get().isAndroid23()) {
7274 getElement().getStyle().clearDisplay();
7275 }
7276 }
7277 }
7278
7279 private int contentAreaBorderHeight = -1;
7280 private int scrollLeft;
7281 private int scrollTop;
7282
7283
7284 public VScrollTableDropHandler dropHandler;
7285
7286 private boolean navKeyDown;
7287
7288
7289 public boolean multiselectPending;
7290
7291
7292 public CollapseMenuContent collapsibleMenuContent;
7293
7294
7295
7296
7297 private int getContentAreaBorderHeight() {
7298 if (contentAreaBorderHeight < 0) {
7299
7300 scrollBodyPanel.getElement().getStyle()
7301 .setOverflow(Overflow.HIDDEN);
7302 int oh = scrollBodyPanel.getOffsetHeight();
7303 int ch = scrollBodyPanel.getElement()
7304 .getPropertyInt("clientHeight");
7305 contentAreaBorderHeight = oh - ch;
7306 scrollBodyPanel.getElement().getStyle().setOverflow(Overflow.AUTO);
7307 }
7308 return contentAreaBorderHeight;
7309 }
7310
7311 @Override
7312 public void setHeight(String height) {
7313 if (height.isEmpty()
7314 && !getElement().getStyle().getHeight().isEmpty()) {
7315
7316
7317
7318
7319 sizeNeedsInit = true;
7320 }
7321 super.setHeight(height);
7322 }
7323
7324
7325 public void updateHeight() {
7326 setContainerHeight();
7327
7328 if (initializedAndAttached) {
7329 updatePageLength();
7330 }
7331
7332 triggerLazyColumnAdjustment(false);
7333
7334
7335
7336
7337
7338 setProperTabIndex();
7339
7340 }
7341
7342
7343
7344
7345
7346
7347
7348 @Override
7349 public void setVisible(boolean visible) {
7350 if (isVisible() != visible) {
7351 super.setVisible(visible);
7352 if (initializedAndAttached) {
7353 if (visible) {
7354 Scheduler.get().scheduleDeferred(new Command() {
7355
7356 @Override
7357 public void execute() {
7358 scrollBodyPanel.setScrollPosition(
7359 measureRowHeightOffset(firstRowInViewPort));
7360 }
7361 });
7362 }
7363 }
7364 }
7365 }
7366
7367
7368
7369
7370
7371
7372
7373
7374 protected String buildCaptionHtmlSnippet(UIDL uidl) {
7375 String s = uidl.hasAttribute("caption")
7376 ? uidl.getStringAttribute("caption")
7377 : "";
7378 if (uidl.hasAttribute("icon")) {
7379 Icon icon = client.getIcon(uidl.getStringAttribute("icon"));
7380 icon.setAlternateText("icon");
7381 s = icon.getElement().getString() + s;
7382 }
7383 return s;
7384 }
7385
7386
7387
7388 private void updateFirstVisibleRow() {
7389 scrollTop = scrollBodyPanel.getScrollPosition();
7390 firstRowInViewPort = calcFirstRowInViewPort();
7391 int maxFirstRow = totalRows - pageLength;
7392 if (firstRowInViewPort > maxFirstRow && maxFirstRow >= 0) {
7393 firstRowInViewPort = maxFirstRow;
7394 }
7395 lastRequestedFirstvisible = firstRowInViewPort;
7396 client.updateVariable(paintableId, "firstvisible", firstRowInViewPort,
7397 false);
7398 }
7399
7400
7401
7402
7403
7404
7405 @Override
7406 public void onScroll(ScrollEvent event) {
7407
7408
7409 if (isLazyScrollerActive()) {
7410 return;
7411 }
7412
7413 scrollLeft = scrollBodyPanel.getElement().getScrollLeft();
7414 scrollTop = scrollBodyPanel.getScrollPosition();
7415
7416
7417
7418
7419
7420
7421
7422
7423 if (!initializedAndAttached || !isAttached()) {
7424 return;
7425 }
7426 if (!enabled) {
7427 scrollBodyPanel.setScrollPosition(
7428 measureRowHeightOffset(firstRowInViewPort));
7429 return;
7430 }
7431
7432 rowRequestHandler.cancel();
7433
7434 if (BrowserInfo.get().isSafariOrIOS() && event != null
7435 && scrollTop == 0) {
7436
7437
7438
7439 Scheduler.get().scheduleDeferred(new Command() {
7440
7441 @Override
7442 public void execute() {
7443 onScroll(null);
7444 }
7445 });
7446 return;
7447 }
7448
7449
7450 tHead.setHorizontalScrollPosition(scrollLeft);
7451
7452
7453 tFoot.setHorizontalScrollPosition(scrollLeft);
7454
7455 if (totalRows == 0) {
7456
7457 return;
7458 }
7459
7460 firstRowInViewPort = calcFirstRowInViewPort();
7461 int maxFirstRow = totalRows - pageLength;
7462 if (firstRowInViewPort > maxFirstRow && maxFirstRow >= 0) {
7463 firstRowInViewPort = maxFirstRow;
7464 }
7465
7466 int postLimit = (int) (firstRowInViewPort + (pageLength - 1)
7467 + pageLength * cacheReactRate);
7468 if (postLimit > totalRows - 1) {
7469 postLimit = totalRows - 1;
7470 }
7471 int preLimit = (int) (firstRowInViewPort
7472 - pageLength * cacheReactRate);
7473 if (preLimit < 0) {
7474 preLimit = 0;
7475 }
7476 final int lastRendered = scrollBody.getLastRendered();
7477 final int firstRendered = scrollBody.getFirstRendered();
7478
7479 if (postLimit <= lastRendered && preLimit >= firstRendered) {
7480
7481
7482
7483 lastRequestedFirstvisible = firstRowInViewPort;
7484 client.updateVariable(paintableId, "firstvisible",
7485 firstRowInViewPort, false);
7486 return;
7487 }
7488
7489 if (allRenderedRowsAreNew()) {
7490
7491 rowRequestHandler.setReqFirstRow(
7492 (firstRowInViewPort - (int) (pageLength * cacheRate)));
7493 int last = firstRowInViewPort + (int) (cacheRate * pageLength)
7494 + pageLength - 1;
7495 if (last >= totalRows) {
7496 last = totalRows - 1;
7497 }
7498 rowRequestHandler
7499 .setReqRows(last - rowRequestHandler.getReqFirstRow() + 1);
7500 updatedReqRows = false;
7501 rowRequestHandler.deferRowFetch();
7502 return;
7503 }
7504 if (preLimit < firstRendered) {
7505
7506
7507 rowRequestHandler.setReqFirstRow(
7508 (int) (firstRowInViewPort - pageLength * cacheRate));
7509 rowRequestHandler.setReqRows(
7510 firstRendered - rowRequestHandler.getReqFirstRow());
7511 rowRequestHandler.deferRowFetch();
7512
7513 return;
7514 }
7515 if (postLimit > lastRendered) {
7516
7517 int reqRows = (int) ((firstRowInViewPort + pageLength
7518 + pageLength * cacheRate) - lastRendered);
7519 rowRequestHandler.triggerRowFetch(lastRendered + 1, reqRows);
7520 }
7521 }
7522
7523 private boolean allRenderedRowsAreNew() {
7524 int firstRowInViewPort = calcFirstRowInViewPort();
7525 int firstRendered = scrollBody.getFirstRendered();
7526 int lastRendered = scrollBody.getLastRendered();
7527 return (firstRowInViewPort - pageLength * cacheRate > lastRendered
7528 || firstRowInViewPort + pageLength
7529 + pageLength * cacheRate < firstRendered);
7530 }
7531
7532 protected int calcFirstRowInViewPort() {
7533 return (int) Math.ceil(scrollTop / scrollBody.getRowHeight());
7534 }
7535
7536 @Override
7537 public VScrollTableDropHandler getDropHandler() {
7538 return dropHandler;
7539 }
7540
7541 private static class TableDDDetails {
7542 int overkey = -1;
7543 VerticalDropLocation dropLocation;
7544 String colkey;
7545
7546 @Override
7547 public boolean equals(Object obj) {
7548 if (obj instanceof TableDDDetails) {
7549 TableDDDetails other = (TableDDDetails) obj;
7550 return dropLocation == other.dropLocation
7551 && overkey == other.overkey
7552 && ((colkey != null && colkey.equals(other.colkey))
7553 || (colkey == null && other.colkey == null));
7554 }
7555 return false;
7556 }
7557
7558 @Override
7559 public int hashCode() {
7560 final int prime = 31;
7561 int result = 1;
7562 result = prime * result
7563 + ((dropLocation == null) ? 0 : dropLocation.hashCode());
7564 result = prime * result + overkey;
7565 result = prime * result
7566 + ((colkey == null) ? 0 : colkey.hashCode());
7567 return result;
7568 }
7569 }
7570
7571 public class VScrollTableDropHandler extends VAbstractDropHandler {
7572
7573 private static final String ROWSTYLEBASE = "v-table-row-drag-";
7574 private TableDDDetails dropDetails;
7575 private TableDDDetails lastEmphasized;
7576
7577 @Override
7578 public void dragEnter(VDragEvent drag) {
7579 updateDropDetails(drag);
7580 super.dragEnter(drag);
7581 }
7582
7583 private void updateDropDetails(VDragEvent drag) {
7584 dropDetails = new TableDDDetails();
7585 Element elementOver = drag.getElementOver();
7586
7587 Class<? extends Widget> clazz = getRowClass();
7588 VScrollTableRow row = null;
7589 if (clazz != null) {
7590 row = WidgetUtil.findWidget(elementOver, clazz);
7591 }
7592 if (row != null) {
7593 dropDetails.overkey = row.rowKey;
7594 Element tr = row.getElement();
7595 Element element = elementOver;
7596 while (element != null && element.getParentElement() != tr) {
7597 element = element.getParentElement();
7598 }
7599 int childIndex = DOM.getChildIndex(tr, element);
7600 dropDetails.colkey = tHead.getHeaderCell(childIndex)
7601 .getColKey();
7602 dropDetails.dropLocation = DDUtil.getVerticalDropLocation(
7603 row.getElement(), drag.getCurrentGwtEvent(), 0.2);
7604 }
7605
7606 drag.getDropDetails().put("itemIdOver", dropDetails.overkey + "");
7607 drag.getDropDetails().put("detail",
7608 dropDetails.dropLocation != null
7609 ? dropDetails.dropLocation.toString()
7610 : null);
7611
7612 }
7613
7614 private Class<? extends Widget> getRowClass() {
7615
7616
7617 Iterator<Widget> it = scrollBody.iterator();
7618 if (it.hasNext()) {
7619 return it.next().getClass();
7620 }
7621 return null;
7622 }
7623
7624 @Override
7625 public void dragOver(VDragEvent drag) {
7626 TableDDDetails oldDetails = dropDetails;
7627 updateDropDetails(drag);
7628 if (!oldDetails.equals(dropDetails)) {
7629 deEmphasis();
7630 final TableDDDetails newDetails = dropDetails;
7631 VAcceptCallback cb = new VAcceptCallback() {
7632
7633 @Override
7634 public void accepted(VDragEvent event) {
7635 if (newDetails.equals(dropDetails)) {
7636 dragAccepted(event);
7637 }
7638
7639
7640
7641 }
7642 };
7643 validate(cb, drag);
7644 }
7645 }
7646
7647 @Override
7648 public void dragLeave(VDragEvent drag) {
7649 deEmphasis();
7650 super.dragLeave(drag);
7651 }
7652
7653 @Override
7654 public boolean drop(VDragEvent drag) {
7655 deEmphasis();
7656 return super.drop(drag);
7657 }
7658
7659 private void deEmphasis() {
7660 UIObject.setStyleName(getElement(), getStylePrimaryName() + "-drag",
7661 false);
7662 if (lastEmphasized == null) {
7663 return;
7664 }
7665 for (Widget w : scrollBody.renderedRows) {
7666 VScrollTableRow row = (VScrollTableRow) w;
7667 if (lastEmphasized != null
7668 && row.rowKey == lastEmphasized.overkey) {
7669 String stylename = ROWSTYLEBASE
7670 + lastEmphasized.dropLocation.toString()
7671 .toLowerCase(Locale.ROOT);
7672 VScrollTableRow.setStyleName(row.getElement(), stylename,
7673 false);
7674 lastEmphasized = null;
7675 return;
7676 }
7677 }
7678 }
7679
7680
7681
7682
7683
7684 private void emphasis(TableDDDetails details) {
7685 deEmphasis();
7686 UIObject.setStyleName(getElement(), getStylePrimaryName() + "-drag",
7687 true);
7688
7689 for (Widget w : scrollBody.renderedRows) {
7690 VScrollTableRow row = (VScrollTableRow) w;
7691 if (details != null && details.overkey == row.rowKey) {
7692 String stylename = ROWSTYLEBASE + details.dropLocation
7693 .toString().toLowerCase(Locale.ROOT);
7694 VScrollTableRow.setStyleName(row.getElement(), stylename,
7695 true);
7696 lastEmphasized = details;
7697 return;
7698 }
7699 }
7700 }
7701
7702 @Override
7703 protected void dragAccepted(VDragEvent drag) {
7704 emphasis(dropDetails);
7705 }
7706
7707 @Override
7708 public ComponentConnector getConnector() {
7709 return ConnectorMap.get(client).getConnector(VScrollTablePatched.this);
7710 }
7711
7712 @Override
7713 public ApplicationConnection getApplicationConnection() {
7714 return client;
7715 }
7716
7717 }
7718
7719 protected VScrollTableRow getFocusedRow() {
7720 return focusedRow;
7721 }
7722
7723
7724
7725
7726
7727
7728
7729
7730 public boolean setRowFocus(VScrollTableRow row) {
7731
7732 if (!isSelectable()) {
7733 return false;
7734 }
7735
7736
7737 if (focusedRow != null && focusedRow != row) {
7738 focusedRow.removeStyleName(getStylePrimaryName() + "-focus");
7739 }
7740
7741 if (row != null) {
7742
7743 row.addStyleName(getStylePrimaryName() + "-focus");
7744
7745
7746
7747
7748 if (row == focusedRow) {
7749 return false;
7750 }
7751
7752
7753 focusedRow = row;
7754
7755 if (hasFocus()) {
7756 ensureRowIsVisible(row);
7757 }
7758
7759 return true;
7760 }
7761
7762 return false;
7763 }
7764
7765
7766
7767
7768
7769
7770
7771 private void ensureRowIsVisible(VScrollTableRow row) {
7772 if (BrowserInfo.get().isTouchDevice()) {
7773
7774
7775 return;
7776 }
7777
7778
7779
7780
7781 WidgetUtil.scrollIntoViewVertically(row.getElement());
7782 }
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
7794
7795 protected boolean handleNavigation(int keycode, boolean ctrl,
7796 boolean shift) {
7797 if (keycode == KeyCodes.KEY_TAB || keycode == KeyCodes.KEY_SHIFT) {
7798
7799 return false;
7800 }
7801
7802
7803 if (!isSelectable() && keycode == getNavigationDownKey()) {
7804 scrollBodyPanel.setScrollPosition(
7805 scrollBodyPanel.getScrollPosition() + scrollingVelocity);
7806 return true;
7807 } else if (keycode == getNavigationDownKey()) {
7808 if (isMultiSelectModeAny() && moveFocusDown()) {
7809 selectFocusedRow(ctrl, shift);
7810
7811 } else if (isSingleSelectMode() && !shift && moveFocusDown()) {
7812 selectFocusedRow(ctrl, shift);
7813 }
7814 return true;
7815 }
7816
7817
7818 if (!isSelectable() && keycode == getNavigationUpKey()) {
7819 scrollBodyPanel.setScrollPosition(
7820 scrollBodyPanel.getScrollPosition() - scrollingVelocity);
7821 return true;
7822 } else if (keycode == getNavigationUpKey()) {
7823 if (isMultiSelectModeAny() && moveFocusUp()) {
7824 selectFocusedRow(ctrl, shift);
7825 } else if (isSingleSelectMode() && !shift && moveFocusUp()) {
7826 selectFocusedRow(ctrl, shift);
7827 }
7828 return true;
7829 }
7830
7831 if (keycode == getNavigationLeftKey()) {
7832
7833 scrollBodyPanel.setHorizontalScrollPosition(
7834 scrollBodyPanel.getHorizontalScrollPosition()
7835 - scrollingVelocity);
7836 return true;
7837
7838 } else if (keycode == getNavigationRightKey()) {
7839
7840 scrollBodyPanel.setHorizontalScrollPosition(
7841 scrollBodyPanel.getHorizontalScrollPosition()
7842 + scrollingVelocity);
7843 return true;
7844 }
7845
7846
7847 if (isSelectable() && keycode == getNavigationSelectKey()) {
7848 if (isSingleSelectMode()) {
7849 boolean wasSelected = focusedRow.isSelected();
7850 deselectAll();
7851 if (!wasSelected || !nullSelectionAllowed) {
7852 focusedRow.toggleSelection();
7853 }
7854 } else {
7855 focusedRow.toggleSelection();
7856 removeRowFromUnsentSelectionRanges(focusedRow);
7857 }
7858
7859 sendSelectedRows();
7860 return true;
7861 }
7862
7863
7864 if (keycode == getNavigationPageDownKey()) {
7865 if (isSelectable()) {
7866
7867
7868
7869
7870
7871
7872 if (!isFocusAtTheEndOfTable()) {
7873 VScrollTableRow lastVisibleRowInViewPort = scrollBody
7874 .getRowByRowIndex(firstRowInViewPort
7875 + getFullyVisibleRowCount() - 1);
7876 if (lastVisibleRowInViewPort != null
7877 && lastVisibleRowInViewPort != focusedRow) {
7878
7879
7880 setRowFocus(lastVisibleRowInViewPort);
7881 selectFocusedRow(ctrl, shift);
7882 updateFirstVisibleAndSendSelectedRows();
7883 } else {
7884 int indexOfToBeFocused = focusedRow.getIndex()
7885 + getFullyVisibleRowCount();
7886 if (indexOfToBeFocused >= totalRows) {
7887 indexOfToBeFocused = totalRows - 1;
7888 }
7889 VScrollTableRow toBeFocusedRow = scrollBody
7890 .getRowByRowIndex(indexOfToBeFocused);
7891
7892 if (toBeFocusedRow != null) {
7893
7894
7895
7896 setRowFocus(toBeFocusedRow);
7897 selectFocusedRow(ctrl, shift);
7898
7899 updateFirstVisibleAndSendSelectedRows();
7900 } else {
7901
7902
7903
7904 selectLastItemInNextRender = true;
7905 multiselectPending = shift;
7906 scrollByPagelength(1);
7907 }
7908 }
7909 }
7910 } else {
7911
7912 scrollByPagelength(1);
7913 }
7914 return true;
7915 }
7916
7917
7918 if (keycode == getNavigationPageUpKey()) {
7919 if (isSelectable()) {
7920
7921
7922
7923
7924
7925
7926 if (!isFocusAtTheBeginningOfTable()) {
7927 VScrollTableRow firstVisibleRowInViewPort = scrollBody
7928 .getRowByRowIndex(firstRowInViewPort);
7929 if (firstVisibleRowInViewPort != null
7930 && firstVisibleRowInViewPort != focusedRow) {
7931
7932
7933 setRowFocus(firstVisibleRowInViewPort);
7934 selectFocusedRow(ctrl, shift);
7935 updateFirstVisibleAndSendSelectedRows();
7936 } else {
7937 int indexOfToBeFocused = focusedRow.getIndex()
7938 - getFullyVisibleRowCount();
7939 if (indexOfToBeFocused < 0) {
7940 indexOfToBeFocused = 0;
7941 }
7942 VScrollTableRow toBeFocusedRow = scrollBody
7943 .getRowByRowIndex(indexOfToBeFocused);
7944
7945 if (toBeFocusedRow != null) {
7946
7947
7948 setRowFocus(toBeFocusedRow);
7949 selectFocusedRow(ctrl, shift);
7950
7951 updateFirstVisibleAndSendSelectedRows();
7952 } else {
7953
7954
7955
7956
7957 selectFirstItemInNextRender = true;
7958 multiselectPending = shift;
7959 scrollByPagelength(-1);
7960 }
7961 }
7962 }
7963 } else {
7964
7965 scrollByPagelength(-1);
7966 }
7967
7968 return true;
7969 }
7970
7971
7972 if (keycode == getNavigationStartKey()) {
7973 scrollBodyPanel.setScrollPosition(0);
7974 if (isSelectable()) {
7975 if (focusedRow != null && focusedRow.getIndex() == 0) {
7976 return false;
7977 } else {
7978 VScrollTableRow rowByRowIndex = (VScrollTableRow) scrollBody
7979 .iterator().next();
7980 if (rowByRowIndex.getIndex() == 0) {
7981 setRowFocus(rowByRowIndex);
7982 selectFocusedRow(ctrl, shift);
7983 updateFirstVisibleAndSendSelectedRows();
7984 } else {
7985
7986 if (ctrl) {
7987 focusFirstItemInNextRender = true;
7988 } else {
7989 selectFirstItemInNextRender = true;
7990 multiselectPending = shift;
7991 }
7992 }
7993 }
7994 }
7995 return true;
7996 }
7997
7998
7999 if (keycode == getNavigationEndKey()) {
8000 scrollBodyPanel.setScrollPosition(scrollBody.getOffsetHeight());
8001 if (isSelectable()) {
8002 final int lastRendered = scrollBody.getLastRendered();
8003 if (lastRendered + 1 == totalRows) {
8004 VScrollTableRow rowByRowIndex = scrollBody
8005 .getRowByRowIndex(lastRendered);
8006 if (focusedRow != rowByRowIndex) {
8007 setRowFocus(rowByRowIndex);
8008 selectFocusedRow(ctrl, shift);
8009 updateFirstVisibleAndSendSelectedRows();
8010 }
8011 } else {
8012 if (ctrl) {
8013 focusLastItemInNextRender = true;
8014 } else {
8015 selectLastItemInNextRender = true;
8016 multiselectPending = shift;
8017 }
8018 }
8019 }
8020 return true;
8021 }
8022
8023 return false;
8024 }
8025
8026 private boolean isFocusAtTheBeginningOfTable() {
8027 return focusedRow.getIndex() == 0;
8028 }
8029
8030 private boolean isFocusAtTheEndOfTable() {
8031 return focusedRow.getIndex() + 1 >= totalRows;
8032 }
8033
8034 private int getFullyVisibleRowCount() {
8035 return (int) (scrollBodyPanel.getOffsetHeight()
8036 / scrollBody.getRowHeight());
8037 }
8038
8039 private void scrollByPagelength(int i) {
8040 int pixels = i * scrollBodyPanel.getOffsetHeight();
8041 int newPixels = scrollBodyPanel.getScrollPosition() + pixels;
8042 if (newPixels < 0) {
8043 newPixels = 0;
8044 }
8045
8046 scrollBodyPanel.setScrollPosition(newPixels);
8047 }
8048
8049
8050
8051
8052
8053
8054
8055
8056
8057 @Override
8058 public void onFocus(FocusEvent event) {
8059 if (isFocusable()) {
8060 hasFocus = true;
8061
8062
8063 if (focusedRow == null) {
8064 focusRowFromBody();
8065 } else {
8066 setRowFocus(focusedRow);
8067 }
8068 }
8069 }
8070
8071
8072
8073
8074
8075
8076
8077
8078
8079 @Override
8080 public void onBlur(BlurEvent event) {
8081 onBlur();
8082 }
8083
8084 private void onBlur() {
8085 hasFocus = false;
8086 navKeyDown = false;
8087
8088 if (BrowserInfo.get().isIE()) {
8089
8090
8091
8092
8093
8094 Element focusedElement = WidgetUtil.getFocusedElement();
8095 if (focusedElement != null
8096 && focusedElement != scrollBodyPanel.getFocusElement()
8097 && Util.getConnectorForElement(client, getParent(),
8098 focusedElement) == ConnectorMap.get(client)
8099 .getConnector(this)) {
8100
8101
8102
8103
8104 focus();
8105 return;
8106 }
8107 }
8108
8109 if (isFocusable()) {
8110
8111 setRowFocus(null);
8112 }
8113 }
8114
8115
8116
8117
8118
8119
8120
8121 public void removeRowFromUnsentSelectionRanges(VScrollTableRow row) {
8122 Collection<SelectionRange> newRanges = null;
8123 for (Iterator<SelectionRange> iterator = selectedRowRanges
8124 .iterator(); iterator.hasNext();) {
8125 SelectionRange range = iterator.next();
8126 if (range.inRange(row)) {
8127
8128 Collection<SelectionRange> splitranges = range.split(row);
8129 if (newRanges == null) {
8130 newRanges = new ArrayList<SelectionRange>();
8131 }
8132 newRanges.addAll(splitranges);
8133 iterator.remove();
8134 }
8135 }
8136 if (newRanges != null) {
8137 selectedRowRanges.addAll(newRanges);
8138 }
8139 }
8140
8141
8142
8143
8144
8145
8146 public boolean isFocusable() {
8147 if (scrollBody != null && enabled) {
8148 return !(!hasHorizontalScrollbar() && !hasVerticalScrollbar()
8149 && !isSelectable());
8150 }
8151 return false;
8152 }
8153
8154 private boolean hasHorizontalScrollbar() {
8155 return scrollBody.getOffsetWidth() > scrollBodyPanel.getOffsetWidth();
8156 }
8157
8158 private boolean hasVerticalScrollbar() {
8159 return scrollBody.getOffsetHeight() > scrollBodyPanel.getOffsetHeight();
8160 }
8161
8162
8163
8164
8165
8166
8167
8168 @Override
8169 public void focus() {
8170 if (isFocusable()) {
8171 scrollBodyPanel.focus();
8172 }
8173 }
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184
8185
8186
8187 public void setProperTabIndex() {
8188 int storedScrollTop = 0;
8189 int storedScrollLeft = 0;
8190
8191 if (BrowserInfo.get().getOperaVersion() >= 11) {
8192
8193 storedScrollTop = scrollBodyPanel.getScrollPosition();
8194 storedScrollLeft = scrollBodyPanel.getHorizontalScrollPosition();
8195 }
8196
8197 if (tabIndex == 0 && !isFocusable()) {
8198 scrollBodyPanel.setTabIndex(-1);
8199 } else {
8200 scrollBodyPanel.setTabIndex(tabIndex);
8201 }
8202
8203 if (BrowserInfo.get().getOperaVersion() >= 11) {
8204
8205 scrollBodyPanel.setScrollPosition(storedScrollTop);
8206 scrollBodyPanel.setHorizontalScrollPosition(storedScrollLeft);
8207 }
8208 }
8209
8210 public void startScrollingVelocityTimer() {
8211 if (scrollingVelocityTimer == null) {
8212 scrollingVelocityTimer = new Timer() {
8213
8214 @Override
8215 public void run() {
8216 scrollingVelocity++;
8217 }
8218 };
8219 scrollingVelocityTimer.scheduleRepeating(100);
8220 }
8221 }
8222
8223 public void cancelScrollingVelocityTimer() {
8224 if (scrollingVelocityTimer != null) {
8225
8226 scrollingVelocityTimer.cancel();
8227 scrollingVelocityTimer = null;
8228 scrollingVelocity = 10;
8229 }
8230 }
8231
8232
8233
8234
8235
8236
8237 private boolean isNavigationKey(int keyCode) {
8238 return keyCode == getNavigationUpKey()
8239 || keyCode == getNavigationLeftKey()
8240 || keyCode == getNavigationRightKey()
8241 || keyCode == getNavigationDownKey()
8242 || keyCode == getNavigationPageUpKey()
8243 || keyCode == getNavigationPageDownKey()
8244 || keyCode == getNavigationEndKey()
8245 || keyCode == getNavigationStartKey();
8246 }
8247
8248 public void lazyRevertFocusToRow(
8249 final VScrollTableRow currentlyFocusedRow) {
8250 Scheduler.get().scheduleFinally(new ScheduledCommand() {
8251 @Override
8252 public void execute() {
8253 if (currentlyFocusedRow != null) {
8254 setRowFocus(currentlyFocusedRow);
8255 } else {
8256 getLogger().info("no row?");
8257 focusRowFromBody();
8258 }
8259 scrollBody.ensureFocus();
8260 }
8261 });
8262 }
8263
8264 @Override
8265 public Action[] getActions() {
8266 if (bodyActionKeys == null) {
8267 return new Action[] {};
8268 }
8269 final Action[] actions = new Action[bodyActionKeys.length];
8270 for (int i = 0; i < actions.length; i++) {
8271 final String actionKey = bodyActionKeys[i];
8272 Action bodyAction = new TreeAction(this, null, actionKey);
8273 bodyAction.setCaption(getActionCaption(actionKey));
8274 bodyAction.setIconUrl(getActionIcon(actionKey));
8275 actions[i] = bodyAction;
8276 }
8277 return actions;
8278 }
8279
8280 @Override
8281 public ApplicationConnection getClient() {
8282 return client;
8283 }
8284
8285 @Override
8286 public String getPaintableId() {
8287 return paintableId;
8288 }
8289
8290
8291
8292
8293
8294
8295
8296
8297 private static native JavaScriptObject getPreventTextSelectionIEHack()
8298
8299
8300 ;
8301
8302 public void triggerLazyColumnAdjustment(boolean now) {
8303 lazyAdjustColumnWidths.cancel();
8304 if (now) {
8305 lazyAdjustColumnWidths.run();
8306 } else {
8307 lazyAdjustColumnWidths.schedule(LAZY_COLUMN_ADJUST_TIMEOUT);
8308 }
8309 }
8310
8311 private boolean isDynamicWidth() {
8312 ComponentConnector paintable = ConnectorMap.get(client)
8313 .getConnector(this);
8314 return paintable.isUndefinedWidth();
8315 }
8316
8317 private boolean isDynamicHeight() {
8318 ComponentConnector paintable = ConnectorMap.get(client)
8319 .getConnector(this);
8320 if (paintable == null) {
8321
8322
8323
8324 return false;
8325 }
8326 return paintable.isUndefinedHeight();
8327 }
8328
8329 private void debug(String msg) {
8330 if (enableDebug) {
8331 getLogger().severe(msg);
8332 }
8333 }
8334
8335 public Widget getWidgetForPaintable() {
8336 return this;
8337 }
8338
8339 private static final String SUBPART_HEADER = "header";
8340 private static final String SUBPART_FOOTER = "footer";
8341 private static final String SUBPART_ROW = "row";
8342 private static final String SUBPART_COL = "col";
8343
8344
8345
8346
8347 private static final RegExp SUBPART_HEADER_REGEXP = RegExp
8348 .compile(SUBPART_HEADER + "\\[(\\d+)\\]");
8349
8350
8351
8352
8353 private static final RegExp SUBPART_FOOTER_REGEXP = RegExp
8354 .compile(SUBPART_FOOTER + "\\[(\\d+)\\]");
8355
8356 private static final RegExp SUBPART_ROW_REGEXP = RegExp
8357 .compile(SUBPART_ROW + "\\[(\\d+)]");
8358
8359
8360
8361 private static final RegExp SUBPART_ROW_COL_REGEXP = RegExp.compile(
8362 SUBPART_ROW + "\\[(\\d+)\\]/" + SUBPART_COL + "\\[(\\d+)\\]");
8363
8364 @Override
8365 public com.google.gwt.user.client.Element getSubPartElement(
8366 String subPart) {
8367 if (SUBPART_ROW_COL_REGEXP.test(subPart)) {
8368 MatchResult result = SUBPART_ROW_COL_REGEXP.exec(subPart);
8369 int rowIx = Integer.valueOf(result.getGroup(1));
8370 int colIx = Integer.valueOf(result.getGroup(2));
8371 VScrollTableRow row = scrollBody.getRowByRowIndex(rowIx);
8372 if (row != null) {
8373 Element rowElement = row.getElement();
8374 if (colIx < rowElement.getChildCount()) {
8375 return rowElement.getChild(colIx).getFirstChild().cast();
8376 }
8377 }
8378
8379 } else if (SUBPART_ROW_REGEXP.test(subPart)) {
8380 MatchResult result = SUBPART_ROW_REGEXP.exec(subPart);
8381 int rowIx = Integer.valueOf(result.getGroup(1));
8382 VScrollTableRow row = scrollBody.getRowByRowIndex(rowIx);
8383 if (row != null) {
8384 return row.getElement();
8385 }
8386
8387 } else if (SUBPART_HEADER_REGEXP.test(subPart)) {
8388 MatchResult result = SUBPART_HEADER_REGEXP.exec(subPart);
8389 int headerIx = Integer.valueOf(result.getGroup(1));
8390 HeaderCell headerCell = tHead.getHeaderCell(headerIx);
8391 if (headerCell != null) {
8392 return headerCell.getElement();
8393 }
8394
8395 } else if (SUBPART_FOOTER_REGEXP.test(subPart)) {
8396 MatchResult result = SUBPART_FOOTER_REGEXP.exec(subPart);
8397 int footerIx = Integer.valueOf(result.getGroup(1));
8398 FooterCell footerCell = tFoot.getFooterCell(footerIx);
8399 if (footerCell != null) {
8400 return footerCell.getElement();
8401 }
8402 }
8403
8404 return null;
8405 }
8406
8407 @Override
8408 public String getSubPartName(
8409 com.google.gwt.user.client.Element subElement) {
8410 Widget widget = WidgetUtil.findWidget(subElement, null);
8411 if (widget instanceof HeaderCell) {
8412 return SUBPART_HEADER + "[" + tHead.visibleCells.indexOf(widget)
8413 + "]";
8414 } else if (widget instanceof FooterCell) {
8415 return SUBPART_FOOTER + "[" + tFoot.visibleCells.indexOf(widget)
8416 + "]";
8417 } else if (widget instanceof VScrollTableRow) {
8418
8419 VScrollTableRow row = (VScrollTableRow) widget;
8420 int rowIx = scrollBody.indexOf(row);
8421 if (rowIx >= 0) {
8422 int colIx = -1;
8423 for (int ix = 0; ix < row.getElement().getChildCount(); ix++) {
8424 if (row.getElement().getChild(ix)
8425 .isOrHasChild(subElement)) {
8426 colIx = ix;
8427 break;
8428 }
8429 }
8430 if (colIx >= 0) {
8431 return SUBPART_ROW + "[" + rowIx + "]/" + SUBPART_COL + "["
8432 + colIx + "]";
8433 }
8434 return SUBPART_ROW + "[" + rowIx + "]";
8435 }
8436 }
8437
8438 return null;
8439 }
8440
8441
8442
8443
8444 public void onUnregister() {
8445 if (addCloseHandler != null) {
8446 addCloseHandler.removeHandler();
8447 }
8448 }
8449
8450
8451
8452
8453 @Override
8454 public boolean isWorkPending() {
8455 return lazyAdjustColumnWidths.isRunning();
8456 }
8457
8458 private static Logger getLogger() {
8459 return Logger.getLogger(VScrollTable.class.getName());
8460 }
8461
8462 public ChildMeasurementHint getChildMeasurementHint() {
8463 return childMeasurementHint;
8464 }
8465
8466 public void setChildMeasurementHint(ChildMeasurementHint hint) {
8467 childMeasurementHint = hint;
8468 }
8469
8470 private boolean hasFocus() {
8471 if (hasFocus && BrowserInfo.get().isIE()) {
8472 com.google.gwt.user.client.Element focusedElement = Util
8473 .getIEFocusedElement();
8474 if (!getElement().isOrHasChild(focusedElement)) {
8475
8476 getLogger().warning(
8477 "IE did not send a blur event, firing manually");
8478 onBlur();
8479 }
8480 }
8481 return hasFocus;
8482 }
8483
8484 }