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