Revision 969a4d94
b/build.xml | ||
---|---|---|
77 | 77 |
</target> |
78 | 78 |
|
79 | 79 |
<target name="uploadToVM" depends="gwt-compile"> |
80 |
<scp todir="chstath@pithos.dev.grnet.gr:/var/www/pithos_web_client" keyfile="/home/chstath/.ssh/id_rsa" passphrase="r0bax45">
|
|
80 |
<scp todir="chstath@pithos.dev.grnet.gr:/var/www/pithos_web_client" keyfile="/home/chstath/.ssh/chstath@ross.key" passphrase="r0bax45">
|
|
81 | 81 |
<fileset dir="${gwt.www.dir}/${gwt.module}"/> |
82 | 82 |
</scp> |
83 | 83 |
</target> |
... | ... | |
106 | 106 |
<arg value="${gwt.www.dir}"/> |
107 | 107 |
<arg value="-noserver"/> |
108 | 108 |
<arg value="-startupUrl"/> |
109 |
<arg value="http://127.0.0.1:8080/client/pithos.html"/>
|
|
109 |
<arg value="http://127.0.0.1:8080/client"/> |
|
110 | 110 |
<arg value="${gwt.module}"/> |
111 | 111 |
</java> |
112 | 112 |
</target> |
b/src/gr/grnet/pithos/web/Pithos.gwt.xml | ||
---|---|---|
40 | 40 |
<inherits name="com.google.gwt.json.JSON"/> |
41 | 41 |
|
42 | 42 |
<entry-point class='gr.grnet.pithos.web.client.Pithos' /> |
43 |
<stylesheet src='gss.css' />
|
|
43 |
<stylesheet src='pithos.css' />
|
|
44 | 44 |
|
45 | 45 |
<source path="client"/> |
46 | 46 |
</module> |
b/src/gr/grnet/pithos/web/client/FileList.java | ||
---|---|---|
83 | 83 |
|
84 | 84 |
interface TableResources extends CellTable.Resources { |
85 | 85 |
@Override |
86 |
@Source({CellTable.Style.DEFAULT_CSS, "GssCellTable.css"})
|
|
86 |
@Source({CellTable.Style.DEFAULT_CSS, "PithosCellTable.css"})
|
|
87 | 87 |
TableStyle cellTableStyle(); |
88 | 88 |
} |
89 | 89 |
|
... | ... | |
331 | 331 |
|
332 | 332 |
selectionModel = new MultiSelectionModel<File>(keyProvider); |
333 | 333 |
|
334 |
celltable.setSelectionModel(selectionModel, GSSSelectionEventManager.<File> createDefaultManager());
|
|
334 |
celltable.setSelectionModel(selectionModel, PithosSelectionEventManager.<File> createDefaultManager());
|
|
335 | 335 |
// celltable.setPageSize(Pithos.VISIBLE_FILE_COUNT); |
336 | 336 |
|
337 | 337 |
sinkEvents(Event.ONCONTEXTMENU); |
b/src/gr/grnet/pithos/web/client/FilePropertiesDialog.java | ||
---|---|---|
289 | 289 |
permList.updatePermissionTable(); |
290 | 290 |
} |
291 | 291 |
}); |
292 |
add.addStyleName("button"); |
|
292 | 293 |
permButtons.add(add); |
293 | 294 |
permButtons.setCellHorizontalAlignment(add, HasHorizontalAlignment.ALIGN_CENTER); |
294 | 295 |
|
... | ... | |
300 | 301 |
permList.updatePermissionTable(); |
301 | 302 |
} |
302 | 303 |
}); |
304 |
addUser.addStyleName("button"); |
|
303 | 305 |
permButtons.add(addUser); |
304 | 306 |
permButtons.setCellHorizontalAlignment(addUser, HasHorizontalAlignment.ALIGN_CENTER); |
305 | 307 |
|
b/src/gr/grnet/pithos/web/client/FolderPropertiesDialog.java | ||
---|---|---|
175 | 175 |
dlg.center(); |
176 | 176 |
} |
177 | 177 |
}); |
178 |
add.addStyleName("button"); |
|
178 | 179 |
permButtons.add(add); |
179 | 180 |
permButtons.setCellHorizontalAlignment(add, HasHorizontalAlignment.ALIGN_CENTER); |
180 | 181 |
|
... | ... | |
185 | 186 |
dlg.center(); |
186 | 187 |
} |
187 | 188 |
}); |
188 |
addUser.getElement().setId("folderPropertiesDialog.button.addUser");
|
|
189 |
addUser.addStyleName("button");
|
|
189 | 190 |
permButtons.add(addUser); |
190 | 191 |
permButtons.setCellHorizontalAlignment(addUser, HasHorizontalAlignment.ALIGN_CENTER); |
191 | 192 |
permButtons.setSpacing(8); |
192 |
permButtons.addStyleName("gss-TabPanelBottom"); |
|
193 | 193 |
permPanel.add(permButtons); |
194 | 194 |
} |
195 | 195 |
|
/dev/null | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright 2011 GRNET S.A. All rights reserved. |
|
3 |
* |
|
4 |
* Redistribution and use in source and binary forms, with or |
|
5 |
* without modification, are permitted provided that the following |
|
6 |
* conditions are met: |
|
7 |
* |
|
8 |
* 1. Redistributions of source code must retain the above |
|
9 |
* copyright notice, this list of conditions and the following |
|
10 |
* disclaimer. |
|
11 |
* |
|
12 |
* 2. Redistributions in binary form must reproduce the above |
|
13 |
* copyright notice, this list of conditions and the following |
|
14 |
* disclaimer in the documentation and/or other materials |
|
15 |
* provided with the distribution. |
|
16 |
* |
|
17 |
* THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
18 |
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
19 |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
20 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
21 |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
22 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
23 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
24 |
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
25 |
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
26 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
27 |
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
28 |
* POSSIBILITY OF SUCH DAMAGE. |
|
29 |
* |
|
30 |
* The views and conclusions contained in the software and |
|
31 |
* documentation are those of the authors and should not be |
|
32 |
* interpreted as representing official policies, either expressed |
|
33 |
* or implied, of GRNET S.A. |
|
34 |
*/ |
|
35 |
package gr.grnet.pithos.web.client; |
|
36 |
|
|
37 |
|
|
38 |
|
|
39 |
import java.util.ArrayList; |
|
40 |
import java.util.List; |
|
41 |
|
|
42 |
import com.google.gwt.dom.client.Element; |
|
43 |
import com.google.gwt.dom.client.InputElement; |
|
44 |
import com.google.gwt.dom.client.NativeEvent; |
|
45 |
import com.google.gwt.view.client.CellPreviewEvent; |
|
46 |
import com.google.gwt.view.client.HasData; |
|
47 |
import com.google.gwt.view.client.MultiSelectionModel; |
|
48 |
import com.google.gwt.view.client.Range; |
|
49 |
import com.google.gwt.view.client.SelectionModel; |
|
50 |
|
|
51 |
/** |
|
52 |
* An implementation of {@link com.google.gwt.view.client.CellPreviewEvent.Handler} that adds selection |
|
53 |
* support via the spacebar and mouse clicks and handles the control key. |
|
54 |
* |
|
55 |
* <p> |
|
56 |
* If the {@link HasData} source of the selection event uses a |
|
57 |
* {@link MultiSelectionModel}, this manager additionally provides support for |
|
58 |
* shift key to select a range of values. For all other {@link SelectionModel}s, |
|
59 |
* only the control key is supported. |
|
60 |
* </p> |
|
61 |
* |
|
62 |
* @param <T> the data type of records in the list |
|
63 |
*/ |
|
64 |
public class GSSSelectionEventManager<T> implements |
|
65 |
CellPreviewEvent.Handler<T> { |
|
66 |
|
|
67 |
/** |
|
68 |
* Implementation of {@link gr.grnet.pithos.web.client.GSSSelectionEventManager.EventTranslator} that only triggers selection when |
|
69 |
* any checkbox is selected. |
|
70 |
* |
|
71 |
* @param <T> the data type |
|
72 |
*/ |
|
73 |
public static class CheckboxEventTranslator<T> implements EventTranslator<T> { |
|
74 |
|
|
75 |
/** |
|
76 |
* The column index of the checkbox. Other columns are ignored. |
|
77 |
*/ |
|
78 |
private final int column; |
|
79 |
|
|
80 |
/** |
|
81 |
* Construct a new {@link gr.grnet.pithos.web.client.GSSSelectionEventManager.CheckboxEventTranslator} that will trigger |
|
82 |
* selection when any checkbox in any column is selected. |
|
83 |
*/ |
|
84 |
public CheckboxEventTranslator() { |
|
85 |
this(-1); |
|
86 |
} |
|
87 |
|
|
88 |
/** |
|
89 |
* Construct a new {@link gr.grnet.pithos.web.client.GSSSelectionEventManager.CheckboxEventTranslator} that will trigger |
|
90 |
* selection when a checkbox in the specified column is selected. |
|
91 |
* |
|
92 |
* @param column the column index, or -1 for all columns |
|
93 |
*/ |
|
94 |
public CheckboxEventTranslator(int column) { |
|
95 |
this.column = column; |
|
96 |
} |
|
97 |
|
|
98 |
@Override |
|
99 |
public boolean clearCurrentSelection(@SuppressWarnings("unused") CellPreviewEvent<T> event) { |
|
100 |
return false; |
|
101 |
} |
|
102 |
|
|
103 |
@Override |
|
104 |
public SelectAction translateSelectionEvent(CellPreviewEvent<T> event) { |
|
105 |
// Handle the event. |
|
106 |
NativeEvent nativeEvent = event.getNativeEvent(); |
|
107 |
if ("click".equals(nativeEvent.getType())) { |
|
108 |
// Ignore if the event didn't occur in the correct column. |
|
109 |
if (column > -1 && column != event.getColumn()) { |
|
110 |
return SelectAction.IGNORE; |
|
111 |
} |
|
112 |
|
|
113 |
// Determine if we clicked on a checkbox. |
|
114 |
Element target = nativeEvent.getEventTarget().cast(); |
|
115 |
if ("input".equals(target.getTagName().toLowerCase())) { |
|
116 |
final InputElement input = target.cast(); |
|
117 |
if ("checkbox".equals(input.getType().toLowerCase())) { |
|
118 |
// Synchronize the checkbox with the current selection state. |
|
119 |
input.setChecked(event.getDisplay().getSelectionModel().isSelected( |
|
120 |
event.getValue())); |
|
121 |
return SelectAction.TOGGLE; |
|
122 |
} |
|
123 |
} |
|
124 |
return SelectAction.IGNORE; |
|
125 |
} |
|
126 |
|
|
127 |
// For keyboard events, do the default action. |
|
128 |
return SelectAction.DEFAULT; |
|
129 |
} |
|
130 |
} |
|
131 |
|
|
132 |
/** |
|
133 |
* Translates {@link CellPreviewEvent}s into {@link SelectAction}s. |
|
134 |
*/ |
|
135 |
public static interface EventTranslator<T> { |
|
136 |
/** |
|
137 |
* Check whether a user selection event should clear all currently selected |
|
138 |
* values. |
|
139 |
* |
|
140 |
* @param event the {@link CellPreviewEvent} to translate |
|
141 |
*/ |
|
142 |
boolean clearCurrentSelection(CellPreviewEvent<T> event); |
|
143 |
|
|
144 |
/** |
|
145 |
* Translate the user selection event into a {@link SelectAction}. |
|
146 |
* |
|
147 |
* @param event the {@link CellPreviewEvent} to translate |
|
148 |
*/ |
|
149 |
SelectAction translateSelectionEvent(CellPreviewEvent<T> event); |
|
150 |
} |
|
151 |
|
|
152 |
/** |
|
153 |
* The action that controls how selection is handled. |
|
154 |
*/ |
|
155 |
public static enum SelectAction { |
|
156 |
DEFAULT, // Perform the default action. |
|
157 |
SELECT, // Select the value. |
|
158 |
DESELECT, // Deselect the value. |
|
159 |
TOGGLE, // Toggle the selected state of the value. |
|
160 |
IGNORE; // Ignore the event. |
|
161 |
} |
|
162 |
|
|
163 |
/** |
|
164 |
* Construct a new {@link GSSSelectionEventManager} that triggers |
|
165 |
* selection when any checkbox in any column is clicked. |
|
166 |
* |
|
167 |
* @param <T> the data type of the display |
|
168 |
* @return a {@link GSSSelectionEventManager} instance |
|
169 |
*/ |
|
170 |
public static <T> GSSSelectionEventManager<T> createCheckboxManager() { |
|
171 |
return new GSSSelectionEventManager<T>(new CheckboxEventTranslator<T>()); |
|
172 |
} |
|
173 |
|
|
174 |
/** |
|
175 |
* Construct a new {@link GSSSelectionEventManager} that triggers |
|
176 |
* selection when a checkbox in the specified column is clicked. |
|
177 |
* |
|
178 |
* @param <T> the data type of the display |
|
179 |
* @param column the column to handle |
|
180 |
* @return a {@link GSSSelectionEventManager} instance |
|
181 |
*/ |
|
182 |
public static <T> GSSSelectionEventManager<T> createCheckboxManager( |
|
183 |
int column) { |
|
184 |
return new GSSSelectionEventManager<T>(new CheckboxEventTranslator<T>( |
|
185 |
column)); |
|
186 |
} |
|
187 |
|
|
188 |
/** |
|
189 |
* Create a new {@link GSSSelectionEventManager} using the specified |
|
190 |
* {@link EventTranslator} to control which {@link SelectAction} to take for |
|
191 |
* each event. |
|
192 |
* |
|
193 |
* @param <T> the data type of the display |
|
194 |
* @param translator the {@link EventTranslator} to use |
|
195 |
* @return a {@link GSSSelectionEventManager} instance |
|
196 |
*/ |
|
197 |
public static <T> GSSSelectionEventManager<T> createCustomManager( |
|
198 |
EventTranslator<T> translator) { |
|
199 |
return new GSSSelectionEventManager<T>(translator); |
|
200 |
} |
|
201 |
|
|
202 |
/** |
|
203 |
* Create a new {@link GSSSelectionEventManager} that handles selection |
|
204 |
* via user interactions. |
|
205 |
* |
|
206 |
* @param <T> the data type of the display |
|
207 |
* @return a new {@link GSSSelectionEventManager} instance |
|
208 |
*/ |
|
209 |
public static <T> GSSSelectionEventManager<T> createDefaultManager() { |
|
210 |
return new GSSSelectionEventManager<T>(null); |
|
211 |
} |
|
212 |
|
|
213 |
/** |
|
214 |
* The last {@link HasData} that was handled. |
|
215 |
*/ |
|
216 |
private HasData<T> lastDisplay; |
|
217 |
|
|
218 |
/** |
|
219 |
* The last page start. |
|
220 |
*/ |
|
221 |
private int lastPageStart; |
|
222 |
|
|
223 |
/** |
|
224 |
* The last selected row index. |
|
225 |
*/ |
|
226 |
private int lastSelectedIndex = -1; |
|
227 |
|
|
228 |
/** |
|
229 |
* A boolean indicating that the last shift selection was additive. |
|
230 |
*/ |
|
231 |
private boolean shiftAdditive; |
|
232 |
|
|
233 |
/** |
|
234 |
* The last place where the user clicked without holding shift. Multi |
|
235 |
* selections that use the shift key are rooted at the anchor. |
|
236 |
*/ |
|
237 |
private int shiftAnchor = -1; |
|
238 |
|
|
239 |
/** |
|
240 |
* The {@link EventTranslator} that controls how selection is handled. |
|
241 |
*/ |
|
242 |
private final EventTranslator<T> translator; |
|
243 |
|
|
244 |
/** |
|
245 |
* Construct a new {@link GSSSelectionEventManager} using the specified |
|
246 |
* {@link EventTranslator} to control which {@link SelectAction} to take for |
|
247 |
* each event. |
|
248 |
* |
|
249 |
* @param translator the {@link EventTranslator} to use |
|
250 |
*/ |
|
251 |
protected GSSSelectionEventManager(EventTranslator<T> translator) { |
|
252 |
this.translator = translator; |
|
253 |
} |
|
254 |
|
|
255 |
/** |
|
256 |
* Update the selection model based on a user selection event. |
|
257 |
* |
|
258 |
* @param selectionModel the selection model to update |
|
259 |
* @param row the selected row index relative to the page start |
|
260 |
* @param rowValue the selected row value |
|
261 |
* @param action the {@link SelectAction} to apply |
|
262 |
* @param selectRange true to select the range from the last selected row |
|
263 |
* @param clearOthers true to clear the current selection |
|
264 |
*/ |
|
265 |
public void doMultiSelection(MultiSelectionModel<? super T> selectionModel, |
|
266 |
HasData<T> display, int row, T rowValue, SelectAction action, |
|
267 |
boolean selectRange, boolean clearOthers) { |
|
268 |
// Determine if we will add or remove selection. |
|
269 |
boolean addToSelection = true; |
|
270 |
if (action != null) { |
|
271 |
switch (action) { |
|
272 |
case IGNORE: |
|
273 |
// Ignore selection. |
|
274 |
return; |
|
275 |
case SELECT: |
|
276 |
addToSelection = true; |
|
277 |
break; |
|
278 |
case DESELECT: |
|
279 |
addToSelection = false; |
|
280 |
break; |
|
281 |
case TOGGLE: |
|
282 |
addToSelection = !selectionModel.isSelected(rowValue); |
|
283 |
break; |
|
284 |
case DEFAULT: |
|
285 |
break; |
|
286 |
} |
|
287 |
} |
|
288 |
|
|
289 |
// Determine which rows will be newly selected. |
|
290 |
int pageStart = display.getVisibleRange().getStart(); |
|
291 |
if (selectRange && pageStart == lastPageStart && lastSelectedIndex > -1 |
|
292 |
&& shiftAnchor > -1 && display == lastDisplay) { |
|
293 |
/* |
|
294 |
* Get the new shift bounds based on the existing shift anchor and the |
|
295 |
* selected row. |
|
296 |
*/ |
|
297 |
int start = Math.min(shiftAnchor, row); // Inclusive. |
|
298 |
int end = Math.max(shiftAnchor, row); // Inclusive. |
|
299 |
|
|
300 |
if (lastSelectedIndex < start) { |
|
301 |
// Revert previous selection if the user reselects a smaller range. |
|
302 |
setRangeSelection(selectionModel, display, new Range(lastSelectedIndex, |
|
303 |
start - lastSelectedIndex), !shiftAdditive, false); |
|
304 |
} else if (lastSelectedIndex > end) { |
|
305 |
// Revert previous selection if the user reselects a smaller range. |
|
306 |
setRangeSelection(selectionModel, display, new Range(end + 1, |
|
307 |
lastSelectedIndex - end), !shiftAdditive, false); |
|
308 |
} else { |
|
309 |
// Remember if we are adding or removing rows. |
|
310 |
shiftAdditive = addToSelection; |
|
311 |
} |
|
312 |
|
|
313 |
// Update the last selected row, but do not move the shift anchor. |
|
314 |
lastSelectedIndex = row; |
|
315 |
|
|
316 |
// Select the range. |
|
317 |
setRangeSelection(selectionModel, display, new Range(start, end - start |
|
318 |
+ 1), shiftAdditive, clearOthers); |
|
319 |
} else { |
|
320 |
/* |
|
321 |
* If we are not selecting a range, save the last row and set the shift |
|
322 |
* anchor. |
|
323 |
*/ |
|
324 |
lastDisplay = display; |
|
325 |
lastPageStart = pageStart; |
|
326 |
lastSelectedIndex = row; |
|
327 |
shiftAnchor = row; |
|
328 |
selectOne(selectionModel, rowValue, addToSelection, clearOthers); |
|
329 |
} |
|
330 |
} |
|
331 |
|
|
332 |
@Override |
|
333 |
public void onCellPreview(CellPreviewEvent<T> event) { |
|
334 |
// Early exit if selection is already handled or we are editing. |
|
335 |
if (event.isCellEditing() || event.isSelectionHandled()) { |
|
336 |
return; |
|
337 |
} |
|
338 |
|
|
339 |
// Early exit if we do not have a SelectionModel. |
|
340 |
HasData<T> display = event.getDisplay(); |
|
341 |
SelectionModel<? super T> selectionModel = display.getSelectionModel(); |
|
342 |
if (selectionModel == null) { |
|
343 |
return; |
|
344 |
} |
|
345 |
|
|
346 |
// Check for user defined actions. |
|
347 |
SelectAction action = (translator == null) ? SelectAction.DEFAULT |
|
348 |
: translator.translateSelectionEvent(event); |
|
349 |
|
|
350 |
// Handle the event based on the SelectionModel type. |
|
351 |
if (selectionModel instanceof MultiSelectionModel) { |
|
352 |
// Add shift key support for MultiSelectionModel. |
|
353 |
handleMultiSelectionEvent(event, action, |
|
354 |
(MultiSelectionModel<? super T>) selectionModel); |
|
355 |
} else { |
|
356 |
// Use the standard handler. |
|
357 |
handleSelectionEvent(event, action, selectionModel); |
|
358 |
} |
|
359 |
} |
|
360 |
|
|
361 |
/** |
|
362 |
* Removes all items from the selection. |
|
363 |
* |
|
364 |
* @param selectionModel the {@link MultiSelectionModel} to clear |
|
365 |
*/ |
|
366 |
protected void clearSelection(MultiSelectionModel<? super T> selectionModel) { |
|
367 |
selectionModel.clear(); |
|
368 |
} |
|
369 |
|
|
370 |
/** |
|
371 |
* Handle an event that could cause a value to be selected for a |
|
372 |
* {@link MultiSelectionModel}. This overloaded method adds support for both |
|
373 |
* the control and shift keys. If the shift key is held down, all rows between |
|
374 |
* the previous selected row and the current row are selected. |
|
375 |
* |
|
376 |
* @param event the {@link CellPreviewEvent} that triggered selection |
|
377 |
* @param action the action to handle |
|
378 |
* @param selectionModel the {@link SelectionModel} to update |
|
379 |
*/ |
|
380 |
protected void handleMultiSelectionEvent(CellPreviewEvent<T> event, |
|
381 |
SelectAction action, MultiSelectionModel<? super T> selectionModel) { |
|
382 |
NativeEvent nativeEvent = event.getNativeEvent(); |
|
383 |
String type = nativeEvent.getType(); |
|
384 |
boolean rightclick = "mousedown".equals(type) && nativeEvent.getButton()==NativeEvent.BUTTON_RIGHT; |
|
385 |
SelectAction action1 = action; |
|
386 |
if(rightclick){ |
|
387 |
boolean shift = nativeEvent.getShiftKey(); |
|
388 |
boolean ctrlOrMeta = nativeEvent.getCtrlKey() || nativeEvent.getMetaKey(); |
|
389 |
boolean clearOthers = (translator == null) ? !ctrlOrMeta |
|
390 |
: translator.clearCurrentSelection(event); |
|
391 |
if (action == null || action == SelectAction.DEFAULT) { |
|
392 |
action1 = ctrlOrMeta ? SelectAction.TOGGLE : SelectAction.SELECT; |
|
393 |
} |
|
394 |
//if the row is selected then do nothing |
|
395 |
if(selectionModel.isSelected(event.getValue())){ |
|
396 |
return; |
|
397 |
} |
|
398 |
doMultiSelection(selectionModel, event.getDisplay(), event.getIndex(), |
|
399 |
event.getValue(), action1, shift, clearOthers); |
|
400 |
} |
|
401 |
else if ("click".equals(type)) { |
|
402 |
/* |
|
403 |
* Update selection on click. Selection is toggled only if the user |
|
404 |
* presses the ctrl key. If the user does not press the control key, |
|
405 |
* selection is additive. |
|
406 |
*/ |
|
407 |
boolean shift = nativeEvent.getShiftKey(); |
|
408 |
boolean ctrlOrMeta = nativeEvent.getCtrlKey() || nativeEvent.getMetaKey(); |
|
409 |
boolean clearOthers = (translator == null) ? !ctrlOrMeta |
|
410 |
: translator.clearCurrentSelection(event); |
|
411 |
if (action == null || action == SelectAction.DEFAULT) { |
|
412 |
action1 = ctrlOrMeta ? SelectAction.TOGGLE : SelectAction.SELECT; |
|
413 |
} |
|
414 |
doMultiSelection(selectionModel, event.getDisplay(), event.getIndex(), |
|
415 |
event.getValue(), action1, shift, clearOthers); |
|
416 |
if(ctrlOrMeta){ |
|
417 |
event.setCanceled(true); |
|
418 |
} |
|
419 |
} else if ("keyup".equals(type)) { |
|
420 |
int keyCode = nativeEvent.getKeyCode(); |
|
421 |
if (keyCode == 32) { |
|
422 |
/* |
|
423 |
* Update selection when the space bar is pressed. The spacebar always |
|
424 |
* toggles selection, regardless of whether the control key is pressed. |
|
425 |
*/ |
|
426 |
boolean shift = nativeEvent.getShiftKey(); |
|
427 |
boolean clearOthers = (translator == null) ? false |
|
428 |
: translator.clearCurrentSelection(event); |
|
429 |
if (action == null || action == SelectAction.DEFAULT) { |
|
430 |
action1 = SelectAction.TOGGLE; |
|
431 |
} |
|
432 |
doMultiSelection(selectionModel, event.getDisplay(), event.getIndex(), |
|
433 |
event.getValue(), action1, shift, clearOthers); |
|
434 |
} |
|
435 |
} |
|
436 |
} |
|
437 |
|
|
438 |
/** |
|
439 |
* Handle an event that could cause a value to be selected. This method works |
|
440 |
* for any {@link SelectionModel}. Pressing the space bar or ctrl+click will |
|
441 |
* toggle the selection state. Clicking selects the row if it is not selected. |
|
442 |
* |
|
443 |
* @param event the {@link CellPreviewEvent} that triggered selection |
|
444 |
* @param action the action to handle |
|
445 |
* @param selectionModel the {@link SelectionModel} to update |
|
446 |
*/ |
|
447 |
protected void handleSelectionEvent(CellPreviewEvent<T> event, |
|
448 |
SelectAction action, SelectionModel<? super T> selectionModel) { |
|
449 |
// Handle selection overrides. |
|
450 |
T value = event.getValue(); |
|
451 |
if (action != null) { |
|
452 |
switch (action) { |
|
453 |
case IGNORE: |
|
454 |
return; |
|
455 |
case SELECT: |
|
456 |
selectionModel.setSelected(value, true); |
|
457 |
return; |
|
458 |
case DESELECT: |
|
459 |
selectionModel.setSelected(value, false); |
|
460 |
return; |
|
461 |
case TOGGLE: |
|
462 |
selectionModel.setSelected(value, !selectionModel.isSelected(value)); |
|
463 |
return; |
|
464 |
case DEFAULT: |
|
465 |
break; |
|
466 |
} |
|
467 |
} |
|
468 |
|
|
469 |
// Handle default selection. |
|
470 |
NativeEvent nativeEvent = event.getNativeEvent(); |
|
471 |
String type = nativeEvent.getType(); |
|
472 |
if ("click".equals(type)) { |
|
473 |
if (nativeEvent.getCtrlKey() || nativeEvent.getMetaKey()) { |
|
474 |
// Toggle selection on ctrl+click. |
|
475 |
selectionModel.setSelected(value, !selectionModel.isSelected(value)); |
|
476 |
} else { |
|
477 |
// Select on click. |
|
478 |
selectionModel.setSelected(value, true); |
|
479 |
} |
|
480 |
} else if ("keyup".equals(type)) { |
|
481 |
// Toggle selection on space. |
|
482 |
int keyCode = nativeEvent.getKeyCode(); |
|
483 |
if (keyCode == 32) { |
|
484 |
selectionModel.setSelected(value, !selectionModel.isSelected(value)); |
|
485 |
} |
|
486 |
} |
|
487 |
} |
|
488 |
|
|
489 |
/** |
|
490 |
* Selects the given item, optionally clearing any prior selection. |
|
491 |
* |
|
492 |
* @param selectionModel the {@link MultiSelectionModel} to update |
|
493 |
* @param target the item to select |
|
494 |
* @param selected true to select, false to deselect |
|
495 |
* @param clearOthers true to clear all other selected items |
|
496 |
*/ |
|
497 |
protected void selectOne(MultiSelectionModel<? super T> selectionModel, |
|
498 |
T target, boolean selected, boolean clearOthers) { |
|
499 |
if (clearOthers) { |
|
500 |
clearSelection(selectionModel); |
|
501 |
} |
|
502 |
selectionModel.setSelected(target, selected); |
|
503 |
} |
|
504 |
|
|
505 |
/** |
|
506 |
* Select or deselect a range of row indexes, optionally deselecting all other |
|
507 |
* values. |
|
508 |
* |
|
509 |
* @param selectionModel the {@link MultiSelectionModel} to update |
|
510 |
* @param display the {@link HasData} source of the selection event |
|
511 |
* @param range the {@link Range} of rows to select or deselect |
|
512 |
* @param addToSelection true to select, false to deselect the range |
|
513 |
* @param clearOthers true to deselect rows not in the range |
|
514 |
*/ |
|
515 |
protected void setRangeSelection( |
|
516 |
MultiSelectionModel<? super T> selectionModel, HasData<T> display, |
|
517 |
Range range, boolean addToSelection, boolean clearOthers) { |
|
518 |
// Get the list of values to select. |
|
519 |
List<T> toUpdate = new ArrayList<T>(); |
|
520 |
int start = range.getStart(); |
|
521 |
int end = start + range.getLength(); |
|
522 |
for (int i = start; i < end ; i++) { |
|
523 |
toUpdate.add(display.getVisibleItem(i-display.getVisibleRange().getStart())); |
|
524 |
} |
|
525 |
// Clear all other values. |
|
526 |
if (clearOthers) { |
|
527 |
clearSelection(selectionModel); |
|
528 |
} |
|
529 |
|
|
530 |
// Update the state of the values. |
|
531 |
for (T value : toUpdate) { |
|
532 |
selectionModel.setSelected(value, addToSelection); |
|
533 |
} |
|
534 |
} |
|
535 |
} |
|
536 |
|
/dev/null | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright 2010 Google Inc. |
|
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 |
/* Incremental changes from CellTable.css */ |
|
17 |
.cellTableFooter { |
|
18 |
padding: 3px 9px; |
|
19 |
} |
|
20 |
|
|
21 |
.cellTableHeader { |
|
22 |
border:none; |
|
23 |
font-weight: bold; |
|
24 |
cursor: pointer; |
|
25 |
color: white; |
|
26 |
} |
|
27 |
|
|
28 |
.cellTableCell { |
|
29 |
padding: 4px 9px; |
|
30 |
border-width: 0; |
|
31 |
} |
|
32 |
|
|
33 |
.cellTableFirstColumn { |
|
34 |
padding: 0px; |
|
35 |
} |
|
36 |
|
|
37 |
.cellTableLastColumn { |
|
38 |
padding: 0px; |
|
39 |
} |
|
40 |
|
|
41 |
.cellTableFirstColumnFooter { |
|
42 |
border: 0px; |
|
43 |
padding: 0px; |
|
44 |
} |
|
45 |
|
|
46 |
.cellTableFirstColumnHeader { |
|
47 |
border: 0px; |
|
48 |
padding: 0px; |
|
49 |
} |
|
50 |
|
|
51 |
.cellTableLastColumnFooter { |
|
52 |
border: 0px; |
|
53 |
padding: 0px; |
|
54 |
} |
|
55 |
|
|
56 |
.cellTableLastColumnHeader { |
|
57 |
border: 0px; |
|
58 |
padding: 0px; |
|
59 |
} |
|
60 |
|
|
61 |
.cellTableEvenRow { |
|
62 |
cursor: hand; |
|
63 |
cursor: pointer; |
|
64 |
background: none; |
|
65 |
} |
|
66 |
|
|
67 |
.cellTableOddRow { |
|
68 |
cursor: hand; |
|
69 |
cursor: pointer; |
|
70 |
background: none; |
|
71 |
} |
|
72 |
|
|
73 |
.cellTableSelectedRow { |
|
74 |
color: #d45500; |
|
75 |
height: auto; |
|
76 |
overflow: auto; |
|
77 |
} |
|
78 |
|
|
79 |
.cellTableHoveredRow { |
|
80 |
background: none; |
|
81 |
} |
/dev/null | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright 2010 Google Inc. |
|
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 |
.cellTreeWidget { |
|
17 |
|
|
18 |
} |
|
19 |
|
|
20 |
.cellTreeEmptyMessage { |
|
21 |
padding-left: 16px; |
|
22 |
font-style: italic; |
|
23 |
background: url(images/cellTreeLoadingBasic.gif) no-repeat; |
|
24 |
color: #4085A5; |
|
25 |
} |
|
26 |
|
|
27 |
.cellTreeItem { |
|
28 |
padding-top: 4px; |
|
29 |
padding-bottom: 4px; |
|
30 |
cursor: hand; |
|
31 |
cursor: pointer; |
|
32 |
zoom: 1; |
|
33 |
} |
|
34 |
|
|
35 |
.cellTreeItemImage { |
|
36 |
|
|
37 |
} |
|
38 |
|
|
39 |
.cellTreeItemImageValue { |
|
40 |
zoom: 1; |
|
41 |
} |
|
42 |
|
|
43 |
.cellTreeItemValue { |
|
44 |
padding-left: 3px; |
|
45 |
padding-right: 3px; |
|
46 |
outline: none; |
|
47 |
} |
|
48 |
|
|
49 |
.cellTreeOpenItem { |
|
50 |
|
|
51 |
} |
|
52 |
|
|
53 |
.cellTreeTopItem { |
|
54 |
|
|
55 |
} |
|
56 |
|
|
57 |
.cellTreeTopItemImage { |
|
58 |
|
|
59 |
} |
|
60 |
|
|
61 |
.cellTreeTopItemImageValue { |
|
62 |
|
|
63 |
} |
|
64 |
|
|
65 |
.cellTreeKeyboardSelectedItem { |
|
66 |
outline: none; |
|
67 |
} |
|
68 |
|
|
69 |
.cellTreeSelectedItem { |
|
70 |
color: #d45500; |
|
71 |
height: auto; |
|
72 |
overflow: visible; |
|
73 |
} |
|
74 |
|
|
75 |
.cellTreeShowMoreButton { |
|
76 |
padding-left: 16px; |
|
77 |
outline: none; |
|
78 |
} |
b/src/gr/grnet/pithos/web/client/PithosCellTable.css | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright 2010 Google Inc. |
|
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 |
/* Incremental changes from CellTable.css */ |
|
17 |
.cellTableFooter { |
|
18 |
padding: 3px 9px; |
|
19 |
} |
|
20 |
|
|
21 |
.cellTableHeader { |
|
22 |
border:none; |
|
23 |
font-weight: bold; |
|
24 |
cursor: pointer; |
|
25 |
color: white; |
|
26 |
} |
|
27 |
|
|
28 |
.cellTableCell { |
|
29 |
padding: 4px 9px; |
|
30 |
border-width: 0; |
|
31 |
} |
|
32 |
|
|
33 |
.cellTableFirstColumn { |
|
34 |
padding: 0px; |
|
35 |
} |
|
36 |
|
|
37 |
.cellTableLastColumn { |
|
38 |
padding: 0px; |
|
39 |
} |
|
40 |
|
|
41 |
.cellTableFirstColumnFooter { |
|
42 |
border: 0px; |
|
43 |
padding: 0px; |
|
44 |
} |
|
45 |
|
|
46 |
.cellTableFirstColumnHeader { |
|
47 |
border: 0px; |
|
48 |
padding: 0px; |
|
49 |
} |
|
50 |
|
|
51 |
.cellTableLastColumnFooter { |
|
52 |
border: 0px; |
|
53 |
padding: 0px; |
|
54 |
} |
|
55 |
|
|
56 |
.cellTableLastColumnHeader { |
|
57 |
border: 0px; |
|
58 |
padding: 0px; |
|
59 |
} |
|
60 |
|
|
61 |
.cellTableEvenRow { |
|
62 |
cursor: hand; |
|
63 |
cursor: pointer; |
|
64 |
background: none; |
|
65 |
} |
|
66 |
|
|
67 |
.cellTableOddRow { |
|
68 |
cursor: hand; |
|
69 |
cursor: pointer; |
|
70 |
background: none; |
|
71 |
} |
|
72 |
|
|
73 |
.cellTableSelectedRow { |
|
74 |
color: #d45500; |
|
75 |
height: auto; |
|
76 |
overflow: auto; |
|
77 |
} |
|
78 |
|
|
79 |
.cellTableHoveredRow { |
|
80 |
background: none; |
|
81 |
} |
b/src/gr/grnet/pithos/web/client/PithosCellTreeBasic.css | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright 2010 Google Inc. |
|
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 |
.cellTreeWidget { |
|
17 |
|
|
18 |
} |
|
19 |
|
|
20 |
.cellTreeEmptyMessage { |
|
21 |
padding-left: 16px; |
|
22 |
font-style: italic; |
|
23 |
background: url(images/cellTreeLoadingBasic.gif) no-repeat; |
|
24 |
color: #4085A5; |
|
25 |
} |
|
26 |
|
|
27 |
.cellTreeItem { |
|
28 |
padding-top: 4px; |
|
29 |
padding-bottom: 4px; |
|
30 |
cursor: hand; |
|
31 |
cursor: pointer; |
|
32 |
zoom: 1; |
|
33 |
} |
|
34 |
|
|
35 |
.cellTreeItemImage { |
|
36 |
|
|
37 |
} |
|
38 |
|
|
39 |
.cellTreeItemImageValue { |
|
40 |
zoom: 1; |
|
41 |
} |
|
42 |
|
|
43 |
.cellTreeItemValue { |
|
44 |
padding-left: 3px; |
|
45 |
padding-right: 3px; |
|
46 |
outline: none; |
|
47 |
} |
|
48 |
|
|
49 |
.cellTreeOpenItem { |
|
50 |
|
|
51 |
} |
|
52 |
|
|
53 |
.cellTreeTopItem { |
|
54 |
|
|
55 |
} |
|
56 |
|
|
57 |
.cellTreeTopItemImage { |
|
58 |
|
|
59 |
} |
|
60 |
|
|
61 |
.cellTreeTopItemImageValue { |
|
62 |
|
|
63 |
} |
|
64 |
|
|
65 |
.cellTreeKeyboardSelectedItem { |
|
66 |
outline: none; |
|
67 |
} |
|
68 |
|
|
69 |
.cellTreeSelectedItem { |
|
70 |
color: #d45500; |
|
71 |
height: auto; |
|
72 |
overflow: visible; |
|
73 |
} |
|
74 |
|
|
75 |
.cellTreeShowMoreButton { |
|
76 |
padding-left: 16px; |
|
77 |
outline: none; |
|
78 |
} |
b/src/gr/grnet/pithos/web/client/PithosSelectionEventManager.java | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright 2011 GRNET S.A. All rights reserved. |
|
3 |
* |
|
4 |
* Redistribution and use in source and binary forms, with or |
|
5 |
* without modification, are permitted provided that the following |
|
6 |
* conditions are met: |
|
7 |
* |
|
8 |
* 1. Redistributions of source code must retain the above |
|
9 |
* copyright notice, this list of conditions and the following |
|
10 |
* disclaimer. |
|
11 |
* |
|
12 |
* 2. Redistributions in binary form must reproduce the above |
|
13 |
* copyright notice, this list of conditions and the following |
|
14 |
* disclaimer in the documentation and/or other materials |
|
15 |
* provided with the distribution. |
|
16 |
* |
|
17 |
* THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
|
18 |
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
19 |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
20 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
|
21 |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
22 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
23 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
24 |
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
25 |
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
26 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
27 |
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
28 |
* POSSIBILITY OF SUCH DAMAGE. |
|
29 |
* |
|
30 |
* The views and conclusions contained in the software and |
|
31 |
* documentation are those of the authors and should not be |
|
32 |
* interpreted as representing official policies, either expressed |
|
33 |
* or implied, of GRNET S.A. |
|
34 |
*/ |
|
35 |
package gr.grnet.pithos.web.client; |
|
36 |
|
|
37 |
|
|
38 |
|
|
39 |
import java.util.ArrayList; |
|
40 |
import java.util.List; |
|
41 |
|
|
42 |
import com.google.gwt.dom.client.Element; |
|
43 |
import com.google.gwt.dom.client.InputElement; |
|
44 |
import com.google.gwt.dom.client.NativeEvent; |
|
45 |
import com.google.gwt.view.client.CellPreviewEvent; |
|
46 |
import com.google.gwt.view.client.HasData; |
|
47 |
import com.google.gwt.view.client.MultiSelectionModel; |
|
48 |
import com.google.gwt.view.client.Range; |
|
49 |
import com.google.gwt.view.client.SelectionModel; |
|
50 |
|
|
51 |
/** |
|
52 |
* An implementation of {@link com.google.gwt.view.client.CellPreviewEvent.Handler} that adds selection |
|
53 |
* support via the spacebar and mouse clicks and handles the control key. |
|
54 |
* |
|
55 |
* <p> |
|
56 |
* If the {@link HasData} source of the selection event uses a |
|
57 |
* {@link MultiSelectionModel}, this manager additionally provides support for |
|
58 |
* shift key to select a range of values. For all other {@link SelectionModel}s, |
|
59 |
* only the control key is supported. |
|
60 |
* </p> |
|
61 |
* |
|
62 |
* @param <T> the data type of records in the list |
|
63 |
*/ |
|
64 |
public class PithosSelectionEventManager<T> implements |
|
65 |
CellPreviewEvent.Handler<T> { |
|
66 |
|
|
67 |
/** |
|
68 |
* Implementation of {@link gr.grnet.pithos.web.client.PithosSelectionEventManager.EventTranslator} that only triggers selection when |
|
69 |
* any checkbox is selected. |
|
70 |
* |
|
71 |
* @param <T> the data type |
|
72 |
*/ |
|
73 |
public static class CheckboxEventTranslator<T> implements EventTranslator<T> { |
|
74 |
|
|
75 |
/** |
|
76 |
* The column index of the checkbox. Other columns are ignored. |
|
77 |
*/ |
|
78 |
private final int column; |
|
79 |
|
|
80 |
/** |
|
81 |
* Construct a new {@link gr.grnet.pithos.web.client.PithosSelectionEventManager.CheckboxEventTranslator} that will trigger |
|
82 |
* selection when any checkbox in any column is selected. |
|
83 |
*/ |
|
84 |
public CheckboxEventTranslator() { |
|
85 |
this(-1); |
|
86 |
} |
|
87 |
|
|
88 |
/** |
|
89 |
* Construct a new {@link gr.grnet.pithos.web.client.PithosSelectionEventManager.CheckboxEventTranslator} that will trigger |
|
90 |
* selection when a checkbox in the specified column is selected. |
|
91 |
* |
|
92 |
* @param column the column index, or -1 for all columns |
|
93 |
*/ |
|
94 |
public CheckboxEventTranslator(int column) { |
|
95 |
this.column = column; |
|
96 |
} |
|
97 |
|
|
98 |
@Override |
|
99 |
public boolean clearCurrentSelection(@SuppressWarnings("unused") CellPreviewEvent<T> event) { |
|
100 |
return false; |
|
101 |
} |
|
102 |
|
|
103 |
@Override |
|
104 |
public SelectAction translateSelectionEvent(CellPreviewEvent<T> event) { |
|
105 |
// Handle the event. |
|
106 |
NativeEvent nativeEvent = event.getNativeEvent(); |
|
107 |
if ("click".equals(nativeEvent.getType())) { |
|
108 |
// Ignore if the event didn't occur in the correct column. |
|
109 |
if (column > -1 && column != event.getColumn()) { |
|
110 |
return SelectAction.IGNORE; |
|
111 |
} |
|
112 |
|
|
113 |
// Determine if we clicked on a checkbox. |
|
114 |
Element target = nativeEvent.getEventTarget().cast(); |
|
115 |
if ("input".equals(target.getTagName().toLowerCase())) { |
|
116 |
final InputElement input = target.cast(); |
|
117 |
if ("checkbox".equals(input.getType().toLowerCase())) { |
|
118 |
// Synchronize the checkbox with the current selection state. |
|
119 |
input.setChecked(event.getDisplay().getSelectionModel().isSelected( |
|
120 |
event.getValue())); |
|
121 |
return SelectAction.TOGGLE; |
|
122 |
} |
|
123 |
} |
|
124 |
return SelectAction.IGNORE; |
|
125 |
} |
|
126 |
|
|
127 |
// For keyboard events, do the default action. |
|
128 |
return SelectAction.DEFAULT; |
|
129 |
} |
|
130 |
} |
|
131 |
|
|
132 |
/** |
|
133 |
* Translates {@link CellPreviewEvent}s into {@link SelectAction}s. |
|
134 |
*/ |
|
135 |
public static interface EventTranslator<T> { |
|
136 |
/** |
|
137 |
* Check whether a user selection event should clear all currently selected |
|
138 |
* values. |
|
139 |
* |
|
140 |
* @param event the {@link CellPreviewEvent} to translate |
|
141 |
*/ |
|
142 |
boolean clearCurrentSelection(CellPreviewEvent<T> event); |
|
143 |
|
|
144 |
/** |
|
145 |
* Translate the user selection event into a {@link SelectAction}. |
|
146 |
* |
|
147 |
* @param event the {@link CellPreviewEvent} to translate |
|
148 |
*/ |
|
149 |
SelectAction translateSelectionEvent(CellPreviewEvent<T> event); |
|
150 |
} |
|
151 |
|
|
152 |
/** |
|
153 |
* The action that controls how selection is handled. |
|
154 |
*/ |
|
155 |
public static enum SelectAction { |
|
156 |
DEFAULT, // Perform the default action. |
|
157 |
SELECT, // Select the value. |
|
158 |
DESELECT, // Deselect the value. |
|
159 |
TOGGLE, // Toggle the selected state of the value. |
|
160 |
IGNORE; // Ignore the event. |
|
161 |
} |
|
162 |
|
|
163 |
/** |
|
164 |
* Construct a new {@link PithosSelectionEventManager} that triggers |
|
165 |
* selection when any checkbox in any column is clicked. |
|
166 |
* |
|
167 |
* @param <T> the data type of the display |
|
168 |
* @return a {@link PithosSelectionEventManager} instance |
|
169 |
*/ |
|
170 |
public static <T> PithosSelectionEventManager<T> createCheckboxManager() { |
|
171 |
return new PithosSelectionEventManager<T>(new CheckboxEventTranslator<T>()); |
|
172 |
} |
|
173 |
|
|
174 |
/** |
|
175 |
* Construct a new {@link PithosSelectionEventManager} that triggers |
|
176 |
* selection when a checkbox in the specified column is clicked. |
|
177 |
* |
|
178 |
* @param <T> the data type of the display |
|
179 |
* @param column the column to handle |
|
180 |
* @return a {@link PithosSelectionEventManager} instance |
|
181 |
*/ |
|
182 |
public static <T> PithosSelectionEventManager<T> createCheckboxManager( |
|
183 |
int column) { |
|
184 |
return new PithosSelectionEventManager<T>(new CheckboxEventTranslator<T>( |
|
185 |
column)); |
|
186 |
} |
|
187 |
|
|
188 |
/** |
|
189 |
* Create a new {@link PithosSelectionEventManager} using the specified |
|
190 |
* {@link EventTranslator} to control which {@link SelectAction} to take for |
|
191 |
* each event. |
|
192 |
* |
|
193 |
* @param <T> the data type of the display |
|
194 |
* @param translator the {@link EventTranslator} to use |
|
195 |
* @return a {@link PithosSelectionEventManager} instance |
|
196 |
*/ |
|
197 |
public static <T> PithosSelectionEventManager<T> createCustomManager( |
|
198 |
EventTranslator<T> translator) { |
|
199 |
return new PithosSelectionEventManager<T>(translator); |
|
200 |
} |
|
201 |
|
|
202 |
/** |
|
203 |
* Create a new {@link PithosSelectionEventManager} that handles selection |
|
204 |
* via user interactions. |
|
205 |
* |
|
206 |
* @param <T> the data type of the display |
|
207 |
* @return a new {@link PithosSelectionEventManager} instance |
|
208 |
*/ |
|
209 |
public static <T> PithosSelectionEventManager<T> createDefaultManager() { |
|
210 |
return new PithosSelectionEventManager<T>(null); |
|
211 |
} |
|
212 |
|
|
213 |
/** |
|
214 |
* The last {@link HasData} that was handled. |
|
215 |
*/ |
|
216 |
private HasData<T> lastDisplay; |
|
217 |
|
|
218 |
/** |
|
219 |
* The last page start. |
|
220 |
*/ |
|
221 |
private int lastPageStart; |
|
222 |
|
|
223 |
/** |
|
224 |
* The last selected row index. |
|
225 |
*/ |
|
226 |
private int lastSelectedIndex = -1; |
|
227 |
|
|
228 |
/** |
|
229 |
* A boolean indicating that the last shift selection was additive. |
|
230 |
*/ |
|
231 |
private boolean shiftAdditive; |
|
232 |
|
|
233 |
/** |
|
234 |
* The last place where the user clicked without holding shift. Multi |
|
235 |
* selections that use the shift key are rooted at the anchor. |
|
236 |
*/ |
|
237 |
private int shiftAnchor = -1; |
|
238 |
|
|
239 |
/** |
|
240 |
* The {@link EventTranslator} that controls how selection is handled. |
|
241 |
*/ |
|
242 |
private final EventTranslator<T> translator; |
|
243 |
|
|
244 |
/** |
|
245 |
* Construct a new {@link PithosSelectionEventManager} using the specified |
|
246 |
* {@link EventTranslator} to control which {@link SelectAction} to take for |
|
247 |
* each event. |
|
248 |
* |
|
249 |
* @param translator the {@link EventTranslator} to use |
|
250 |
*/ |
|
251 |
protected PithosSelectionEventManager(EventTranslator<T> translator) { |
|
252 |
this.translator = translator; |
|
253 |
} |
|
254 |
|
|
255 |
/** |
|
256 |
* Update the selection model based on a user selection event. |
|
257 |
* |
|
258 |
* @param selectionModel the selection model to update |
|
259 |
* @param row the selected row index relative to the page start |
|
260 |
* @param rowValue the selected row value |
|
261 |
* @param action the {@link SelectAction} to apply |
|
262 |
* @param selectRange true to select the range from the last selected row |
|
263 |
* @param clearOthers true to clear the current selection |
|
264 |
*/ |
|
265 |
public void doMultiSelection(MultiSelectionModel<? super T> selectionModel, |
|
266 |
HasData<T> display, int row, T rowValue, SelectAction action, |
|
267 |
boolean selectRange, boolean clearOthers) { |
|
268 |
// Determine if we will add or remove selection. |
|
269 |
boolean addToSelection = true; |
|
270 |
if (action != null) { |
|
271 |
switch (action) { |
|
272 |
case IGNORE: |
|
273 |
// Ignore selection. |
|
274 |
return; |
|
275 |
case SELECT: |
|
276 |
addToSelection = true; |
|
277 |
break; |
|
278 |
case DESELECT: |
|
279 |
addToSelection = false; |
|
280 |
break; |
|
281 |
case TOGGLE: |
|
282 |
addToSelection = !selectionModel.isSelected(rowValue); |
|
283 |
break; |
|
284 |
case DEFAULT: |
|
285 |
break; |
|
286 |
} |
|
287 |
} |
|
288 |
|
|
289 |
// Determine which rows will be newly selected. |
|
290 |
int pageStart = display.getVisibleRange().getStart(); |
|
291 |
if (selectRange && pageStart == lastPageStart && lastSelectedIndex > -1 |
|
292 |
&& shiftAnchor > -1 && display == lastDisplay) { |
|
293 |
/* |
|
294 |
* Get the new shift bounds based on the existing shift anchor and the |
|
295 |
* selected row. |
|
296 |
*/ |
|
297 |
int start = Math.min(shiftAnchor, row); // Inclusive. |
|
298 |
int end = Math.max(shiftAnchor, row); // Inclusive. |
|
299 |
|
|
300 |
if (lastSelectedIndex < start) { |
|
301 |
// Revert previous selection if the user reselects a smaller range. |
|
302 |
setRangeSelection(selectionModel, display, new Range(lastSelectedIndex, |
|
303 |
start - lastSelectedIndex), !shiftAdditive, false); |
|
304 |
} else if (lastSelectedIndex > end) { |
|
305 |
// Revert previous selection if the user reselects a smaller range. |
|
306 |
setRangeSelection(selectionModel, display, new Range(end + 1, |
|
307 |
lastSelectedIndex - end), !shiftAdditive, false); |
|
308 |
} else { |
|
309 |
// Remember if we are adding or removing rows. |
|
310 |
shiftAdditive = addToSelection; |
|
311 |
} |
|
312 |
|
|
313 |
// Update the last selected row, but do not move the shift anchor. |
|
314 |
lastSelectedIndex = row; |
|
315 |
|
|
316 |
// Select the range. |
|
317 |
setRangeSelection(selectionModel, display, new Range(start, end - start |
|
318 |
+ 1), shiftAdditive, clearOthers); |
|
319 |
} else { |
|
320 |
/* |
|
321 |
* If we are not selecting a range, save the last row and set the shift |
|
322 |
* anchor. |
|
323 |
*/ |
|
324 |
lastDisplay = display; |
|
325 |
lastPageStart = pageStart; |
|
326 |
lastSelectedIndex = row; |
|
327 |
shiftAnchor = row; |
|
328 |
selectOne(selectionModel, rowValue, addToSelection, clearOthers); |
|
329 |
} |
|
330 |
} |
|
331 |
|
|
332 |
@Override |
|
333 |
public void onCellPreview(CellPreviewEvent<T> event) { |
|
334 |
// Early exit if selection is already handled or we are editing. |
|
335 |
if (event.isCellEditing() || event.isSelectionHandled()) { |
|
336 |
return; |
|
337 |
} |
|
338 |
|
|
339 |
// Early exit if we do not have a SelectionModel. |
|
340 |
HasData<T> display = event.getDisplay(); |
|
341 |
SelectionModel<? super T> selectionModel = display.getSelectionModel(); |
|
342 |
if (selectionModel == null) { |
|
343 |
return; |
|
344 |
} |
|
345 |
|
|
346 |
// Check for user defined actions. |
|
347 |
SelectAction action = (translator == null) ? SelectAction.DEFAULT |
|
348 |
: translator.translateSelectionEvent(event); |
|
349 |
|
|
350 |
// Handle the event based on the SelectionModel type. |
|
351 |
if (selectionModel instanceof MultiSelectionModel) { |
|
352 |
// Add shift key support for MultiSelectionModel. |
|
353 |
handleMultiSelectionEvent(event, action, |
|
354 |
(MultiSelectionModel<? super T>) selectionModel); |
|
355 |
} else { |
|
356 |
// Use the standard handler. |
|
357 |
handleSelectionEvent(event, action, selectionModel); |
|
358 |
} |
|
359 |
} |
|
360 |
|
|
361 |
/** |
|
362 |
* Removes all items from the selection. |
|
363 |
* |
|
364 |
* @param selectionModel the {@link MultiSelectionModel} to clear |
|
365 |
*/ |
|
366 |
protected void clearSelection(MultiSelectionModel<? super T> selectionModel) { |
|
367 |
selectionModel.clear(); |
|
368 |
} |
|
369 |
|
|
370 |
/** |
|
371 |
* Handle an event that could cause a value to be selected for a |
|
372 |
* {@link MultiSelectionModel}. This overloaded method adds support for both |
|
373 |
* the control and shift keys. If the shift key is held down, all rows between |
|
374 |
* the previous selected row and the current row are selected. |
|
375 |
* |
|
376 |
* @param event the {@link CellPreviewEvent} that triggered selection |
|
377 |
* @param action the action to handle |
|
378 |
* @param selectionModel the {@link SelectionModel} to update |
|
379 |
*/ |
|
380 |
protected void handleMultiSelectionEvent(CellPreviewEvent<T> event, |
|
381 |
SelectAction action, MultiSelectionModel<? super T> selectionModel) { |
|
382 |
NativeEvent nativeEvent = event.getNativeEvent(); |
|
383 |
String type = nativeEvent.getType(); |
|
384 |
boolean rightclick = "mousedown".equals(type) && nativeEvent.getButton()==NativeEvent.BUTTON_RIGHT; |
|
385 |
SelectAction action1 = action; |
|
386 |
if(rightclick){ |
|
387 |
boolean shift = nativeEvent.getShiftKey(); |
|
388 |
boolean ctrlOrMeta = nativeEvent.getCtrlKey() || nativeEvent.getMetaKey(); |
|
389 |
boolean clearOthers = (translator == null) ? !ctrlOrMeta |
|
390 |
: translator.clearCurrentSelection(event); |
|
391 |
if (action == null || action == SelectAction.DEFAULT) { |
|
392 |
action1 = ctrlOrMeta ? SelectAction.TOGGLE : SelectAction.SELECT; |
|
393 |
} |
|
394 |
//if the row is selected then do nothing |
|
395 |
if(selectionModel.isSelected(event.getValue())){ |
|
396 |
return; |
|
397 |
} |
|
398 |
doMultiSelection(selectionModel, event.getDisplay(), event.getIndex(), |
|
399 |
event.getValue(), action1, shift, clearOthers); |
|
400 |
} |
|
401 |
else if ("click".equals(type)) { |
|
402 |
/* |
|
403 |
* Update selection on click. Selection is toggled only if the user |
|
404 |
* presses the ctrl key. If the user does not press the control key, |
|
405 |
* selection is additive. |
|
406 |
*/ |
|
407 |
boolean shift = nativeEvent.getShiftKey(); |
|
408 |
boolean ctrlOrMeta = nativeEvent.getCtrlKey() || nativeEvent.getMetaKey(); |
|
409 |
boolean clearOthers = (translator == null) ? !ctrlOrMeta |
|
410 |
: translator.clearCurrentSelection(event); |
|
411 |
if (action == null || action == SelectAction.DEFAULT) { |
|
412 |
action1 = ctrlOrMeta ? SelectAction.TOGGLE : SelectAction.SELECT; |
|
413 |
} |
|
414 |
doMultiSelection(selectionModel, event.getDisplay(), event.getIndex(), |
|
415 |
event.getValue(), action1, shift, clearOthers); |
|
416 |
if(ctrlOrMeta){ |
|
417 |
event.setCanceled(true); |
|
418 |
} |
|
419 |
} else if ("keyup".equals(type)) { |
|
420 |
int keyCode = nativeEvent.getKeyCode(); |
|
421 |
if (keyCode == 32) { |
|
422 |
/* |
|
423 |
* Update selection when the space bar is pressed. The spacebar always |
|
424 |
* toggles selection, regardless of whether the control key is pressed. |
|
425 |
*/ |
|
426 |
boolean shift = nativeEvent.getShiftKey(); |
|
427 |
boolean clearOthers = (translator == null) ? false |
|
428 |
: translator.clearCurrentSelection(event); |
|
429 |
if (action == null || action == SelectAction.DEFAULT) { |
|
430 |
action1 = SelectAction.TOGGLE; |
|
431 |
} |
|
432 |
doMultiSelection(selectionModel, event.getDisplay(), event.getIndex(), |
|
433 |
event.getValue(), action1, shift, clearOthers); |
|
434 |
} |
|
435 |
} |
|
436 |
} |
|
437 |
|
|
438 |
/** |
|
439 |
* Handle an event that could cause a value to be selected. This method works |
|
440 |
* for any {@link SelectionModel}. Pressing the space bar or ctrl+click will |
|
441 |
* toggle the selection state. Clicking selects the row if it is not selected. |
|
442 |
* |
|
443 |
* @param event the {@link CellPreviewEvent} that triggered selection |
|
444 |
* @param action the action to handle |
|
445 |
* @param selectionModel the {@link SelectionModel} to update |
|
446 |
*/ |
|
447 |
protected void handleSelectionEvent(CellPreviewEvent<T> event, |
|
448 |
SelectAction action, SelectionModel<? super T> selectionModel) { |
|
449 |
// Handle selection overrides. |
|
450 |
T value = event.getValue(); |
|
451 |
if (action != null) { |
|
452 |
switch (action) { |
|
453 |
case IGNORE: |
|
454 |
return; |
|
455 |
case SELECT: |
|
456 |
selectionModel.setSelected(value, true); |
|
457 |
return; |
|
458 |
case DESELECT: |
|
459 |
selectionModel.setSelected(value, false); |
|
460 |
return; |
|
461 |
case TOGGLE: |
|
462 |
selectionModel.setSelected(value, !selectionModel.isSelected(value)); |
|
463 |
return; |
|
464 |
case DEFAULT: |
|
465 |
break; |
|
466 |
} |
|
467 |
} |
|
468 |
|
|
469 |
// Handle default selection. |
|
470 |
NativeEvent nativeEvent = event.getNativeEvent(); |
|
471 |
String type = nativeEvent.getType(); |
|
472 |
if ("click".equals(type)) { |
|
473 |
if (nativeEvent.getCtrlKey() || nativeEvent.getMetaKey()) { |
|
474 |
// Toggle selection on ctrl+click. |
|
475 |
selectionModel.setSelected(value, !selectionModel.isSelected(value)); |
|
476 |
} else { |
|
477 |
// Select on click. |
|
478 |
selectionModel.setSelected(value, true); |
|
479 |
} |
|
480 |
} else if ("keyup".equals(type)) { |
|
481 |
// Toggle selection on space. |
|
482 |
int keyCode = nativeEvent.getKeyCode(); |
|
483 |
if (keyCode == 32) { |
|
484 |
selectionModel.setSelected(value, !selectionModel.isSelected(value)); |
|
485 |
} |
|
486 |
} |
|
487 |
} |
|
488 |
|
|
489 |
/** |
|
490 |
* Selects the given item, optionally clearing any prior selection. |
|
491 |
* |
|
492 |
* @param selectionModel the {@link MultiSelectionModel} to update |
|
493 |
* @param target the item to select |
|
494 |
* @param selected true to select, false to deselect |
|
495 |
* @param clearOthers true to clear all other selected items |
|
496 |
*/ |
|
497 |
protected void selectOne(MultiSelectionModel<? super T> selectionModel, |
|
498 |
T target, boolean selected, boolean clearOthers) { |
|
499 |
if (clearOthers) { |
Also available in: Unified diff