Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

GridConnector.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. /*
  2. * Copyright 2000-2016 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.connectors.grid;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Objects;
  25. import java.util.Set;
  26. import java.util.stream.Collectors;
  27. import com.google.gwt.core.client.Scheduler;
  28. import com.google.gwt.dom.client.Element;
  29. import com.google.gwt.dom.client.EventTarget;
  30. import com.google.gwt.dom.client.NativeEvent;
  31. import com.google.gwt.event.shared.HandlerRegistration;
  32. import com.vaadin.client.ComponentConnector;
  33. import com.vaadin.client.ConnectorHierarchyChangeEvent;
  34. import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler;
  35. import com.vaadin.client.DeferredWorker;
  36. import com.vaadin.client.HasComponentsConnector;
  37. import com.vaadin.client.MouseEventDetailsBuilder;
  38. import com.vaadin.client.TooltipInfo;
  39. import com.vaadin.client.WidgetUtil;
  40. import com.vaadin.client.annotations.OnStateChange;
  41. import com.vaadin.client.communication.StateChangeEvent;
  42. import com.vaadin.client.connectors.AbstractListingConnector;
  43. import com.vaadin.client.connectors.grid.ColumnConnector.CustomColumn;
  44. import com.vaadin.client.data.DataSource;
  45. import com.vaadin.client.ui.SimpleManagedLayout;
  46. import com.vaadin.client.widget.escalator.RowContainer;
  47. import com.vaadin.client.widget.grid.CellReference;
  48. import com.vaadin.client.widget.grid.EventCellReference;
  49. import com.vaadin.client.widget.grid.events.BodyClickHandler;
  50. import com.vaadin.client.widget.grid.events.BodyDoubleClickHandler;
  51. import com.vaadin.client.widget.grid.events.GridClickEvent;
  52. import com.vaadin.client.widget.grid.events.GridDoubleClickEvent;
  53. import com.vaadin.client.widget.grid.sort.SortEvent;
  54. import com.vaadin.client.widget.grid.sort.SortOrder;
  55. import com.vaadin.client.widgets.Grid;
  56. import com.vaadin.client.widgets.Grid.Column;
  57. import com.vaadin.client.widgets.Grid.FooterRow;
  58. import com.vaadin.client.widgets.Grid.HeaderRow;
  59. import com.vaadin.client.widgets.Grid.SelectionColumn;
  60. import com.vaadin.shared.MouseEventDetails;
  61. import com.vaadin.shared.data.sort.SortDirection;
  62. import com.vaadin.shared.ui.Connect;
  63. import com.vaadin.shared.ui.grid.GridClientRpc;
  64. import com.vaadin.shared.ui.grid.GridConstants;
  65. import com.vaadin.shared.ui.grid.GridConstants.Section;
  66. import com.vaadin.shared.ui.grid.GridServerRpc;
  67. import com.vaadin.shared.ui.grid.GridState;
  68. import com.vaadin.shared.ui.grid.ScrollDestination;
  69. import com.vaadin.shared.ui.grid.SectionState;
  70. import com.vaadin.shared.ui.grid.SectionState.CellState;
  71. import com.vaadin.shared.ui.grid.SectionState.RowState;
  72. import elemental.json.JsonObject;
  73. /**
  74. * A connector class for the typed Grid component.
  75. *
  76. * @author Vaadin Ltd
  77. * @since 8.0
  78. */
  79. @Connect(com.vaadin.ui.Grid.class)
  80. public class GridConnector extends AbstractListingConnector
  81. implements HasComponentsConnector, SimpleManagedLayout, DeferredWorker {
  82. private Set<Runnable> refreshDetailsCallbacks = new HashSet<>();
  83. private class ItemClickHandler
  84. implements BodyClickHandler, BodyDoubleClickHandler {
  85. @Override
  86. public void onClick(GridClickEvent event) {
  87. if (hasEventListener(GridConstants.ITEM_CLICK_EVENT_ID)) {
  88. fireItemClick(event.getTargetCell(), event.getNativeEvent());
  89. }
  90. }
  91. @Override
  92. public void onDoubleClick(GridDoubleClickEvent event) {
  93. if (hasEventListener(GridConstants.ITEM_CLICK_EVENT_ID)) {
  94. fireItemClick(event.getTargetCell(), event.getNativeEvent());
  95. }
  96. }
  97. private void fireItemClick(CellReference<?> cell,
  98. NativeEvent mouseEvent) {
  99. String rowKey = getRowKey((JsonObject) cell.getRow());
  100. String columnId = columnToIdMap.get(cell.getColumn());
  101. getRpcProxy(GridServerRpc.class).itemClick(rowKey, columnId,
  102. MouseEventDetailsBuilder
  103. .buildMouseEventDetails(mouseEvent));
  104. }
  105. }
  106. /* Map to keep track of all added columns */
  107. private Map<CustomColumn, String> columnToIdMap = new HashMap<>();
  108. private Map<String, CustomColumn> idToColumn = new HashMap<>();
  109. /* Child component list for HasComponentsConnector */
  110. private List<ComponentConnector> childComponents;
  111. private ItemClickHandler itemClickHandler = new ItemClickHandler();
  112. private boolean rowHeightScheduled = false;
  113. /**
  114. * Gets the string identifier of the given column in this grid.
  115. *
  116. * @param column
  117. * the column whose id to get
  118. * @return the string id of the column
  119. */
  120. public String getColumnId(Column<?, ?> column) {
  121. return columnToIdMap.get(column);
  122. }
  123. /**
  124. * Gets the column corresponding to the given string identifier.
  125. *
  126. * @param columnId
  127. * the id of the column to get
  128. * @return the column with the given id
  129. */
  130. public CustomColumn getColumn(String columnId) {
  131. return idToColumn.get(columnId);
  132. }
  133. @Override
  134. @SuppressWarnings("unchecked")
  135. public Grid<JsonObject> getWidget() {
  136. return (Grid<JsonObject>) super.getWidget();
  137. }
  138. /**
  139. * Method called for a row details refresh. Runs all callbacks if any
  140. * details were shown and clears the callbacks.
  141. *
  142. * @param detailsShown
  143. * True if any details were set visible
  144. */
  145. protected void detailsRefreshed(boolean detailsShown) {
  146. if (detailsShown) {
  147. refreshDetailsCallbacks.forEach(Runnable::run);
  148. }
  149. refreshDetailsCallbacks.clear();
  150. }
  151. /**
  152. * Method target for when one single details has been updated and we might
  153. * need to scroll it into view.
  154. *
  155. * @param rowIndex
  156. * index of updated row
  157. */
  158. protected void singleDetailsOpened(int rowIndex) {
  159. addDetailsRefreshCallback(() -> {
  160. if (rowHasDetails(rowIndex)) {
  161. getWidget().scrollToRow(rowIndex);
  162. }
  163. });
  164. }
  165. /**
  166. * Add a single use details runnable callback for when we get a call to
  167. * {@link #detailsRefreshed(boolean)}.
  168. *
  169. * @param refreshCallback
  170. * Details refreshed callback
  171. */
  172. private void addDetailsRefreshCallback(Runnable refreshCallback) {
  173. refreshDetailsCallbacks.add(refreshCallback);
  174. }
  175. /**
  176. * Check if we have details for given row.
  177. *
  178. * @param rowIndex
  179. * @return
  180. */
  181. private boolean rowHasDetails(int rowIndex) {
  182. JsonObject row = getWidget().getDataSource().getRow(rowIndex);
  183. return row != null && row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE)
  184. && !row.getString(GridState.JSONKEY_DETAILS_VISIBLE).isEmpty();
  185. }
  186. @Override
  187. protected void init() {
  188. super.init();
  189. updateWidgetStyleNames();
  190. Grid<JsonObject> grid = getWidget();
  191. // Trigger early redraw of both grid static sections.
  192. grid.setHeaderVisible(!grid.isHeaderVisible());
  193. grid.setFooterVisible(!grid.isFooterVisible());
  194. registerRpc(GridClientRpc.class, new GridClientRpc() {
  195. @Override
  196. public void scrollToRow(int row, ScrollDestination destination) {
  197. Scheduler.get().scheduleFinally(
  198. () -> grid.scrollToRow(row, destination));
  199. // Add details refresh listener and handle possible detail for
  200. // scrolled row.
  201. addDetailsRefreshCallback(() -> {
  202. if (rowHasDetails(row)) {
  203. grid.scrollToRow(row, destination);
  204. }
  205. });
  206. }
  207. @Override
  208. public void scrollToStart() {
  209. Scheduler.get().scheduleFinally(() -> grid.scrollToStart());
  210. }
  211. @Override
  212. public void scrollToEnd() {
  213. Scheduler.get().scheduleFinally(() -> grid.scrollToEnd());
  214. addDetailsRefreshCallback(() -> {
  215. if (rowHasDetails(grid.getDataSource().size() - 1)) {
  216. grid.scrollToEnd();
  217. }
  218. });
  219. }
  220. @Override
  221. public void recalculateColumnWidths() {
  222. grid.recalculateColumnWidths();
  223. }
  224. });
  225. grid.addSortHandler(this::handleSortEvent);
  226. grid.setRowStyleGenerator(rowRef -> {
  227. JsonObject json = rowRef.getRow();
  228. return json.hasKey(GridState.JSONKEY_ROWSTYLE)
  229. ? json.getString(GridState.JSONKEY_ROWSTYLE)
  230. : null;
  231. });
  232. grid.setCellStyleGenerator(cellRef -> {
  233. JsonObject row = cellRef.getRow();
  234. if (!row.hasKey(GridState.JSONKEY_CELLSTYLES)) {
  235. return null;
  236. }
  237. Column<?, JsonObject> column = cellRef.getColumn();
  238. if (column instanceof CustomColumn) {
  239. String id = ((CustomColumn) column).getConnectorId();
  240. JsonObject cellStyles = row
  241. .getObject(GridState.JSONKEY_CELLSTYLES);
  242. if (cellStyles.hasKey(id)) {
  243. return cellStyles.getString(id);
  244. }
  245. }
  246. return null;
  247. });
  248. grid.addColumnVisibilityChangeHandler(event -> {
  249. if (event.isUserOriginated()) {
  250. getRpcProxy(GridServerRpc.class).columnVisibilityChanged(
  251. getColumnId(event.getColumn()), event.isHidden());
  252. }
  253. });
  254. grid.addColumnReorderHandler(event -> {
  255. if (event.isUserOriginated()) {
  256. List<String> newColumnOrder = mapColumnsToIds(
  257. event.getNewColumnOrder());
  258. List<String> oldColumnOrder = mapColumnsToIds(
  259. event.getOldColumnOrder());
  260. getRpcProxy(GridServerRpc.class)
  261. .columnsReordered(newColumnOrder, oldColumnOrder);
  262. }
  263. });
  264. grid.addColumnResizeHandler(event -> {
  265. Column<?, JsonObject> column = event.getColumn();
  266. getRpcProxy(GridServerRpc.class).columnResized(getColumnId(column),
  267. column.getWidthActual());
  268. });
  269. // Handling row height changes
  270. grid.addRowHeightChangedHandler(event -> {
  271. getLayoutManager().setNeedsMeasureRecursively(GridConnector.this);
  272. getLayoutManager().layoutNow();
  273. });
  274. /* Item click events */
  275. grid.addBodyClickHandler(itemClickHandler);
  276. grid.addBodyDoubleClickHandler(itemClickHandler);
  277. layout();
  278. }
  279. @Override
  280. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  281. super.onStateChanged(stateChangeEvent);
  282. if (!getState().columnOrder.containsAll(idToColumn.keySet())) {
  283. updateColumns();
  284. } else if (stateChangeEvent.hasPropertyChanged("columnOrder")) {
  285. updateColumnOrder();
  286. }
  287. if (stateChangeEvent.hasPropertyChanged("header")) {
  288. updateHeader();
  289. }
  290. if (stateChangeEvent.hasPropertyChanged("footer")) {
  291. updateFooter();
  292. }
  293. }
  294. void updateColumnOrder() {
  295. getWidget().setColumnOrder(getState().columnOrder.stream()
  296. .map(this::getColumn).toArray(size -> new CustomColumn[size]));
  297. }
  298. @OnStateChange("columnResizeMode")
  299. void updateColumnResizeMode() {
  300. getWidget().setColumnResizeMode(getState().columnResizeMode);
  301. }
  302. /**
  303. * Updates the grid header section on state change.
  304. */
  305. void updateHeader() {
  306. final Grid<JsonObject> grid = getWidget();
  307. final SectionState state = getState().header;
  308. while (grid.getHeaderRowCount() > 0) {
  309. grid.removeHeaderRow(0);
  310. }
  311. for (RowState rowState : state.rows) {
  312. HeaderRow row = grid.appendHeaderRow();
  313. if (rowState.defaultHeader) {
  314. grid.setDefaultHeaderRow(row);
  315. }
  316. updateStaticRow(rowState, row);
  317. }
  318. grid.setHeaderVisible(state.visible);
  319. }
  320. @OnStateChange({ "bodyRowHeight", "headerRowHeight", "footerRowHeight" })
  321. void updateRowHeight() {
  322. if (rowHeightScheduled) {
  323. return;
  324. }
  325. Scheduler.get().scheduleFinally(() -> {
  326. GridState state = getState();
  327. Grid<JsonObject> grid = getWidget();
  328. if (grid.isAttached() && rowHeightNeedsReset()) {
  329. grid.resetSizesFromDom();
  330. }
  331. updateContainerRowHeigth(grid.getEscalator().getBody(),
  332. state.bodyRowHeight);
  333. updateContainerRowHeigth(grid.getEscalator().getHeader(),
  334. state.headerRowHeight);
  335. updateContainerRowHeigth(grid.getEscalator().getFooter(),
  336. state.footerRowHeight);
  337. rowHeightScheduled = false;
  338. });
  339. rowHeightScheduled = true;
  340. }
  341. private boolean rowHeightNeedsReset() {
  342. GridState state = getState();
  343. // Body
  344. boolean bodyAutoCalc = state.bodyRowHeight < 0;
  345. // Header
  346. boolean headerAutoCalc = state.headerRowHeight < 0;
  347. boolean headerReset = headerAutoCalc && hasVisibleContent(state.header);
  348. // Footer
  349. boolean footerAutoCalc = state.footerRowHeight < 0;
  350. boolean footerReset = footerAutoCalc && hasVisibleContent(state.footer);
  351. return bodyAutoCalc || headerReset || footerReset;
  352. }
  353. private boolean hasVisibleContent(SectionState state) {
  354. return state.visible && !state.rows.isEmpty();
  355. }
  356. private void updateContainerRowHeigth(RowContainer container,
  357. double height) {
  358. if (height >= 0) {
  359. container.setDefaultRowHeight(height);
  360. }
  361. }
  362. private void updateStaticRow(RowState rowState,
  363. Grid.StaticSection.StaticRow row) {
  364. rowState.cells
  365. .forEach((columnId, cellState) -> updateStaticCellFromState(
  366. row.getCell(getColumn(columnId)), cellState));
  367. for (Map.Entry<CellState, Set<String>> cellGroupEntry : rowState.cellGroups
  368. .entrySet()) {
  369. Set<String> group = cellGroupEntry.getValue();
  370. Grid.Column<?, ?>[] columns = group.stream().map(idToColumn::get)
  371. .toArray(size -> new Grid.Column<?, ?>[size]);
  372. // Set state to be the same as first in group.
  373. updateStaticCellFromState(row.join(columns),
  374. cellGroupEntry.getKey());
  375. }
  376. row.setStyleName(rowState.styleName);
  377. }
  378. private void updateStaticCellFromState(Grid.StaticSection.StaticCell cell,
  379. CellState cellState) {
  380. switch (cellState.type) {
  381. case TEXT:
  382. cell.setText(cellState.text);
  383. break;
  384. case HTML:
  385. cell.setHtml(cellState.html);
  386. break;
  387. case WIDGET:
  388. ComponentConnector connector = (ComponentConnector) cellState.connector;
  389. if (connector != null) {
  390. cell.setWidget(connector.getWidget());
  391. } else {
  392. // This happens if you do setVisible(false) on the component on
  393. // the server side
  394. cell.setWidget(null);
  395. }
  396. break;
  397. default:
  398. throw new IllegalStateException(
  399. "unexpected cell type: " + cellState.type);
  400. }
  401. cell.setStyleName(cellState.styleName);
  402. }
  403. /**
  404. * Updates the grid footer section on state change.
  405. */
  406. void updateFooter() {
  407. final Grid<JsonObject> grid = getWidget();
  408. final SectionState state = getState().footer;
  409. while (grid.getFooterRowCount() > 0) {
  410. grid.removeFooterRow(0);
  411. }
  412. for (RowState rowState : state.rows) {
  413. FooterRow row = grid.appendFooterRow();
  414. updateStaticRow(rowState, row);
  415. }
  416. grid.setFooterVisible(state.visible);
  417. }
  418. @OnStateChange({ "sortColumns", "sortDirs" })
  419. void updateSortOrder() {
  420. List<SortOrder> sortOrder = new ArrayList<SortOrder>();
  421. String[] sortColumns = getState().sortColumns;
  422. SortDirection[] sortDirs = getState().sortDirs;
  423. for (int i = 0; i < sortColumns.length; i++) {
  424. sortOrder
  425. .add(new SortOrder(getColumn(sortColumns[i]), sortDirs[i]));
  426. }
  427. getWidget().setSortOrder(sortOrder);
  428. }
  429. @Override
  430. public void setDataSource(DataSource<JsonObject> dataSource) {
  431. super.setDataSource(dataSource);
  432. getWidget().setDataSource(dataSource);
  433. }
  434. /**
  435. * Adds a column to the Grid widget. For each column a communication id
  436. * stored for client to server communication.
  437. *
  438. * @param column
  439. * column to add
  440. * @param id
  441. * communication id
  442. */
  443. public void addColumn(CustomColumn column, String id) {
  444. assert !columnToIdMap.containsKey(column) && !columnToIdMap
  445. .containsValue(id) : "Column with given id already exists.";
  446. columnToIdMap.put(column, id);
  447. idToColumn.put(id, column);
  448. if (idToColumn.keySet().containsAll(getState().columnOrder)) {
  449. // All columns are available.
  450. updateColumns();
  451. }
  452. }
  453. /**
  454. * Updates the widgets columns to match the map in this connector.
  455. */
  456. protected void updateColumns() {
  457. List<Column<?, JsonObject>> currentColumns = getWidget().getColumns();
  458. List<CustomColumn> columnOrder = getState().columnOrder.stream()
  459. .map(this::getColumn).collect(Collectors.toList());
  460. if (isColumnOrderCorrect(currentColumns, columnOrder)) {
  461. // All up to date
  462. return;
  463. }
  464. Grid<JsonObject> grid = getWidget();
  465. // Remove old column
  466. currentColumns.stream()
  467. .filter(col -> !(columnOrder.contains(col)
  468. || col instanceof SelectionColumn))
  469. .forEach(grid::removeColumn);
  470. // Add new columns
  471. grid.addColumns(columnOrder.stream()
  472. .filter(col -> !currentColumns.contains(col))
  473. .toArray(CustomColumn[]::new));
  474. // Make sure order is correct.
  475. grid.setColumnOrder(
  476. columnOrder.toArray(new CustomColumn[columnOrder.size()]));
  477. }
  478. private boolean isColumnOrderCorrect(List<Column<?, JsonObject>> current,
  479. List<CustomColumn> order) {
  480. List<Column<?, JsonObject>> columnsToCompare = current;
  481. if (current.size() > 0 && current.get(0) instanceof SelectionColumn) {
  482. // Remove selection column.
  483. columnsToCompare = current.subList(1, current.size());
  484. }
  485. return columnsToCompare.equals(order);
  486. }
  487. /**
  488. * Removes the given column from mappings in this Connector.
  489. *
  490. * @param column
  491. * column to remove from the mapping
  492. */
  493. public void removeColumnMapping(CustomColumn column) {
  494. assert columnToIdMap
  495. .containsKey(column) : "Given Column does not exist.";
  496. // Remove mapping. Columns are removed from Grid when state changes.
  497. String id = columnToIdMap.remove(column);
  498. idToColumn.remove(id);
  499. }
  500. /**
  501. * Method called by {@code CustomColumn} when its renderer changes. This
  502. * method is used to maintain hierarchical renderer wrap in
  503. * {@code TreeGrid}.
  504. *
  505. * @param column
  506. * the column which now has a new renderer
  507. *
  508. * @since 8.1
  509. */
  510. public void onColumnRendererChanged(CustomColumn column) {
  511. // NO-OP
  512. }
  513. @Override
  514. public void onUnregister() {
  515. super.onUnregister();
  516. }
  517. @Override
  518. public boolean isWorkPending() {
  519. return getWidget().isWorkPending();
  520. }
  521. @Override
  522. public void layout() {
  523. getWidget().onResize();
  524. }
  525. /**
  526. * Sends sort information from an event to the server-side of the Grid.
  527. *
  528. * @param event
  529. * the sort event
  530. */
  531. private void handleSortEvent(SortEvent<JsonObject> event) {
  532. List<String> columnIds = new ArrayList<>();
  533. List<SortDirection> sortDirections = new ArrayList<>();
  534. for (SortOrder so : event.getOrder()) {
  535. if (columnToIdMap.containsKey(so.getColumn())) {
  536. columnIds.add(columnToIdMap.get(so.getColumn()));
  537. sortDirections.add(so.getDirection());
  538. }
  539. }
  540. String[] colArray = columnIds.toArray(new String[0]);
  541. SortDirection[] dirArray = sortDirections.toArray(new SortDirection[0]);
  542. if (!Arrays.equals(colArray, getState().sortColumns)
  543. || !Arrays.equals(dirArray, getState().sortDirs)) {
  544. // State has actually changed, send to server
  545. getRpcProxy(GridServerRpc.class).sort(colArray, dirArray,
  546. event.isUserOriginated());
  547. }
  548. }
  549. /* HasComponentsConnector */
  550. @Override
  551. public void updateCaption(ComponentConnector connector) {
  552. // Details components don't support captions.
  553. }
  554. @Override
  555. public List<ComponentConnector> getChildComponents() {
  556. if (childComponents == null) {
  557. return Collections.emptyList();
  558. }
  559. return childComponents;
  560. }
  561. @Override
  562. public void setChildComponents(List<ComponentConnector> children) {
  563. childComponents = children;
  564. }
  565. @Override
  566. public HandlerRegistration addConnectorHierarchyChangeHandler(
  567. ConnectorHierarchyChangeHandler handler) {
  568. return ensureHandlerManager()
  569. .addHandler(ConnectorHierarchyChangeEvent.TYPE, handler);
  570. }
  571. @Override
  572. public GridState getState() {
  573. return (GridState) super.getState();
  574. }
  575. @Override
  576. public boolean hasTooltip() {
  577. // Always check for generated descriptions.
  578. return true;
  579. }
  580. @Override
  581. public TooltipInfo getTooltipInfo(Element element) {
  582. CellReference<JsonObject> cell = getWidget().getCellReference(element);
  583. if (cell != null) {
  584. JsonObject row = cell.getRow();
  585. if (row != null && (row.hasKey(GridState.JSONKEY_ROWDESCRIPTION)
  586. || row.hasKey(GridState.JSONKEY_CELLDESCRIPTION))) {
  587. Column<?, JsonObject> column = cell.getColumn();
  588. if (column instanceof CustomColumn) {
  589. JsonObject cellDescriptions = row
  590. .getObject(GridState.JSONKEY_CELLDESCRIPTION);
  591. String id = ((CustomColumn) column).getConnectorId();
  592. if (cellDescriptions != null
  593. && cellDescriptions.hasKey(id)) {
  594. return new TooltipInfo(cellDescriptions.getString(id),
  595. ((CustomColumn) column)
  596. .getTooltipContentMode());
  597. } else if (row.hasKey(GridState.JSONKEY_ROWDESCRIPTION)) {
  598. return new TooltipInfo(
  599. row.getString(GridState.JSONKEY_ROWDESCRIPTION),
  600. getState().rowDescriptionContentMode);
  601. }
  602. }
  603. }
  604. }
  605. if (super.hasTooltip()) {
  606. return super.getTooltipInfo(element);
  607. }
  608. return null;
  609. }
  610. @Override
  611. protected void sendContextClickEvent(MouseEventDetails details,
  612. EventTarget eventTarget) {
  613. // if element is the resize indicator, ignore the event
  614. if (isResizeHandle(eventTarget)) {
  615. WidgetUtil.clearTextSelection();
  616. return;
  617. }
  618. EventCellReference<JsonObject> eventCell = getWidget().getEventCell();
  619. Section section = eventCell.getSection();
  620. String rowKey = null;
  621. if (eventCell.isBody() && eventCell.getRow() != null) {
  622. rowKey = getRowKey(eventCell.getRow());
  623. }
  624. String columnId = getColumnId(eventCell.getColumn());
  625. getRpcProxy(GridServerRpc.class).contextClick(eventCell.getRowIndex(),
  626. rowKey, columnId, section, details);
  627. WidgetUtil.clearTextSelection();
  628. }
  629. private boolean isResizeHandle(EventTarget eventTarget) {
  630. if (Element.is(eventTarget)) {
  631. Element e = Element.as(eventTarget);
  632. if (e.getClassName().contains("-column-resize-handle")) {
  633. return true;
  634. }
  635. }
  636. return false;
  637. }
  638. private List<String> mapColumnsToIds(List<Column<?, JsonObject>> columns) {
  639. return columns.stream().map(this::getColumnId).filter(Objects::nonNull)
  640. .collect(Collectors.toList());
  641. }
  642. }