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