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