View Javadoc

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