001/*
002 * #%L
003 * GwtMaterial
004 * %%
005 * Copyright (C) 2015 - 2017 GwtMaterialDesign
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 * 
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 * 
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package gwt.material.design.client.ui.table;
021
022import com.google.gwt.cell.client.Cell;
023import com.google.gwt.cell.client.Cell.Context;
024import com.google.gwt.cell.client.FieldUpdater;
025import com.google.gwt.cell.client.HasCell;
026import com.google.gwt.cell.client.ValueUpdater;
027import com.google.gwt.dom.client.BrowserEvents;
028import com.google.gwt.dom.client.Element;
029import com.google.gwt.dom.client.EventTarget;
030import com.google.gwt.dom.client.TableCellElement;
031import com.google.gwt.dom.client.TableRowElement;
032import com.google.gwt.event.shared.HandlerRegistration;
033import com.google.gwt.user.client.DOM;
034import com.google.gwt.user.client.Event;
035import com.google.gwt.view.client.Range;
036import gwt.material.design.client.base.constants.TableCssName;
037import gwt.material.design.client.data.DataSource;
038import gwt.material.design.client.data.Renderer;
039import gwt.material.design.client.data.SelectionType;
040import gwt.material.design.client.data.SortDir;
041import gwt.material.design.client.data.component.CategoryComponent;
042import gwt.material.design.client.data.component.ComponentFactory;
043import gwt.material.design.client.data.component.RowComponent;
044import gwt.material.design.client.data.events.CategoryClosedEvent;
045import gwt.material.design.client.data.events.CategoryClosedHandler;
046import gwt.material.design.client.data.events.CategoryOpenedEvent;
047import gwt.material.design.client.data.events.CategoryOpenedHandler;
048import gwt.material.design.client.data.events.ColumnSortEvent;
049import gwt.material.design.client.data.events.ColumnSortHandler;
050import gwt.material.design.client.data.events.ComponentsRenderedEvent;
051import gwt.material.design.client.data.events.ComponentsRenderedHandler;
052import gwt.material.design.client.data.events.DestroyEvent;
053import gwt.material.design.client.data.events.DestroyHandler;
054import gwt.material.design.client.data.events.InsertColumnEvent;
055import gwt.material.design.client.data.events.InsertColumnHandler;
056import gwt.material.design.client.data.events.RemoveColumnEvent;
057import gwt.material.design.client.data.events.RemoveColumnHandler;
058import gwt.material.design.client.data.events.RenderedEvent;
059import gwt.material.design.client.data.events.RenderedHandler;
060import gwt.material.design.client.data.events.RowCollapsedEvent;
061import gwt.material.design.client.data.events.RowCollapsedHandler;
062import gwt.material.design.client.data.events.RowCollapsingEvent;
063import gwt.material.design.client.data.events.RowCollapsingHandler;
064import gwt.material.design.client.data.events.RowContextMenuEvent;
065import gwt.material.design.client.data.events.RowContextMenuHandler;
066import gwt.material.design.client.data.events.RowDoubleClickEvent;
067import gwt.material.design.client.data.events.RowDoubleClickHandler;
068import gwt.material.design.client.data.events.RowExpandedEvent;
069import gwt.material.design.client.data.events.RowExpandedHandler;
070import gwt.material.design.client.data.events.RowExpandingEvent;
071import gwt.material.design.client.data.events.RowExpandingHandler;
072import gwt.material.design.client.data.events.RowLongPressEvent;
073import gwt.material.design.client.data.events.RowLongPressHandler;
074import gwt.material.design.client.data.events.RowSelectEvent;
075import gwt.material.design.client.data.events.RowSelectHandler;
076import gwt.material.design.client.data.events.RowShortPressEvent;
077import gwt.material.design.client.data.events.RowShortPressHandler;
078import gwt.material.design.client.data.events.SelectAllEvent;
079import gwt.material.design.client.data.events.SelectAllHandler;
080import gwt.material.design.client.data.events.SetupEvent;
081import gwt.material.design.client.data.events.SetupHandler;
082import gwt.material.design.client.data.factory.RowComponentFactory;
083import gwt.material.design.client.events.DefaultHandlerRegistry;
084import gwt.material.design.client.events.HandlerRegistry;
085import gwt.material.design.client.base.MaterialWidget;
086import gwt.material.design.client.data.DataView;
087import gwt.material.design.client.data.StandardDataView;
088import gwt.material.design.client.ui.table.cell.Column;
089
090import java.util.List;
091import java.util.Set;
092import java.util.logging.Level;
093import java.util.logging.Logger;
094
095/**
096 * An abstract data table implementation that can be attached to the DOM.
097 * The core functionality is based on the registered {@link DataView},
098 * this will handle scaffolding, event registration, and attachment.
099 *
100 * @author Ben Dol
101 */
102public abstract class AbstractDataTable<T> extends MaterialWidget implements DataDisplay<T> {
103
104    private static final Logger logger = Logger.getLogger(AbstractDataTable.class.getName());
105
106    public static class DefaultTableScaffolding extends TableScaffolding {
107        @Override
108        public MaterialWidget createTableBody() {
109            MaterialWidget tableBody = new MaterialWidget(DOM.createDiv());
110            tableBody.addStyleName(TableCssName.TABLE_BODY);
111            return tableBody;
112        }
113
114        @Override
115        public MaterialWidget createTopPanel() {
116            MaterialWidget topPanel = new MaterialWidget(DOM.createDiv());
117            topPanel.addStyleName(TableCssName.TOP_PANEL);
118            return topPanel;
119        }
120
121        @Override
122        public MaterialWidget createInfoPanel() {
123            MaterialWidget infoPanel = new MaterialWidget(DOM.createDiv());
124            infoPanel.addStyleName(TableCssName.INFO_PANEL);
125            return infoPanel;
126        }
127
128        @Override
129        public MaterialWidget createToolPanel() {
130            MaterialWidget toolPanel = new MaterialWidget(DOM.createDiv());
131            toolPanel.addStyleName(TableCssName.TOOL_PANEL);
132            return toolPanel;
133        }
134
135        @Override
136        protected Table createTable() {
137            Table table = new Table();
138            table.addStyleName(TableCssName.TABLE);
139            return table;
140        }
141    }
142
143    protected DataView<T> view;
144    protected TableScaffolding scaffolding;
145    protected HandlerRegistry handlerRegistry;
146
147    private boolean focused;
148    private boolean refreshing;
149    private boolean cellIsEditing;
150    private boolean destroyOnUnload;
151
152    private HandlerRegistration setupHandler;
153
154    public AbstractDataTable() {
155        this(new StandardDataView<>());
156    }
157
158    public AbstractDataTable(DataView<T> view) {
159        this(view, new DefaultTableScaffolding());
160    }
161
162    public AbstractDataTable(TableScaffolding scaffolding) {
163        this();
164        this.scaffolding = scaffolding;
165    }
166
167    public AbstractDataTable(DataView<T> view, TableScaffolding scaffolding) {
168        super(DOM.createElement("section"));
169        this.view = view;
170        this.scaffolding = scaffolding;
171
172        view.setDisplay(this);
173        setStyleName("table-container");
174
175        handlerRegistry = new DefaultHandlerRegistry(this);
176
177        // Build the table scaffolding
178        scaffolding.build();
179        scaffolding.apply(this);
180
181        // Apply the DOM build.
182        build();
183    }
184
185    @Override
186    protected void onLoad() {
187        super.onLoad();
188
189        if(!view.isSetup()) {
190            try {
191                view.setup(scaffolding);
192            } catch (Exception ex) {
193                logger.log(Level.SEVERE,
194                    "Could not setup AbstractDataTable due to previous errors.", ex);
195            }
196            // We should recalculate when we load again.
197        } else if(view.isUseCategories()) {
198            view.getSubheaderLib().recalculate(true);
199        }
200    }
201
202    @Override
203    protected void onUnload() {
204        super.onUnload();
205
206        clearHandlers();
207
208        if(destroyOnUnload) {
209            view.destroy();
210        }
211    }
212
213    protected void build() {
214        // Nothing by default.
215    }
216
217    @Override
218    public final DataView<T> getView() {
219        return view;
220    }
221
222    @Override
223    public final Range getVisibleRange() {
224        return view.getVisibleRange();
225    }
226
227    @Override
228    public final void setVisibleRange(int start, int length) {
229        view.setVisibleRange(start, length);
230    }
231
232    @Override
233    public final void setVisibleRange(Range range) {
234        view.setVisibleRange(range);
235    }
236
237    @Override
238    public final int getRowCount() {
239        return view.getRowCount();
240    }
241
242    @Override
243    public final void setRowData(int start, List<? extends T> values) {
244        view.setRowData(start, values);
245    }
246
247    @Override
248    public final void setCategoryFactory(ComponentFactory<? extends CategoryComponent, String> categoryFactory) {
249        view.setCategoryFactory(categoryFactory);
250    }
251
252    @Override
253    public final CategoryComponent getCategory(String categoryName) {
254        return view.getCategory(categoryName);
255    }
256
257    @Override
258    public final List<CategoryComponent> getCategories() {
259        return view.getCategories();
260    }
261
262    @Override
263    public final List<CategoryComponent> getOpenCategories() {
264        return view.getOpenCategories();
265    }
266
267    @Override
268    public final boolean isCategoryEmpty(CategoryComponent category) {
269        return view.isCategoryEmpty(category);
270    }
271
272    @Override
273    public final void addCategory(String category) {
274        view.addCategory(category);
275    }
276
277    @Override
278    public final void addCategory(CategoryComponent category) {
279        view.addCategory(category);
280    }
281
282    @Override
283    public final boolean hasCategory(String categoryName) {
284        return view.hasCategory(categoryName);
285    }
286
287    @Override
288    public final void disableCategory(String categoryName) {
289        view.disableCategory(categoryName);
290    }
291
292    @Override
293    public final void enableCategory(String categoryName) {
294        view.enableCategory(categoryName);
295    }
296
297    @Override
298    public final void openCategory(String categoryName) {
299        view.openCategory(categoryName);
300    }
301
302    @Override
303    public final void openCategory(CategoryComponent category) {
304        view.openCategory(category);
305    }
306
307    @Override
308    public final void closeCategory(String categoryName) {
309        view.closeCategory(categoryName);
310    }
311
312    @Override
313    public final void closeCategory(CategoryComponent category) {
314        view.closeCategory(category);
315    }
316
317    @Override
318    public final void clearRowsAndCategories(boolean clearData) {
319        view.clearRowsAndCategories(clearData);
320    }
321
322    @Override
323    public final void clearCategories() {
324        view.clearCategories();
325    }
326
327    @Override
328    public final void addColumn(Column<T, ?> column) {
329        view.addColumn(column);
330    }
331
332    @Override
333    public final void addColumn(Column<T, ?> column, String header) {
334        view.addColumn(column, header);
335    }
336
337    @Override
338    public final void insertColumn(int beforeIndex, Column<T, ?> col, String header) {
339        view.insertColumn(beforeIndex, col, header);
340    }
341
342    @Override
343    public final void removeColumn(int colIndex) {
344        view.removeColumn(colIndex);
345    }
346
347    @Override
348    public final void removeColumns() {
349        view.removeColumns();
350    }
351
352    @Override
353    public final List<Column<T, ?>> getColumns() {
354        return view.getColumns();
355    }
356
357    @Override
358    public final int getColumnOffset() {
359        return view.getColumnOffset();
360    }
361
362    @Override
363    public final void sort(int columnIndex) {
364        view.sort(columnIndex);
365    }
366
367    @Override
368    public final void sort(int columnIndex, SortDir dir) {
369        view.sort(columnIndex, dir);
370    }
371
372    @Override
373    public final void sort(Column<T, ?> column) {
374        view.sort(column);
375    }
376
377    @Override
378    public final void sort(Column<T, ?> column, SortDir dir) {
379        view.sort(column, dir);
380    }
381
382    @Override
383    public final void setDataSource(DataSource<T> dataSource) {
384        view.setDataSource(dataSource);
385    }
386
387    @Override
388    public final DataSource<T> getDataSource() {
389        return view.getDataSource();
390    }
391
392    @Override
393    public void loaded(int startIndex, List<T> data) {
394        view.loaded(startIndex, data);
395    }
396
397    @Override
398    public final void setRenderer(Renderer<T> renderer) {
399        view.setRenderer(renderer);
400    }
401
402    @Override
403    public final int getTotalRows() {
404        return view.getTotalRows();
405    }
406
407    @Override
408    public final void setTotalRows(int totalRows) {
409        view.setTotalRows(totalRows);
410    }
411
412    @Override
413    public final int getRowHeight() {
414        return view.getRowHeight();
415    }
416
417    @Override
418    public final void setRowHeight(int rowHeight) {
419        view.setRowHeight(rowHeight);
420    }
421
422    @Override
423    public final void updateRow(T model) {
424        view.updateRow(model);
425    }
426
427    @Override
428    public List<RowComponent<T>> getRows() {
429        return view.getRows();
430    }
431
432    @Override
433    public final RowComponent<T> getRow(T model) {
434        return view.getRow(model);
435    }
436
437    @Override
438    public final RowComponent<T> getRow(int index) {
439        return view.getRow(index);
440    }
441
442    @Override
443    public final RowComponent<T> getRowByModel(T model) {
444        return view.getRowByModel(model);
445    }
446
447    @Override
448    public final void clearRows(boolean clearData) {
449        view.clearRows(clearData);
450    }
451
452    @Override
453    public final void setRowFactory(RowComponentFactory<T> rowFactory) {
454        view.setRowFactory(rowFactory);
455    }
456
457    @Override
458    public RowComponentFactory<T> getRowFactory() {
459        return view.getRowFactory();
460    }
461
462    @Override
463    public final void setSelectionType(SelectionType selectionType) {
464        view.setSelectionType(selectionType);
465    }
466
467    @Override
468    public final SelectionType getSelectionType() {
469        return view.getSelectionType();
470    }
471
472    @Override
473    public final void selectAllRows(boolean select) {
474        view.selectAllRows(select);
475    }
476
477    @Override
478    public final void selectAllRows(boolean select, boolean fireEvent) {
479        view.selectAllRows(select, fireEvent);
480    }
481
482    @Override
483    public final void selectRow(Element row, boolean fireEvent) {
484        view.selectRow(row, fireEvent);
485    }
486
487    @Override
488    public final void deselectRow(Element row, boolean fireEvent) {
489        view.deselectRow(row, fireEvent);
490    }
491
492    @Override
493    public final boolean hasDeselectedRows(boolean visibleOnly) {
494        return view.hasDeselectedRows(visibleOnly);
495    }
496
497    @Override
498    public final boolean hasSelectedRows(boolean visibleOnly) {
499        return view.hasSelectedRows(visibleOnly);
500    }
501
502    @Override
503    public final List<T> getSelectedRowModels(boolean visibleOnly) {
504        return view.getSelectedRowModels(visibleOnly);
505    }
506
507    @Override
508    public final boolean isUseStickyHeader() {
509        return view.isUseStickyHeader();
510    }
511
512    @Override
513    public final void setUseStickyHeader(boolean stickyHeader) {
514        view.setUseStickyHeader(stickyHeader);
515    }
516
517    @Override
518    public final boolean isUseCategories() {
519        return view.isUseCategories();
520    }
521
522    @Override
523    public final void setUseCategories(boolean useCategories) {
524        view.setUseCategories(useCategories);
525    }
526
527    @Override
528    public final boolean isUseLoadOverlay() {
529        return view.isUseLoadOverlay();
530    }
531
532    @Override
533    public final void setUseLoadOverlay(boolean useLoadOverlay) {
534        view.setUseLoadOverlay(useLoadOverlay);
535    }
536
537    @Override
538    public final boolean isUseRowExpansion() {
539        return view.isUseRowExpansion();
540    }
541
542    @Override
543    public final void setUseRowExpansion(boolean useRowExpansion) {
544        view.setUseRowExpansion(useRowExpansion);
545    }
546
547    @Override
548    public final int getLongPressDuration() {
549        return view.getLongPressDuration();
550    }
551
552    @Override
553    public final void setLongPressDuration(int longPressDuration) {
554        view.setLongPressDuration(longPressDuration);
555    }
556
557    @Override
558    public final String getHeight() {
559        return view.getHeight();
560    }
561
562    @Override
563    public final void setHeight(String height) {
564        view.setHeight(height);
565    }
566
567    @Override
568    public void onBrowserEvent(Event event) {
569        CellBasedWidgetImpl.get().onBrowserEvent(this, event);
570
571        // Ignore spurious events (such as onblur) while we refresh the table.
572        if (refreshing) {
573            return;
574        }
575
576        // Verify that the target is still a child of this widget. IE fires focus
577        // events even after the element has been removed from the DOM.
578        EventTarget eventTarget = event.getEventTarget();
579        if (!Element.is(eventTarget)) {
580            return;
581        }
582        Element target = Element.as(eventTarget);
583        if (!getElement().isOrHasChild(Element.as(eventTarget))) {
584            return;
585        }
586        super.onBrowserEvent(event);
587
588        String eventType = event.getType();
589        if (BrowserEvents.FOCUS.equals(eventType)) {
590            // Remember the focus state.
591            focused = true;
592            onFocus();
593        } else if (BrowserEvents.BLUR.equals(eventType)) {
594            // Remember the blur state.
595            focused = false;
596            onBlur();
597        } else if (BrowserEvents.KEYDOWN.equals(eventType)) {
598            // A key event indicates that we already have focus.
599            focused = true;
600        } else if (BrowserEvents.MOUSEDOWN.equals(eventType)
601                && CellBasedWidgetImpl.get().isFocusable(Element.as(target))) {
602            // If a natively focusable element was just clicked, then we must have
603            // focus.
604            focused = true;
605        }
606    }
607
608    protected void onFocus() {
609        // Do nothing by default
610    }
611
612    protected void onBlur() {
613        // Do nothing by default
614    }
615
616    /**
617     * Get the index of the row value from the associated {@link TableRowElement}.
618     *
619     * @param row the row element
620     * @return the row value index
621     */
622    public final int getRowValueIndex(TableRowElement row) {
623        return row.getSectionRowIndex() + getView().getVisibleRange().getStart();
624    }
625
626    /**
627     * Fire an event to the Cell within the specified {@link TableCellElement}.
628     */
629    private <C> void fireEventToCell(Event event, String eventType, Element parentElem,
630                                     final T rowValue, Context context, HasCell<T, C> column) {
631        // Check if the cell consumes the event.
632        Cell<C> cell = column.getCell();
633        if (!cellConsumesEventType(cell, eventType)) {
634            return;
635        }
636
637        C cellValue = column.getValue(rowValue);
638        boolean cellWasEditing = cell.isEditing(context, parentElem, cellValue);
639        if (column instanceof Column) {
640          /*
641           * If the HasCell is a Column, let it handle the event itself. This is
642           * here for legacy support.
643           */
644            Column<T, C> col = (Column<T, C>) column;
645            col.onBrowserEvent(context, parentElem, rowValue, event);
646        } else {
647            // Create a FieldUpdater.
648            final FieldUpdater<T, C> fieldUpdater = column.getFieldUpdater();
649            final int index = context.getIndex();
650            ValueUpdater<C> valueUpdater = (fieldUpdater == null) ? null : (value) -> {
651                fieldUpdater.update(index, rowValue, value);
652            };
653
654            // Fire the event to the cell.
655            cell.onBrowserEvent(context, parentElem, cellValue, event, valueUpdater);
656        }
657
658        // Reset focus if needed.
659        cellIsEditing = cell.isEditing(context, parentElem, cellValue);
660        if (cellWasEditing && !cellIsEditing) {
661            CellBasedWidgetImpl.get().resetFocus(() -> {
662                setFocus(true);
663            });
664        }
665    }
666
667    /**
668     * Check if a cell consumes the specified event type.
669     *
670     * @param cell the cell
671     * @param eventType the event type to check
672     * @return true if consumed, false if not
673     */
674    protected boolean cellConsumesEventType(Cell<?> cell, String eventType) {
675        Set<String> consumedEvents = cell.getConsumedEvents();
676        return consumedEvents != null && consumedEvents.contains(eventType);
677    }
678
679    /**
680     * Get the tables scaffolding elements.
681     */
682    @Override
683    public TableScaffolding getScaffolding() {
684        return scaffolding;
685    }
686
687    /**
688     * @deprecated use {@link #addSetupHandler(SetupHandler)}
689     */
690    @Deprecated
691    public void setLoadedCallback(LoadedCallback callback) {
692        if(setupHandler != null) {
693            setupHandler.removeHandler();
694            setupHandler = null;
695        }
696        setupHandler = addSetupHandler(event -> {
697            callback.onLoaded();
698        });
699    }
700
701    /**
702     * Event fired when the tables view calls {@link DataView#setup(TableScaffolding)}.
703     * @return Handler registration to remove the event handler.
704     */
705    public HandlerRegistration addSetupHandler(SetupHandler handler) {
706        return addHandler(handler, SetupEvent.TYPE);
707    }
708
709    /**
710     * Event fired when the tables view calls {@link DataView#destroy()}.
711     * @return Handler registration to remove the event handler.
712     */
713    public HandlerRegistration addDestroyHandler(DestroyHandler handler) {
714        return addHandler(handler, DestroyEvent.TYPE);
715    }
716
717    /**
718     * Event fired when the tables view calls {@link DataView#insertColumn(int, Column, String)}.
719     * @return Handler registration to remove the event handler.
720     */
721    public HandlerRegistration addInsertColumnHandler(InsertColumnHandler<T> handler) {
722        return addHandler(handler, InsertColumnEvent.TYPE);
723    }
724
725    /**
726     * Event fired when the tables view calls {@link DataView#removeColumn(int)}.
727     * @return Handler registration to remove the event handler.
728     */
729    public HandlerRegistration addRemoveColumnHandler(RemoveColumnHandler handler) {
730        return addHandler(handler, RemoveColumnEvent.TYPE);
731    }
732
733    public boolean isDestroyOnUnload() {
734        return destroyOnUnload;
735    }
736
737    public void setDestroyOnUnload(boolean destroyOnUnload) {
738        this.destroyOnUnload = destroyOnUnload;
739    }
740
741    // Event Handlers
742
743    @Override
744    public HandlerRegistration registerHandler(HandlerRegistration registration) {
745        return handlerRegistry.registerHandler(registration);
746    }
747
748    @Override
749    public void clearHandlers() {
750        handlerRegistry.clearHandlers();
751    }
752
753    @Override
754    public HandlerRegistration addSelectAllHandler(SelectAllHandler<T> handler) {
755        return addHandler(handler, SelectAllEvent.TYPE);
756    }
757
758    @Override
759    public HandlerRegistration addRowSelectHandler(RowSelectHandler<T> handler) {
760        return addHandler(handler, RowSelectEvent.TYPE);
761    }
762
763    @Override
764    public HandlerRegistration addRowExpandingHandler(RowExpandingHandler<T> handler) {
765        return addHandler(handler, RowExpandingEvent.TYPE);
766    }
767
768    @Override
769    public HandlerRegistration addRowExpandedHandler(RowExpandedHandler<T> handler) {
770        return addHandler(handler, RowExpandedEvent.TYPE);
771    }
772
773    @Override
774    public HandlerRegistration addRowCollapseHandler(RowCollapsingHandler<T> handler) {
775        return addHandler(handler, RowCollapsingEvent.TYPE);
776    }
777
778    @Override
779    public HandlerRegistration addRowCollapsedHandler(RowCollapsedHandler<T> handler) {
780        return addHandler(handler, RowCollapsedEvent.TYPE);
781    }
782
783    @Override
784    public HandlerRegistration addRowContextMenuHandler(RowContextMenuHandler<T> handler) {
785        return addHandler(handler, RowContextMenuEvent.TYPE);
786    }
787
788    @Override
789    public HandlerRegistration addRowDoubleClickHandler(RowDoubleClickHandler<T> handler) {
790        return addHandler(handler, RowDoubleClickEvent.TYPE);
791    }
792
793    @Override
794    public HandlerRegistration addRowLongPressHandler(RowLongPressHandler<T> handler) {
795        return addHandler(handler, RowLongPressEvent.TYPE);
796    }
797
798    @Override
799    public HandlerRegistration addRowShortPressHandler(RowShortPressHandler<T> handler) {
800        return addHandler(handler, RowShortPressEvent.TYPE);
801    }
802
803    @Override
804    public HandlerRegistration addColumnSortHandler(ColumnSortHandler<T> handler) {
805        return addHandler(handler, ColumnSortEvent.TYPE);
806    }
807
808    @Override
809    public HandlerRegistration addCategoryOpenedHandler(CategoryOpenedHandler handler) {
810        return addHandler(handler, CategoryOpenedEvent.TYPE);
811    }
812
813    @Override
814    public HandlerRegistration addCategoryClosedHandler(CategoryClosedHandler handler) {
815        return addHandler(handler, CategoryClosedEvent.TYPE);
816    }
817
818    @Override
819    public HandlerRegistration addComponentsRenderedHandler(ComponentsRenderedHandler handler) {
820        return addHandler(handler, ComponentsRenderedEvent.TYPE);
821    }
822
823    @Override
824    public HandlerRegistration addRenderedHandler(RenderedHandler handler) {
825        return addHandler(handler, RenderedEvent.TYPE);
826    }
827}