Statistics
| Branch: | Tag: | Revision:

root / src / gr / grnet / pithos / web / client / PithosSelectionEventManager.java @ f5c86c34

History | View | Annotate | Download (18.9 kB)

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