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