View Javadoc
1   /**
2    * This file Copyright (c) 2012-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.vaadin.gwt.client.grid;
35  
36  import com.google.gwt.dom.client.Document;
37  import com.google.gwt.dom.client.NativeEvent;
38  import com.google.gwt.dom.client.SpanElement;
39  import com.google.gwt.dom.client.TableCellElement;
40  import com.google.gwt.event.logical.shared.ValueChangeEvent;
41  import com.google.gwt.event.logical.shared.ValueChangeHandler;
42  import com.google.gwt.user.client.DOM;
43  import com.google.gwt.user.client.Element;
44  import com.google.gwt.user.client.ui.CheckBox;
45  import com.google.gwt.user.client.ui.HTML;
46  import com.google.gwt.user.client.ui.Widget;
47  import com.googlecode.mgwt.dom.client.recognizer.tap.MultiTapEvent;
48  import com.googlecode.mgwt.dom.client.recognizer.tap.MultiTapHandler;
49  import com.googlecode.mgwt.dom.client.recognizer.tap.MultiTapRecognizer;
50  import com.googlecode.mgwt.ui.client.widget.touch.TouchDelegate;
51  import com.vaadin.client.BrowserInfo;
52  import com.vaadin.client.UIDL;
53  import com.vaadin.client.Util;
54  import com.vaadin.v7.client.ui.VScrollTablePatched;
55  
56  /**
57   * Magnolia table extends VScrollTable in a way that out-of-the-box version of it would not allow.
58   * Therefore maven build will patch the VScrollTable to reveal it's private members.
59   */
60  public class VMagnoliaTable extends VScrollTablePatched {
61  
62      static int checkboxWidth = -1;
63  
64      public VMagnoliaTable() {
65          super();
66          MagnoliaTableHead head = (MagnoliaTableHead) tHead;
67          head.addToDom();
68      }
69  
70      @Override
71      protected TableHead createTableHead() {
72          return new MagnoliaTableHead();
73      }
74  
75      @Override
76      protected VScrollTableBody createScrollBody() {
77          return new MagnoliaTableBody();
78      }
79  
80      @Override
81      protected HeaderCell createHeaderCell(String colId, String headerText) {
82          return new MagnoliaHeaderCell(colId, headerText);
83      }
84  
85      @Override
86      protected void setMultiSelectMode(int multiselectmode) {
87          this.multiselectmode = multiselectmode;
88      }
89  
90      /**
91       * Extend header cell to contain caption text.
92       */
93      public class MagnoliaHeaderCell extends HeaderCell {
94  
95          private Element caption = null;
96          private boolean canBeSorted = false;
97  
98          public MagnoliaHeaderCell(String colId, String headerText) {
99              super(colId, headerText);
100             caption = DOM.createSpan();
101             captionContainer.appendChild(caption);
102             setText(headerText);
103         }
104 
105         @Override
106         public void setText(String headerText) {
107             if (caption != null) {
108                 caption.setInnerHTML(headerText);
109             }
110         }
111 
112         @Override
113         protected void updateStyleNames(String primaryStyleName) {
114             super.updateStyleNames(primaryStyleName);
115             if (this.canBeSorted) {
116                 addStyleName("sortable");
117             }
118         }
119 
120         @Override
121         public void setSortable(boolean sortable) {
122             super.setSortable(sortable);
123             // super class doesn't expose an accessor method
124             // for sortable, so we resort to this field variable.
125             this.canBeSorted = sortable;
126         }
127     }
128 
129     /**
130      * Extend TableHead to contain select all checkbox.
131      */
132     public class MagnoliaTableHead extends TableHead {
133 
134         private CheckBox selectAll = null;
135 
136         public MagnoliaTableHead() {
137             super();
138             selectAll = new CheckBox();
139             selectAll.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
140 
141                 @Override
142                 public void onValueChange(ValueChangeEvent<Boolean> event) {
143                     client.updateVariable(paintableId, "selectAll", event.getValue(), true);
144                 }
145             });
146             selectAll.addStyleName("v-select-all");
147         }
148 
149         public void addToDom() {
150             div.appendChild(selectAll.getElement());
151             getChildren().add(selectAll);
152             adopt(selectAll);
153         }
154 
155         public CheckBox getSelectAllCB() {
156             return selectAll;
157         }
158     }
159 
160     /**
161      * Extension of VScrollTableBody.
162      */
163     public class MagnoliaTableBody extends VScrollTableBody {
164 
165         @Override
166         protected VScrollTableRow createScrollTableRow(UIDL uidl, char[] aligns) {
167             return new MagnoliaTableRow(uidl, aligns);
168         }
169 
170         @Override
171         protected VScrollTableRow createScrollTableRow() {
172             return new MagnoliaTableRow();
173         }
174 
175         @Override
176         public int getColWidth(int columnIndex) {
177             return super.getColWidth(columnIndex);
178         }
179 
180         /**
181          * Extend VScrollTableRow to contain selection checkbox.
182          */
183         public class MagnoliaTableRow extends VScrollTableRow {
184 
185             private CheckBox selectionCheckBox;
186 
187             private HTML selectionCheckBoxSpacer;
188 
189             private String nodeIcon;
190 
191             public MagnoliaTableRow(UIDL uidl, char[] aligns) {
192                 super(uidl, aligns);
193             }
194 
195             public MagnoliaTableRow() {
196                 super();
197             }
198 
199             @Override
200             protected void updateStyleNames(String primaryStyleName) {
201                 // Minor hack. Use row style for icon definition.
202                 if (rowStyle != null) {
203                     String[] rowStyles = rowStyle.split(" ");
204                     for (String style : rowStyles) {
205                         if (style.startsWith("icon")) {
206                             if (nodeIcon == null) {
207                                 nodeIcon = style;
208                             } else {
209                                 nodeIcon += " " + style;
210                             }
211                         }
212                     }
213                 }
214                 super.updateStyleNames(primaryStyleName);
215             }
216 
217             /**
218              * Minor hack. Construction has to happen during base class construction and this method
219              * is called by the base class constructor.
220              */
221             @Override
222             protected void setElement(com.google.gwt.user.client.Element elem) {
223                 super.setElement(elem);
224                 privateConstruction();
225             }
226 
227             @Override
228             protected void initCellWithText(String text, char align, String style, boolean textIsHTML, boolean sorted, String description, final TableCellElement td) {
229                 super.initCellWithText(text, align, style, textIsHTML, sorted, description, td);
230                 if (td.equals(this.getElement().getFirstChildElement())) {
231                     insertNodeIcon(td);
232                     if (isSingleSelectMode()) {
233                         insertSelectionCheckboxSpacer(td);
234                     } else {
235                         insertSelectionCheckbox(td);
236                     }
237                 }
238             }
239 
240             @Override
241             protected void initCellWithWidget(Widget w, char align, String style, boolean sorted, TableCellElement td) {
242                 super.initCellWithWidget(w, align, style, sorted, td);
243                 if (td.equals(this.getElement().getFirstChildElement())) {
244                     insertNodeIcon(td);
245                     if (isSingleSelectMode()) {
246                         insertSelectionCheckboxSpacer(td);
247                     } else {
248                         insertSelectionCheckbox(td);
249                     }
250                 }
251             }
252 
253             /**
254              * Inserts the selection checkbox in first column.
255              */
256             private void insertSelectionCheckbox(final TableCellElement td) {
257                 com.google.gwt.dom.client.Element container = td.getFirstChildElement();
258                 container.insertFirst(selectionCheckBox.getElement());
259             }
260 
261             /**
262              * Inserts a spacer element in the first column to ensure that positioning of content is the same when there is or is not a checkbox.
263              */
264             private void insertSelectionCheckboxSpacer(final TableCellElement td) {
265                 com.google.gwt.dom.client.Element container = td.getFirstChildElement();
266                 container.insertFirst(selectionCheckBoxSpacer.getElement());
267             }
268 
269             protected void insertNodeIcon(TableCellElement td) {
270                 if (nodeIcon != null) {
271                     SpanElement iconElement = Document.get().createSpanElement();
272                     iconElement.setClassName(nodeIcon);
273                     iconElement.addClassName("v-table-icon-element");
274                     td.getFirstChild().insertFirst(iconElement);
275                 }
276             }
277 
278             private void privateConstruction() {
279                 selectionCheckBox = new CheckBox();
280                 selectionCheckBox.setValue(isSelected(), false);
281                 ValueChangeHandler<Boolean> selectionChangeHandler = new ValueChangeHandler<Boolean>() {
282 
283                     @Override
284                     public void onValueChange(ValueChangeEvent<Boolean> event) {
285                         if (event.getSource() instanceof Widget) {
286                             final Widget source = (Widget) event.getSource();
287                             final Element targetTd = source.getElement().getParentElement().cast();
288                             VScrollTableRow row = Util.findWidget(targetTd, null);
289                             if (row != null) {
290                                 boolean wasSelected = row.isSelected();
291 
292                                 if (VMagnoliaTable.this.isSingleSelectMode() && !row.isSelected()) {
293                                     deselectAll();
294                                 }
295 
296                                 row.toggleSelection();
297                                 setRowFocus(row);
298                                 /*
299                                  * next possible range select must start on this row
300                                  */
301                                 selectionRangeStart = row;
302                                 if (wasSelected) {
303                                     removeRowFromUnsentSelectionRanges(row);
304                                 }
305 
306                                 sendSelectedRows(true);
307                             }
308                         }
309                     }
310                 };
311 
312                 selectionCheckBox.addValueChangeHandler(selectionChangeHandler);
313                 selectionCheckBox.addStyleName("v-selection-cb");
314                 getChildren().add(selectionCheckBox);
315                 VMagnoliaTable.this.adopt(selectionCheckBox);
316 
317                 selectionCheckBoxSpacer = new HTML();
318                 selectionCheckBoxSpacer.addStyleName("v-selection-cb");
319                 getChildren().add(selectionCheckBoxSpacer);
320                 VMagnoliaTable.this.adopt(selectionCheckBoxSpacer);
321 
322                 final TouchDelegatet/widget/touch/TouchDelegate.html#TouchDelegate">TouchDelegate delegate = new TouchDelegate(this);
323                 delegate.addTouchHandler(new MultiTapRecognizer(delegate, 1, 2));
324                 addHandler(new MultiTapHandler() {
325 
326                     @Override
327                     public void onMultiTap(MultiTapEvent event) {
328                         if (BrowserInfo.get().isTouchDevice()) {
329                             final NativeEvent doubleClickEvent =
330                                     Document.get().createDblClickEvent(
331                                             0,
332                                             event.getTouchStarts().get(0).get(0).getPageX(),
333                                             event.getTouchStarts().get(0).get(0).getPageY(),
334                                             event.getTouchStarts().get(0).get(0).getPageX(),
335                                             event.getTouchStarts().get(0).get(0).getPageY(),
336                                             false,
337                                             false,
338                                             false,
339                                             false);
340                             getElement().dispatchEvent(doubleClickEvent);
341                         }
342                     }
343                 }, MultiTapEvent.getType());
344             }
345 
346             @Override
347             public void toggleSelection() {
348                 super.toggleSelection();
349 
350                 if (selectionCheckBox != null) {
351                     selectionCheckBox.setValue(isSelected(), false);
352                 }
353                 MagnoliaTableHead head = (MagnoliaTableHead) tHead;
354                 head.getSelectAllCB().setValue(selectedRowKeys.size() == scrollBody.renderedRows.size(), false);
355             }
356 
357             @Override
358             protected boolean isRenderHtmlInCells() {
359                 return true;
360             }
361         }
362 
363     }
364 }