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