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