1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
/*
* Copyright 2000-2022 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.v7.client.renderers;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.MouseEvent;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.vaadin.client.WidgetUtil;
import com.vaadin.v7.client.widget.escalator.Cell;
import com.vaadin.v7.client.widget.escalator.RowContainer;
import com.vaadin.v7.client.widget.grid.CellReference;
import com.vaadin.v7.client.widget.grid.EventCellReference;
import com.vaadin.v7.client.widgets.Escalator;
import com.vaadin.v7.client.widgets.Grid;
import com.vaadin.v7.shared.ui.grid.GridConstants.Section;
/**
* An abstract superclass for renderers that render clickable widgets. Click
* handlers can be added to a renderer to listen to click events emitted by all
* widgets rendered by the renderer.
*
* @param <T>
* the presentation (column) type
* @param <W>
* the widget type
*
* @since 7.4
* @author Vaadin Ltd
*/
public abstract class ClickableRenderer<T, W extends Widget>
extends WidgetRenderer<T, W> implements ClickHandler {
/**
* A handler for {@link RendererClickEvent renderer click events}.
*
* @param <R>
* the row type of the containing Grid
*
* @see ButtonRenderer#addClickHandler(RendererClickHandler)
*/
public interface RendererClickHandler<R> extends EventHandler {
/**
* Called when a rendered button is clicked.
*
* @param event
* the event representing the click
*/
void onClick(RendererClickEvent<R> event);
}
/**
* An event fired when a widget rendered by a ClickableWidgetRenderer
* subclass is clicked.
*
* @param <R>
* the row type of the containing Grid
*/
@SuppressWarnings("rawtypes")
public static class RendererClickEvent<R>
extends MouseEvent<RendererClickHandler> {
@SuppressWarnings("unchecked")
static final Type<RendererClickHandler> TYPE = new Type<RendererClickHandler>(
BrowserEvents.CLICK, new RendererClickEvent());
private CellReference<R> cell;
private R row;
private RendererClickEvent() {
}
/**
* Returns the cell of the clicked button.
*
* @return the cell
*/
public CellReference<R> getCell() {
return cell;
}
/**
* Returns the data object corresponding to the row of the clicked
* button.
*
* @return the row data object
*/
public R getRow() {
return row;
}
@Override
public Type<RendererClickHandler> getAssociatedType() {
return TYPE;
}
@Override
@SuppressWarnings("unchecked")
protected void dispatch(RendererClickHandler handler) {
EventTarget target = getNativeEvent().getEventTarget();
if (!Element.is(target)) {
return;
}
Element e = Element.as(target);
Grid<R> grid = (Grid<R>) findClosestParentGrid(e);
cell = findCell(grid, e);
row = cell.getRow();
handler.onClick(this);
}
/**
* Returns the cell the given element belongs to.
*
* @param grid
* the grid instance that is queried
* @param e
* a cell element or the descendant of one
* @return the cell or null if the element is not a grid cell or a
* descendant of one
*/
private static <T> CellReference<T> findCell(Grid<T> grid, Element e) {
RowContainer container = getEscalator(grid).findRowContainer(e);
if (container == null) {
return null;
}
Cell cell = container.getCell(e);
EventCellReference<T> cellReference = new EventCellReference<T>(
grid);
// FIXME: Section is currently always body. Might be useful for the
// future to have an actual check.
cellReference.set(cell, Section.BODY);
return cellReference;
}
private static native Escalator getEscalator(Grid<?> grid)
/*-{
return grid.@com.vaadin.v7.client.widgets.Grid::escalator;
}-*/;
/**
* Returns the Grid instance containing the given element, if any.
* <p>
* <strong>Note:</strong> This method may not work reliably if the grid
* in question is wrapped in a {@link Composite} <em>unless</em> the
* element is inside another widget that is a child of the wrapped grid;
* please refer to the note in
* {@link WidgetUtil#findWidget(Element, Class) Util.findWidget} for
* details.
*
* @param e
* the element whose parent grid to find
* @return the parent grid or null if none found.
*/
private static Grid<?> findClosestParentGrid(Element e) {
Widget w = WidgetUtil.findWidget(e, null);
while (w != null && !(w instanceof Grid)) {
w = w.getParent();
}
return (Grid<?>) w;
}
}
private HandlerManager handlerManager;
/**
* {@inheritDoc}
* <p>
* <em>Implementation note:</em> It is the implementing method's
* responsibility to add {@code this} as a click handler of the returned
* widget, or a widget nested therein, in order to make click events
* propagate properly to handlers registered via
* {@link #addClickHandler(RendererClickHandler) addClickHandler}.
*/
@Override
public abstract W createWidget();
/**
* Adds a click handler to this button renderer. The handler is invoked
* every time one of the widgets rendered by this renderer is clicked.
* <p>
* Note that the row type of the click handler must match the row type of
* the containing Grid.
*
* @param handler
* the click handler to be added
*/
public HandlerRegistration addClickHandler(
RendererClickHandler<?> handler) {
if (handlerManager == null) {
handlerManager = new HandlerManager(this);
}
return handlerManager.addHandler(RendererClickEvent.TYPE, handler);
}
@Override
public void onClick(ClickEvent event) {
/*
* The handler manager is lazily instantiated so it's null iff
* addClickHandler is never called.
*/
if (handlerManager != null) {
DomEvent.fireNativeEvent(event.getNativeEvent(), handlerManager);
}
}
}
|