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