1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.cb.jset.client.ui;
21
22 /***
23 * Created by IntelliJ IDEA.
24 * User: jerome
25 * Date: Jan 27, 2003
26 * Time: 12:09:22 AM
27 * To change this template use Options | File Templates.
28 */
29
30 import org.cb.cardboard.AbstractCardBoardModel;
31 import org.cb.cardboard.BoardSelectionEvent;
32 import org.cb.cardboard.BoardSelectionListener;
33 import org.cb.cardboard.BoardSelectionModel;
34 import org.cb.cardboard.Card;
35 import org.cb.cardboard.CardBoardModel;
36 import org.cb.cardboard.DefaultBoardSelectionModel;
37
38 import javax.swing.JPanel;
39 import javax.swing.ToolTipManager;
40 import java.io.Serializable;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.List;
44
45 /***
46 * The component representing the set board in the graphical client.
47 *
48 * FIXME think about using Accessible
49 *
50 * @author jerome@coffeebreaks.org - last modified by $LastChangedBy: jerome $
51 * @version $Id: JSetBoardComponent.java 130 2004-04-15 05:18:07Z jerome $
52 */
53 public class JSetBoardComponent extends JPanel
54 {
55
56
57
58
59
60
61
62
63
64 private CardBoardModel _dataModel;
65 private BoardSelectionModel _selectionModel;
66
67 private BoardSelectionListener _selectionListener;
68
69 /***
70 * Constructs a Board component using the specified dataModel.
71 * @param dataModel the model for that component
72 */
73 public JSetBoardComponent(final CardBoardModel dataModel)
74 {
75 if (dataModel == null)
76 {
77 throw new IllegalArgumentException("dataModel must be non null");
78 }
79
80
81
82 final ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
83 toolTipManager.registerComponent(this);
84
85
86
87 this._dataModel = dataModel;
88 _selectionModel = createSelectionModel();
89
90
91
92 updateUI();
93 }
94
95 /***
96 * Constructs a <code>JCardBoardComponent</code> that displays the elements in
97 * the specified array. This constructor just delegates to the
98 * <code>ListModel</code> constructor.
99 *
100 * @param listData the array of Objects to be loaded into the data model
101 */
102 public JSetBoardComponent(final Card[] listData)
103 {
104 this(new AbstractCardBoardModel()
105 {
106 public int getSize()
107 {
108 return listData.length;
109 }
110
111 public Card getElementAt(final int i)
112 {
113 return listData[i];
114 }
115
116 public List getElements()
117 {
118 return Collections.unmodifiableList(Arrays.asList(listData));
119 }
120 });
121 }
122
123 private BoardSelectionModel createSelectionModel()
124 {
125 return new DefaultBoardSelectionModel();
126 }
127
128 /***
129 * @return the model for that board.
130 */
131 public CardBoardModel getModel()
132 {
133 return _dataModel;
134 }
135
136 /***
137 * @return the selection model for that board.
138 */
139 public BoardSelectionModel getSelectionModel()
140 {
141 return _selectionModel;
142 }
143
144 /***
145 * Sets a new model for that board.
146 * A property change event with name "model" will be fired.
147 * @param model the new model
148 */
149 public void setModel(final CardBoardModel model)
150 {
151 if (model != _dataModel)
152 {
153
154 final CardBoardModel oldModel = _dataModel;
155 _dataModel = model;
156
157 clearSelection();
158 firePropertyChange("model", oldModel, _dataModel);
159 }
160 }
161
162 /***
163 * Determines whether single-item or multiple-item
164 * selections are allowed.
165 * The following <code>selectionMode</code> values are allowed:
166 * <ul>
167 * <li> <code>BoardSelectionModel.SINGLE_SELECTION</code>
168 * Only one list index can be selected at a time. In this
169 * mode the <code>setSelectionInterval</code> and
170 * <code>addSelectionInterval</code>
171 * methods are equivalent, and only the second index
172 * argument is used.
173 * <li> <code>BoardSelectionModel.SINGLE_INTERVAL_SELECTION</code>
174 * One contiguous index interval can be selected at a time.
175 * In this mode <code>setSelectionInterval</code> and
176 * <code>addSelectionInterval</code>
177 * are equivalent.
178 * <li> <code>BoardSelectionModel.MULTIPLE_INTERVAL_SELECTION</code>
179 * In this mode, there's no restriction on what can be selected.
180 * This is the default.
181 * </ul>
182 *
183 * @param selectionMode an integer specifying the type of selections
184 * that are permissible
185 * @beaninfo description: The selection mode.
186 * enum: SINGLE_SELECTION BoardSelectionModel.SINGLE_SELECTION
187 * SINGLE_INTERVAL_SELECTION BoardSelectionModel.SINGLE_INTERVAL_SELECTION
188 * MULTIPLE_INTERVAL_SELECTION BoardSelectionModel.MULTIPLE_INTERVAL_SELECTION
189 * @see #getSelectionMode
190 */
191 public void setSelectionMode(final int selectionMode)
192 {
193 getSelectionModel().setSelectionMode(selectionMode);
194 }
195
196 /***
197 * Returns whether single-item or multiple-item selections are allowed.
198 *
199 * @return the value of the <code>selectionMode</code> property
200 * @see #setSelectionMode
201 */
202 public int getSelectionMode()
203 {
204 return getSelectionModel().getSelectionMode();
205 }
206
207
208 /***
209 * Returns the first index argument from the most recent
210 * <code>addSelectionModel</code> or <code>setSelectionInterval</code> call.
211 * This is a convenience method that just delegates to the
212 * <code>selectionModel</code>.
213 *
214 * @return the index that most recently anchored an interval selection
215 * @see BoardSelectionModel#getAnchorSelectionIndex
216 * @see #addSelectionInterval
217 * @see #setSelectionInterval
218 * @see #addBoardSelectionListener
219 */
220 public int getAnchorSelectionIndex()
221 {
222 return getSelectionModel().getAnchorSelectionIndex();
223 }
224
225
226 /***
227 * Returns the second index argument from the most recent
228 * <code>addSelectionInterval</code> or <code>setSelectionInterval</code>
229 * call.
230 * This is a convenience method that just delegates to the
231 * <code>selectionModel</code>.
232 *
233 * @return the index that most recently ended a interval selection
234 * @beaninfo description: The lead selection index.
235 * @see BoardSelectionModel#getLeadSelectionIndex
236 * @see #addSelectionInterval
237 * @see #setSelectionInterval
238 * @see #addBoardSelectionListener
239 */
240 public int getLeadSelectionIndex()
241 {
242 return getSelectionModel().getLeadSelectionIndex();
243 }
244
245
246 /***
247 * Returns the smallest selected cell index.
248 * This is a convenience method that just delegates to the
249 * <code>selectionModel</code>.
250 *
251 * @return the smallest selected cell index
252 * @see BoardSelectionModel#getMinSelectionIndex
253 * @see #addBoardSelectionListener
254 */
255 public int getMinSelectionIndex()
256 {
257 return getSelectionModel().getMinSelectionIndex();
258 }
259
260
261 /***
262 * Returns the largest selected cell index.
263 * This is a convenience method that just delegates to the
264 * <code>selectionModel</code>.
265 *
266 * @return the largest selected cell index
267 * @see BoardSelectionModel#getMaxSelectionIndex
268 * @see #addBoardSelectionListener
269 */
270 public int getMaxSelectionIndex()
271 {
272 return getSelectionModel().getMaxSelectionIndex();
273 }
274
275 /***
276 * Returns true if the specified index is selected.
277 * This is a convenience method that just delegates to the
278 * <code>selectionModel</code>.
279 *
280 * @param index index to be queried for selection state
281 * @return true if the specified index is selected
282 * @see BoardSelectionModel#isSelectedIndex
283 * @see #setSelectedIndex
284 * @see #addBoardSelectionListener
285 */
286 public boolean isSelectedIndex(final int index)
287 {
288 return getSelectionModel().isSelectedIndex(index);
289 }
290
291 /***
292 * Returns true if nothing is selected.
293 * This is a convenience method that just delegates to the
294 * <code>selectionModel</code>.
295 *
296 * @return true if nothing is selected
297 * @see BoardSelectionModel#isSelectionEmpty
298 * @see #clearSelection
299 * @see #addBoardSelectionListener
300 */
301 public boolean isSelectionEmpty()
302 {
303 return getSelectionModel().isSelectionEmpty();
304 }
305
306 /***
307 * Returns an array of all of the selected indices in increasing
308 * order.
309 *
310 * @return all of the selected indices, in increasing order
311 * @see #removeSelectionInterval
312 * @see #addBoardSelectionListener
313 */
314 public int[] getSelectedIndices()
315 {
316 final BoardSelectionModel sm = getSelectionModel();
317 final int iMin = sm.getMinSelectionIndex();
318 final int iMax = sm.getMaxSelectionIndex();
319
320 if ((iMin < 0) || (iMax < 0))
321 {
322 return new int[0];
323 }
324
325 final int[] rvTmp = new int[1 + (iMax - iMin)];
326 int n = 0;
327 for (int i = iMin; i <= iMax; i++)
328 {
329 if (sm.isSelectedIndex(i))
330 {
331 rvTmp[n++] = i;
332 }
333 }
334 final int[] rv = new int[n];
335 System.arraycopy(rvTmp, 0, rv, 0, n);
336 return rv;
337 }
338
339 /***
340 * Selects a single cell.
341 *
342 * @param index the index of the one cell to select
343 * @beaninfo description: The index of the selected cell.
344 * @see BoardSelectionModel#setSelectionInterval
345 * @see #isSelectedIndex
346 * @see #addBoardSelectionListener
347 */
348 public void setSelectedIndex(final int index)
349 {
350 getSelectionModel().setSelectionInterval(index, index);
351 }
352
353
354 /***
355 * Selects a set of cells.
356 *
357 * @param indices an array of the indices of the cells to select
358 * @see BoardSelectionModel#addSelectionInterval
359 * @see #isSelectedIndex
360 * @see #addBoardSelectionListener
361 */
362 public void setSelectedIndices(final int[] indices)
363 {
364 final BoardSelectionModel sm = getSelectionModel();
365 sm.clearSelection();
366 for (int i = 0; i < indices.length; i++)
367 {
368 sm.addSelectionInterval(indices[i], indices[i]);
369 }
370 }
371
372 /***
373 * Returns an array of the values for the selected cells.
374 * The returned values are sorted in increasing index order.
375 *
376 * @return the selected values or an empty list if
377 * nothing is selected
378 * @see #isSelectedIndex
379 * @see #getModel
380 * @see #addBoardSelectionListener
381 */
382 public Object[] getSelectedValues()
383 {
384 final BoardSelectionModel sm = getSelectionModel();
385 final CardBoardModel dm = getModel();
386
387 final int iMin = sm.getMinSelectionIndex();
388 final int iMax = sm.getMaxSelectionIndex();
389
390 if ((iMin < 0) || (iMax < 0))
391 {
392 return new Object[0];
393 }
394
395 final Object[] rvTmp = new Object[1 + (iMax - iMin)];
396 int n = 0;
397 for (int i = iMin; i <= iMax; i++)
398 {
399 if (sm.isSelectedIndex(i))
400 {
401 rvTmp[n++] = dm.getElementAt(i);
402 }
403 }
404 final Object[] rv = new Object[n];
405 System.arraycopy(rvTmp, 0, rv, 0, n);
406 return rv;
407 }
408
409 /***
410 * Adds a listener to the cardBoard that's notified each time a change
411 * to the selection occurs. Listeners added directly to the
412 * <code>JCardBoardComponent</code>
413 * will have their <code>ListSelectionEvent.getSource() ==
414 * this JCardBoardComponent</code>
415 * (instead of the <code>BoardSelectionModel</code>).
416 *
417 * @param listener the <code>ListSelectionListener</code> to add
418 * @see #getSelectionModel
419 * @see #getBoardSelectionListeners()
420 */
421 public void addBoardSelectionListener(final BoardSelectionListener listener)
422 {
423 if (_selectionListener == null)
424 {
425 _selectionListener = new BoardSelectionHandler();
426 getSelectionModel().addBoardSelectionListener(_selectionListener);
427 }
428
429 listenerList.add(BoardSelectionListener.class, listener);
430 }
431
432
433 /***
434 * Removes a listener from the list that's notified each time a
435 * change to the selection occurs.
436 *
437 * @param listener the <code>BoardSelectionListener</code> to remove
438 * @see #addBoardSelectionListener
439 * @see #getSelectionModel
440 */
441 public void removeBoardSelectionListener(final BoardSelectionListener listener)
442 {
443 listenerList.remove(BoardSelectionListener.class, listener);
444 }
445
446 /***
447 * Returns an array of all the <code>BoardSelectionListener</code>s added
448 * to this JCardBoardComponent with addBoardSelectionListener().
449 *
450 * @return all of the <code>BoardSelectionListener</code>s added or an empty
451 * array if no listeners have been added
452 * @see #addBoardSelectionListener
453 * @since 1.4
454 */
455 public BoardSelectionListener[] getBoardSelectionListeners()
456 {
457 return (BoardSelectionListener[]) listenerList.getListeners(BoardSelectionListener.class);
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493 private class BoardSelectionHandler implements BoardSelectionListener, Serializable
494 {
495 public void valueChanged(final BoardSelectionEvent e)
496 {
497 fireSelectionValueChanged(e.getFirstIndex(), e.getLastIndex(), e.getValueIsAdjusting());
498 }
499 }
500
501
502 /***
503 * Notifies <code>JCardBoardComponent</code> <code>BoardSelectionListener</code>s that
504 * the selection model has changed. It's used to forward
505 * <code>BoardSelectionEvents</code> from the <code>selectionModel</code>
506 * to the <code>BoardSelectionListener</code>s added directly to the
507 * <code>JCardBoardComponent</code>.
508 *
509 * @param firstIndex the first selected index
510 * @param lastIndex the last selected index
511 * @param isAdjusting true if multiple changes are being made
512 * @see #addBoardSelectionListener(org.cb.cardboard.BoardSelectionListener)
513 * @see #removeBoardSelectionListener(BoardSelectionListener)
514 * @see javax.swing.event.EventListenerList
515 */
516 protected void fireSelectionValueChanged(final int firstIndex, final int lastIndex,
517 final boolean isAdjusting)
518 {
519 final Object[] listeners = listenerList.getListenerList();
520 BoardSelectionEvent e = null;
521
522 for (int i = listeners.length - 2; i >= 0; i -= 2)
523 {
524 if (listeners[i] == BoardSelectionListener.class)
525 {
526 if (e == null)
527 {
528 e = new BoardSelectionEvent(this, firstIndex, isAdjusting);
529 }
530 ((BoardSelectionListener) listeners[i + 1]).valueChanged(e);
531 }
532 }
533 }
534
535
536 /***
537 * Clears the selection - after calling this method
538 * <code>isSelectionEmpty</code> will return true.
539 * This is a convenience method that just delegates to the
540 * <code>selectionModel</code>.
541 *
542 * @see BoardSelectionModel#clearSelection
543 * @see #isSelectionEmpty
544 * @see #addBoardSelectionListener
545 */
546 public void clearSelection()
547 {
548 getSelectionModel().clearSelection();
549 }
550
551
552 /***
553 * Selects the specified interval. Both the <code>anchor</code>
554 * and <code>lead</code> indices are included. It's not
555 * necessary for <code>anchor</code> to be less than <code>lead</code>.
556 * This is a convenience method that just delegates to the
557 * <code>selectionModel</code>.
558 * The <code>DefaultBoardSelectionModel</code> implementation
559 * will do nothing if either <code>anchor</code> or
560 * <code>lead</code> are -1.
561 * If <code>anchor</code> or <code>lead</code> are less than -1,
562 * <code>IndexOutOfBoundsException</code> is thrown.
563 *
564 * @param anchor the first index to select
565 * @param lead the last index to select
566 * @throws IndexOutOfBoundsException if either <code>anchor</code>
567 * or <code>lead</code> are less than -1
568 * @see BoardSelectionModel#setSelectionInterval
569 * @see #addSelectionInterval
570 * @see #removeSelectionInterval
571 * @see #addBoardSelectionListener
572 */
573 public void setSelectionInterval(final int anchor, final int lead)
574 {
575 getSelectionModel().setSelectionInterval(anchor, lead);
576 }
577
578 /***
579 * Sets the selection to be the union of the specified interval with current
580 * selection. Both the anchor and lead indices are
581 * included. It's not necessary for anchor to be less than lead.
582 * This is a convenience method that just delegates to the
583 * <code>selectionModel</code>. The
584 * <code>DefaultBoardSelectionModel</code> implementation
585 * will do nothing if either <code>anchor</code> or
586 * <code>lead</code> are -1.
587 * If <code>anchor</code> or <code>lead</code> are less than -1,
588 * <code>IndexOutOfBoundsException</code> is thrown.
589 *
590 * @param anchor the first index to add to the selection
591 * @param lead the last index to add to the selection
592 * @throws IndexOutOfBoundsException if either <code>anchor</code>
593 * or <code>lead</code> are less than -1
594 * @see BoardSelectionModel#addSelectionInterval
595 * @see #setSelectionInterval
596 * @see #removeSelectionInterval
597 * @see #addBoardSelectionListener
598 */
599 public void addSelectionInterval(final int anchor, final int lead)
600 {
601 getSelectionModel().addSelectionInterval(anchor, lead);
602 }
603
604
605 /***
606 * Sets the selection to be the set difference of the specified interval
607 * and the current selection. Both the <code>index0</code> and
608 * <code>index1</code> indices are removed. It's not necessary for
609 * <code>index0</code> to be less than <code>index1</code>.
610 * This is a convenience method that just delegates to the
611 * <code>selectionModel</code>.
612 * The <code>DefaultBoardSelectionModel</code> implementation
613 * will do nothing if either <code>index0</code> or
614 * <code>index1</code> are -1.
615 * If <code>index0</code> or <code>index1</code> are less than -1,
616 * <code>IndexOutOfBoundsException</code> is thrown.
617 *
618 * @param index0 the first index to remove from the selection
619 * @param index1 the last index to remove from the selection
620 * @throws IndexOutOfBoundsException if either <code>index0</code>
621 * or <code>index1</code> are less than -1
622 * @see BoardSelectionModel#removeSelectionInterval
623 * @see #setSelectionInterval
624 * @see #addSelectionInterval
625 * @see #addBoardSelectionListener
626 */
627 public void removeSelectionInterval(final int index0, final int index1)
628 {
629 getSelectionModel().removeSelectionInterval(index0, index1);
630 }
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649 }