You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TableConnector.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui.table;
  17. import java.util.Iterator;
  18. import com.google.gwt.core.client.Scheduler;
  19. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  20. import com.google.gwt.dom.client.Element;
  21. import com.google.gwt.dom.client.Style.Position;
  22. import com.google.gwt.user.client.ui.Widget;
  23. import com.vaadin.client.ApplicationConnection;
  24. import com.vaadin.client.BrowserInfo;
  25. import com.vaadin.client.ComponentConnector;
  26. import com.vaadin.client.ConnectorHierarchyChangeEvent;
  27. import com.vaadin.client.DirectionalManagedLayout;
  28. import com.vaadin.client.Paintable;
  29. import com.vaadin.client.ServerConnector;
  30. import com.vaadin.client.TooltipInfo;
  31. import com.vaadin.client.UIDL;
  32. import com.vaadin.client.Util;
  33. import com.vaadin.client.ui.AbstractHasComponentsConnector;
  34. import com.vaadin.client.ui.PostLayoutListener;
  35. import com.vaadin.client.ui.VScrollTable;
  36. import com.vaadin.client.ui.VScrollTable.ContextMenuDetails;
  37. import com.vaadin.client.ui.VScrollTable.VScrollTableBody.VScrollTableRow;
  38. import com.vaadin.shared.ui.Connect;
  39. import com.vaadin.shared.ui.table.TableConstants;
  40. import com.vaadin.shared.ui.table.TableState;
  41. @Connect(com.vaadin.ui.Table.class)
  42. public class TableConnector extends AbstractHasComponentsConnector implements
  43. Paintable, DirectionalManagedLayout, PostLayoutListener {
  44. @Override
  45. protected void init() {
  46. super.init();
  47. getWidget().init(getConnection());
  48. }
  49. /*
  50. * (non-Javadoc)
  51. *
  52. * @see com.vaadin.client.ui.AbstractComponentConnector#onUnregister()
  53. */
  54. @Override
  55. public void onUnregister() {
  56. super.onUnregister();
  57. getWidget().onUnregister();
  58. }
  59. /*
  60. * (non-Javadoc)
  61. *
  62. * @see com.vaadin.client.Paintable#updateFromUIDL(com.vaadin.client.UIDL,
  63. * com.vaadin.client.ApplicationConnection)
  64. */
  65. @Override
  66. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  67. getWidget().rendering = true;
  68. // If a row has an open context menu, it will be closed as the row is
  69. // detached. Retain a reference here so we can restore the menu if
  70. // required.
  71. ContextMenuDetails contextMenuBeforeUpdate = getWidget().contextMenu;
  72. if (uidl.hasAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST)) {
  73. getWidget().serverCacheFirst = uidl
  74. .getIntAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST);
  75. getWidget().serverCacheLast = uidl
  76. .getIntAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_LAST);
  77. } else {
  78. getWidget().serverCacheFirst = -1;
  79. getWidget().serverCacheLast = -1;
  80. }
  81. /*
  82. * We need to do this before updateComponent since updateComponent calls
  83. * this.setHeight() which will calculate a new body height depending on
  84. * the space available.
  85. */
  86. if (uidl.hasAttribute("colfooters")) {
  87. getWidget().showColFooters = uidl.getBooleanAttribute("colfooters");
  88. }
  89. getWidget().tFoot.setVisible(getWidget().showColFooters);
  90. if (!isRealUpdate(uidl)) {
  91. getWidget().rendering = false;
  92. return;
  93. }
  94. getWidget().enabled = isEnabled();
  95. if (BrowserInfo.get().isIE8() && !getWidget().enabled) {
  96. /*
  97. * The disabled shim will not cover the table body if it is relative
  98. * in IE8. See #7324
  99. */
  100. getWidget().scrollBodyPanel.getElement().getStyle()
  101. .setPosition(Position.STATIC);
  102. } else if (BrowserInfo.get().isIE8()) {
  103. getWidget().scrollBodyPanel.getElement().getStyle()
  104. .setPosition(Position.RELATIVE);
  105. }
  106. getWidget().paintableId = uidl.getStringAttribute("id");
  107. getWidget().immediate = getState().immediate;
  108. int previousTotalRows = getWidget().totalRows;
  109. getWidget().updateTotalRows(uidl);
  110. boolean totalRowsHaveChanged = (getWidget().totalRows != previousTotalRows);
  111. getWidget().updateDragMode(uidl);
  112. getWidget().updateSelectionProperties(uidl, getState(), isReadOnly());
  113. if (uidl.hasAttribute("alb")) {
  114. getWidget().bodyActionKeys = uidl.getStringArrayAttribute("alb");
  115. } else {
  116. // Need to clear the actions if the action handlers have been
  117. // removed
  118. getWidget().bodyActionKeys = null;
  119. }
  120. getWidget().setCacheRateFromUIDL(uidl);
  121. getWidget().recalcWidths = uidl.hasAttribute("recalcWidths");
  122. if (getWidget().recalcWidths) {
  123. getWidget().tHead.clear();
  124. getWidget().tFoot.clear();
  125. }
  126. getWidget().updatePageLength(uidl);
  127. getWidget().updateFirstVisibleAndScrollIfNeeded(uidl);
  128. getWidget().showRowHeaders = uidl.getBooleanAttribute("rowheaders");
  129. getWidget().showColHeaders = uidl.getBooleanAttribute("colheaders");
  130. getWidget().updateSortingProperties(uidl);
  131. getWidget().updateActionMap(uidl);
  132. getWidget().updateColumnProperties(uidl);
  133. UIDL ac = uidl.getChildByTagName("-ac");
  134. if (ac == null) {
  135. if (getWidget().dropHandler != null) {
  136. // remove dropHandler if not present anymore
  137. getWidget().dropHandler = null;
  138. }
  139. } else {
  140. if (getWidget().dropHandler == null) {
  141. getWidget().dropHandler = getWidget().new VScrollTableDropHandler();
  142. }
  143. getWidget().dropHandler.updateAcceptRules(ac);
  144. }
  145. UIDL partialRowAdditions = uidl.getChildByTagName("prows");
  146. UIDL partialRowUpdates = uidl.getChildByTagName("urows");
  147. if (partialRowUpdates != null || partialRowAdditions != null) {
  148. getWidget().postponeSanityCheckForLastRendered = true;
  149. // we may have pending cache row fetch, cancel it. See #2136
  150. getWidget().rowRequestHandler.cancel();
  151. getWidget().updateRowsInBody(partialRowUpdates);
  152. getWidget().addAndRemoveRows(partialRowAdditions);
  153. // sanity check (in case the value has slipped beyond the total
  154. // amount of rows)
  155. getWidget().scrollBody.setLastRendered(getWidget().scrollBody
  156. .getLastRendered());
  157. getWidget().updateMaxIndent();
  158. } else {
  159. getWidget().postponeSanityCheckForLastRendered = false;
  160. UIDL rowData = uidl.getChildByTagName("rows");
  161. if (rowData != null) {
  162. // we may have pending cache row fetch, cancel it. See #2136
  163. getWidget().rowRequestHandler.cancel();
  164. if (!getWidget().recalcWidths
  165. && getWidget().initializedAndAttached) {
  166. getWidget().updateBody(rowData,
  167. uidl.getIntAttribute("firstrow"),
  168. uidl.getIntAttribute("rows"));
  169. if (getWidget().headerChangedDuringUpdate) {
  170. getWidget().triggerLazyColumnAdjustment(true);
  171. } else if (!getWidget().isScrollPositionVisible()
  172. || totalRowsHaveChanged
  173. || getWidget().lastRenderedHeight != getWidget().scrollBody
  174. .getOffsetHeight()) {
  175. // webkits may still bug with their disturbing scrollbar
  176. // bug, see #3457
  177. // Run overflow fix for the scrollable area
  178. // #6698 - If there's a scroll going on, don't abort it
  179. // by changing overflows as the length of the contents
  180. // *shouldn't* have changed (unless the number of rows
  181. // or the height of the widget has also changed)
  182. Util.runWebkitOverflowAutoFixDeferred(getWidget().scrollBodyPanel
  183. .getElement());
  184. }
  185. } else {
  186. getWidget().initializeRows(uidl, rowData);
  187. }
  188. }
  189. }
  190. boolean keyboardSelectionOverRowFetchInProgress = getWidget()
  191. .selectSelectedRows(uidl);
  192. // If a row had an open context menu before the update, and after the
  193. // update there's a row with the same key as that row, restore the
  194. // context menu. See #8526.
  195. showSavedContextMenu(contextMenuBeforeUpdate);
  196. if (!getWidget().isSelectable()) {
  197. getWidget().scrollBody.addStyleName(getWidget()
  198. .getStylePrimaryName() + "-body-noselection");
  199. } else {
  200. getWidget().scrollBody.removeStyleName(getWidget()
  201. .getStylePrimaryName() + "-body-noselection");
  202. }
  203. getWidget().hideScrollPositionAnnotation();
  204. // selection is no in sync with server, avoid excessive server visits by
  205. // clearing to flag used during the normal operation
  206. if (!keyboardSelectionOverRowFetchInProgress) {
  207. getWidget().selectionChanged = false;
  208. }
  209. /*
  210. * This is called when the Home or page up button has been pressed in
  211. * selectable mode and the next selected row was not yet rendered in the
  212. * client
  213. */
  214. if (getWidget().selectFirstItemInNextRender
  215. || getWidget().focusFirstItemInNextRender) {
  216. getWidget().selectFirstRenderedRowInViewPort(
  217. getWidget().focusFirstItemInNextRender);
  218. getWidget().selectFirstItemInNextRender = getWidget().focusFirstItemInNextRender = false;
  219. }
  220. /*
  221. * This is called when the page down or end button has been pressed in
  222. * selectable mode and the next selected row was not yet rendered in the
  223. * client
  224. */
  225. if (getWidget().selectLastItemInNextRender
  226. || getWidget().focusLastItemInNextRender) {
  227. getWidget().selectLastRenderedRowInViewPort(
  228. getWidget().focusLastItemInNextRender);
  229. getWidget().selectLastItemInNextRender = getWidget().focusLastItemInNextRender = false;
  230. }
  231. getWidget().multiselectPending = false;
  232. if (getWidget().focusedRow != null) {
  233. if (!getWidget().focusedRow.isAttached()
  234. && !getWidget().rowRequestHandler.isRequestHandlerRunning()) {
  235. // focused row has been orphaned, can't focus
  236. if (getWidget().selectedRowKeys.contains(getWidget().focusedRow
  237. .getKey())) {
  238. // if row cache was refreshed, focused row should be
  239. // in selection and exists with same index
  240. getWidget().setRowFocus(
  241. getWidget().getRenderedRowByKey(
  242. getWidget().focusedRow.getKey()));
  243. } else if (getWidget().selectedRowKeys.size() > 0) {
  244. // try to focus any row in selection
  245. getWidget().setRowFocus(
  246. getWidget().getRenderedRowByKey(
  247. getWidget().selectedRowKeys.iterator()
  248. .next()));
  249. } else {
  250. // try to focus any row
  251. getWidget().focusRowFromBody();
  252. }
  253. }
  254. }
  255. /*
  256. * If the server has (re)initialized the rows, our selectionRangeStart
  257. * row will point to an index that the server knows nothing about,
  258. * causing problems if doing multi selection with shift. The field will
  259. * be cleared a little later when the row focus has been restored.
  260. * (#8584)
  261. */
  262. if (uidl.hasAttribute(TableConstants.ATTRIBUTE_KEY_MAPPER_RESET)
  263. && uidl.getBooleanAttribute(TableConstants.ATTRIBUTE_KEY_MAPPER_RESET)
  264. && getWidget().selectionRangeStart != null) {
  265. assert !getWidget().selectionRangeStart.isAttached();
  266. getWidget().selectionRangeStart = getWidget().focusedRow;
  267. }
  268. getWidget().tabIndex = getState().tabIndex;
  269. getWidget().setProperTabIndex();
  270. Scheduler.get().scheduleFinally(new ScheduledCommand() {
  271. @Override
  272. public void execute() {
  273. getWidget().resizeSortedColumnForSortIndicator();
  274. }
  275. });
  276. // Remember this to detect situations where overflow hack might be
  277. // needed during scrolling
  278. getWidget().lastRenderedHeight = getWidget().scrollBody
  279. .getOffsetHeight();
  280. getWidget().rendering = false;
  281. getWidget().headerChangedDuringUpdate = false;
  282. }
  283. @Override
  284. public VScrollTable getWidget() {
  285. return (VScrollTable) super.getWidget();
  286. }
  287. @Override
  288. public void updateCaption(ComponentConnector component) {
  289. // NOP, not rendered
  290. }
  291. @Override
  292. public void layoutVertically() {
  293. getWidget().updateHeight();
  294. }
  295. @Override
  296. public void layoutHorizontally() {
  297. getWidget().updateWidth();
  298. }
  299. @Override
  300. public void postLayout() {
  301. VScrollTable table = getWidget();
  302. if (table.sizeNeedsInit) {
  303. table.sizeInit();
  304. Scheduler.get().scheduleFinally(new ScheduledCommand() {
  305. @Override
  306. public void execute() {
  307. // IE8 needs some hacks to measure sizes correctly
  308. Util.forceIE8Redraw(getWidget().getElement());
  309. getLayoutManager().setNeedsMeasure(TableConnector.this);
  310. ServerConnector parent = getParent();
  311. if (parent instanceof ComponentConnector) {
  312. getLayoutManager().setNeedsMeasure(
  313. (ComponentConnector) parent);
  314. }
  315. getLayoutManager().setNeedsVerticalLayout(
  316. TableConnector.this);
  317. getLayoutManager().layoutNow();
  318. }
  319. });
  320. }
  321. }
  322. @Override
  323. public boolean isReadOnly() {
  324. return super.isReadOnly() || getState().propertyReadOnly;
  325. }
  326. @Override
  327. public TableState getState() {
  328. return (TableState) super.getState();
  329. }
  330. /**
  331. * Shows a saved row context menu if the row for the context menu is still
  332. * visible. Does nothing if a context menu has not been saved.
  333. *
  334. * @param savedContextMenu
  335. */
  336. public void showSavedContextMenu(ContextMenuDetails savedContextMenu) {
  337. if (isEnabled() && savedContextMenu != null) {
  338. Iterator<Widget> iterator = getWidget().scrollBody.iterator();
  339. while (iterator.hasNext()) {
  340. Widget w = iterator.next();
  341. VScrollTableRow row = (VScrollTableRow) w;
  342. if (row.getKey().equals(savedContextMenu.rowKey)) {
  343. row.showContextMenu(savedContextMenu.left,
  344. savedContextMenu.top);
  345. }
  346. }
  347. }
  348. }
  349. @Override
  350. public TooltipInfo getTooltipInfo(Element element) {
  351. TooltipInfo info = null;
  352. if (element != getWidget().getElement()) {
  353. Object node = Util.findWidget(element, VScrollTableRow.class);
  354. if (node != null) {
  355. VScrollTableRow row = (VScrollTableRow) node;
  356. info = row.getTooltip(element);
  357. }
  358. }
  359. if (info == null) {
  360. info = super.getTooltipInfo(element);
  361. }
  362. return info;
  363. }
  364. @Override
  365. public boolean hasTooltip() {
  366. /*
  367. * Tooltips for individual rows and cells are not processed until
  368. * updateFromUIDL, so we can't be sure that there are no tooltips during
  369. * onStateChange when this method is used.
  370. */
  371. return true;
  372. }
  373. @Override
  374. public void onConnectorHierarchyChange(
  375. ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) {
  376. // TODO Move code from updateFromUIDL to this method
  377. }
  378. }